From a2f170b35a67bda4841db0fb5326ce675f4cd5d5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 24 Aug 2015 11:23:24 -0700 Subject: Tweak Reply by email docs. --- doc/reply_by_email/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 91eea956e52..67dc2b04000 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -4,9 +4,13 @@ GitLab can be set up to allow users to comment on issues and merge requests by r In order to do this, you need access to an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the [Postfix](http://www.postfix.org/) mail server which you can run on-premises. +If you want to use a Gmail with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255). + +To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these instructions](https://help.ubuntu.com/community/PostfixBasicSetupHowto). + ## Set it up -In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. If you're actually using Gmail with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255). +In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ### Installations from source -- cgit v1.2.1 From 7d5c6a1ada3da0d3d2c641736cd3c140c598d7eb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 24 Aug 2015 14:46:45 -0700 Subject: Expand documentation --- doc/reply_by_email/README.md | 8 +- doc/reply_by_email/postfix.md | 316 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 doc/reply_by_email/postfix.md diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 67dc2b04000..accd83b66df 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -2,11 +2,13 @@ GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails. -In order to do this, you need access to an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the [Postfix](http://www.postfix.org/) mail server which you can run on-premises. +## Get a mailbox -If you want to use a Gmail with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255). +Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises. -To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these instructions](https://help.ubuntu.com/community/PostfixBasicSetupHowto). +If you want to use Gmail with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255). + +To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these instructions](./postfix.md). ## Set it up diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md new file mode 100644 index 00000000000..bab107b958d --- /dev/null +++ b/doc/reply_by_email/postfix.md @@ -0,0 +1,316 @@ +# Set up Postfix for Reply by email + +This document will take you through the steps of setting up a basic Postfix mail server with IMAP authentication on Ubuntu, to be used with Reply by email. + +The instructions make the assumption that you will be using the email address `replies@gitlab.example.com`, that is, username `replies` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets. + +## Configure your server DNS + +1. Add an MX record pointing from `gitlab.example.com` to your server IP. +1. Add an A record pointing from `mail.gitlab.example.com` to your server IP. +1. Verify that the changes have propagated: + + ```sh + dig mx gitlab.example.com + ``` + + ```sh + dig a mail.gitlab.example.com + ``` + + You should see an `ANSWER SECTION` with the expected result in the output for both. + +## Install packages + +1. Install the `postfix` package if it is not installed already: + + ```sh + sudo apt-get install postfix + ``` + + When asked about the environment, select 'Internet Site'. When asked to confirm the hostname, make sure it matches `gitlab.example.com`. + +1. Install the `mailutils` package. + + ```sh + sudo apt-get install mailutils + ``` + +## Create user + +1. Create a user for replies. + + ```sh + sudo useradd -m -s /bin/bash replies + ``` + +1. Set a password for this user. + + ```sh + sudo passwd replies + ``` + + Be sure not to forget this, you'll need it later. + +## Test the out-of-the-box setup + +1. Connect to the local SMTP server: + + ```sh + telnet localhost 25 + ``` + + You should see a prompt like this: + + ```sh + Trying 127.0.0.1... + Connected to localhost. + Escape character is '^]'. + 220 gitlab.example.com ESMTP Postfix (Ubuntu) + ``` + + If you get a `Connection refused` error instead, check if `postfix` is running: + + ```sh + sudo postfix status + ``` + + If it is not, start it: + + ```sh + sudo postfix start + ``` + +1. Send the new `replies` user a dummy email to test SMTP, by entering the following into the SMTP prompt: + + ``` + ehlo localhost + mail from: root@localhost + rcpt to: replies@localhost + data + Subject: Re: Some issue + + Sounds good! + . + quit + ``` + + (Note: The `.` is a literal period on its own line) + +1. Check if the `replies` user received the email: + + ```sh + sh - replies + mail + ``` + + You should see output like this: + + ``` + "/var/mail/replies": 1 message 1 unread + >U 1 root@localhost 59/2842 Re: Some issue + ``` + + Quit the mail app: + + ```sh + q + ``` + +1. Log out of the `replies` account and go back to being `root`: + + ```sh + logout + ``` + +## Configure Postfix to use Maildir-style mailboxes + +Courier, which we will install later to add IMAP authentication, requiers mailboxes to have the Maildir format, rather than mbox. + +1. Configure Postfix to use Maildir-style mailboxes: + + ```sh + sudo postconf -e "home_mailbox = Maildir/" + sudo postconf -e "mailbox_command = " + ``` + +1. Restart Postfix: + + ```sh + sudo postfix restart + ``` + +1. Test the new setup: + + 1. Follow steps 1 and 2 of "Test the out-of-the-box setup". + 2. Check if the `replies` user received the email: + + ```sh + sh - replies + MAIL=/home/replies/Maildir + mail + ``` + + You should see output like this: + + ``` + "/home/replies/Maildir": 1 message 1 unread + >U 1 root@localhost 59/2842 Re: Some issue + ``` + + Quit the mail app: + + ```sh + q + ``` + +1. Log out of the `replies` account and go back to being `root`: + + ```sh + logout + ``` + +## Install the Courier IMAP server + +1. Install the `courier-imap` package: + + ```sh + sudo apt-get install courier-imap + ``` + +## Configure Postfix to receive email from the internet + +1. Let Postfix know about the domains that it should consider local: + + ```sh + sudo postconf -e "mydestination = mail.gitlab.example.com, localhost.localdomain, localhost, gitlab.example.com" + ``` + +1. Let Postfix know about the IPs that it should consider part of the LAN: + + We'll assume `192.168.1.0/24` is your local LAN. You can safely skip this step if you don't have other machines in the same local network. + + ```sh + sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.1.0/24" + ``` + +1. Configure Postfix to receive mail on all interfaces, which includes the internet: + + ```sh + sudo postconf -e "init_interfaces = all" + ``` + +1. Configure Postfix to receive mail on both IPv4 and IPv6 protocols: + + ```sh + sudo postconf -e "inet_protocols = all" + ``` + +## Test the final setup + +1. Test SMTP under the new setup: + + 1. Connect to the SMTP server: + + ```sh + telnet mail.gitlab.example.com 25 + ``` + + You should see a prompt like this: + + ```sh + Trying 123.123.123.123... + Connected to mail.gitlab.example.com. + Escape character is '^]'. + 220 gitlab.example.com ESMTP Postfix (Ubuntu) + ``` + + If you get a `Connection refused` error instead, make sure your firewall is setup to allow inbound traffic on port 25. + + 1. Send the `replies` user a dummy email to test SMTP, by entering the following into the SMTP prompt: + + ``` + ehlo gitlab.example.com + mail from: root@gitlab.example.com + rcpt to: replies@gitlab.example.com + data + Subject: Re: Some issue + + Sounds good! + . + quit + ``` + + (Note: The `.` is a literal period on its own line) + + 1. Check if the `replies` user received the email: + + ```sh + sh - replies + MAIL=/home/replies/Maildir + mail + ``` + + You should see output like this: + + ``` + "/home/replies/Maildir": 1 message 1 unread + >U 1 root@gitlab.example.com 59/2842 Re: Some issue + ``` + + Quit the mail app: + + ```sh + q + ``` + + 1. Log out of the `replies` account and go back to being `root`: + + ```sh + logout + ``` + +1. Test IMAP under the new setup: + + 1. Connect to the IMAP server: + + ```sh + telnet mail.gitlab.example.com 143 + ``` + + You should see a prompt like this: + + ```sh + Trying 123.123.123.123... + Connected to mail.example.gitlab.com. + Escape character is '^]'. + - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information. + ``` + + 1. Sign in as the `replies` user to test IMAP, by entering the following into the IMAP prompt: + + ``` + a login replies PASSWORD + ``` + + Replace PASSWORD by the password you set on the `replies` user earlier. + + You should see output like this: + + ``` + a OK LOGIN Ok. + ``` + + 1. Disconnect from the IMAP server: + + ```sh + a logout + ``` + +## Done! + +If all the tests went all right, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) doc to configure GitLab. + +--------- + +_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._ -- cgit v1.2.1 From f00fdc71e9ea4e227b405dbfa671bf9c6f94736d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 25 Aug 2015 10:23:55 -0700 Subject: Don't overwrite /etc/default/gitlab. --- doc/reply_by_email/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index accd83b66df..705cb08dd1a 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -36,7 +36,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. -2. Find `config/mail_room.yml.example` and copy it to `config/mail_room.yml`: +2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: ```sh sudo cp config/mail_room.yml.example config/mail_room.yml @@ -77,10 +77,10 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ``` -4. Find `lib/support/init.d/gitlab.default.example` and copy it to `/etc/default/gitlab`: +4. Copy `lib/support/init.d/gitlab.default.example` to `/etc/default/gitlab`, if that does not already exist: ```sh - sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab + [ -f /etc/default/gitlab ] || sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab ``` 5. Edit `/etc/default/gitlab` to enable `mail_room`: @@ -89,6 +89,8 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. sudo editor /etc/default/gitlab ``` + Either change `mail_room_enabled=false` to the below, or add it at the bottom of the file: + ```sh mail_room_enabled=true ``` @@ -125,7 +127,7 @@ TODO As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. -2. Find `config/mail_room.yml.example` and copy it to `config/mail_room.yml`: +2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: ```sh sudo cp config/mail_room.yml.example config/mail_room.yml -- cgit v1.2.1 From 5747fa00162a6bc5762bf0a1641ef743a772bdb8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 25 Aug 2015 12:03:31 -0700 Subject: Fix docs --- doc/reply_by_email/postfix.md | 22 +++++++++++++++++----- lib/tasks/gitlab/check.rake | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md index bab107b958d..1d80037c1da 100644 --- a/doc/reply_by_email/postfix.md +++ b/doc/reply_by_email/postfix.md @@ -100,7 +100,7 @@ The instructions make the assumption that you will be using the email address `r 1. Check if the `replies` user received the email: ```sh - sh - replies + su - replies mail ``` @@ -137,7 +137,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Restart Postfix: ```sh - sudo postfix restart + sudo /etc/init.d/postfix restart ``` 1. Test the new setup: @@ -146,7 +146,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 2. Check if the `replies` user received the email: ```sh - sh - replies + su - replies MAIL=/home/replies/Maildir mail ``` @@ -197,7 +197,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Configure Postfix to receive mail on all interfaces, which includes the internet: ```sh - sudo postconf -e "init_interfaces = all" + sudo postconf -e "inet_interfaces = all" ``` 1. Configure Postfix to receive mail on both IPv4 and IPv6 protocols: @@ -206,6 +206,18 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo sudo postconf -e "inet_protocols = all" ``` +1. Configure Postfix to use the `+` delimiter for sub-addressing: + + ```sh + sudo postconf -e "recipient_delimiter = +" + ``` + +1. Restart Postfix: + + ```sh + sudo /etc/init.d/postfix restart + ``` + ## Test the final setup 1. Test SMTP under the new setup: @@ -246,7 +258,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Check if the `replies` user received the email: ```sh - sh - replies + su - replies MAIL=/home/replies/Maildir mail ``` diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 2b9688c1b40..13f1cf58fca 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -595,7 +595,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("RAILS_ENV=production bin/background_jobs start") + sudo_gitlab("RAILS_ENV=production sudo -u git -H bin/background_jobs start") ) for_more_information( see_installation_guide_section("Install Init Script"), @@ -726,7 +726,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("RAILS_ENV=production bin/mail_room start") + sudo_gitlab("RAILS_ENV=production sudo -u git -H bin/mail_room start") ) for_more_information( see_installation_guide_section("Install Init Script"), -- cgit v1.2.1 From 5c226d335fa18a9af48fef051c2d309467324f46 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 25 Aug 2015 12:21:54 -0700 Subject: No DNS setup necessary, but firewall setup may be. --- doc/reply_by_email/postfix.md | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md index 1d80037c1da..e2a974fbe17 100644 --- a/doc/reply_by_email/postfix.md +++ b/doc/reply_by_email/postfix.md @@ -4,21 +4,10 @@ This document will take you through the steps of setting up a basic Postfix mail The instructions make the assumption that you will be using the email address `replies@gitlab.example.com`, that is, username `replies` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets. -## Configure your server DNS +## Configure your server firewall -1. Add an MX record pointing from `gitlab.example.com` to your server IP. -1. Add an A record pointing from `mail.gitlab.example.com` to your server IP. -1. Verify that the changes have propagated: - - ```sh - dig mx gitlab.example.com - ``` - - ```sh - dig a mail.gitlab.example.com - ``` - - You should see an `ANSWER SECTION` with the expected result in the output for both. +1. Open up port 25 on your server so that people can send email into the server over SMTP. +2. If the mail server is different from the server running GitLab, open up port 143 on your server so that GitLab can read email from the server over IMAP. ## Install packages @@ -183,7 +172,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Let Postfix know about the domains that it should consider local: ```sh - sudo postconf -e "mydestination = mail.gitlab.example.com, localhost.localdomain, localhost, gitlab.example.com" + sudo postconf -e "mydestination = gitlab.example.com, localhost.localdomain, localhost" ``` 1. Let Postfix know about the IPs that it should consider part of the LAN: @@ -200,12 +189,6 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo sudo postconf -e "inet_interfaces = all" ``` -1. Configure Postfix to receive mail on both IPv4 and IPv6 protocols: - - ```sh - sudo postconf -e "inet_protocols = all" - ``` - 1. Configure Postfix to use the `+` delimiter for sub-addressing: ```sh @@ -215,7 +198,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Restart Postfix: ```sh - sudo /etc/init.d/postfix restart + sudo service postfix restart ``` ## Test the final setup @@ -225,14 +208,14 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Connect to the SMTP server: ```sh - telnet mail.gitlab.example.com 25 + telnet gitlab.example.com 25 ``` You should see a prompt like this: ```sh Trying 123.123.123.123... - Connected to mail.gitlab.example.com. + Connected to gitlab.example.com. Escape character is '^]'. 220 gitlab.example.com ESMTP Postfix (Ubuntu) ``` @@ -287,7 +270,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Connect to the IMAP server: ```sh - telnet mail.gitlab.example.com 143 + telnet gitlab.example.com 143 ``` You should see a prompt like this: -- cgit v1.2.1 From 9ae12c398143cb15799eeca301fc761f7e096b2c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 25 Aug 2015 12:29:11 -0700 Subject: No mailbox_command needed --- doc/reply_by_email/postfix.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md index e2a974fbe17..0b16e5e8e80 100644 --- a/doc/reply_by_email/postfix.md +++ b/doc/reply_by_email/postfix.md @@ -120,7 +120,6 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo ```sh sudo postconf -e "home_mailbox = Maildir/" - sudo postconf -e "mailbox_command = " ``` 1. Restart Postfix: -- cgit v1.2.1 From 046b28312704f3131e72dcd2dbdacc5264d4aa62 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 25 Aug 2015 18:42:46 -0700 Subject: Groundwork for merging CI into CE --- CHANGELOG-CI | 298 +++++++ Gemfile | 194 +++-- Gemfile.lock | 544 +++++++------ Procfile | 2 +- app/assets/images/ci/arch.jpg | Bin 0 -> 25222 bytes app/assets/images/ci/favicon.ico | Bin 0 -> 5430 bytes app/assets/images/ci/loader.gif | Bin 0 -> 4405 bytes app/assets/images/ci/no_avatar.png | Bin 0 -> 1337 bytes app/assets/images/ci/rails.png | Bin 0 -> 6646 bytes app/assets/images/ci/service_sample.png | Bin 0 -> 76024 bytes app/assets/javascripts/ci/Chart.min.js | 39 + app/assets/javascripts/ci/application.js.coffee | 50 ++ app/assets/javascripts/ci/build.coffee | 41 + app/assets/javascripts/ci/pager.js.coffee | 42 + app/assets/javascripts/ci/projects.js.coffee | 6 + app/assets/stylesheets/ci/application.scss | 46 ++ app/assets/stylesheets/ci/generic/avatar.scss | 29 + app/assets/stylesheets/ci/generic/buttons.scss | 7 + app/assets/stylesheets/ci/generic/callout.scss | 45 + app/assets/stylesheets/ci/generic/common.scss | 189 +++++ app/assets/stylesheets/ci/generic/forms.scss | 28 + app/assets/stylesheets/ci/generic/tables.scss | 20 + app/assets/stylesheets/ci/generic/typography.scss | 63 ++ app/assets/stylesheets/ci/generic/xterm.scss | 904 +++++++++++++++++++++ app/assets/stylesheets/ci/main/fonts.scss | 2 + app/assets/stylesheets/ci/main/layout.scss | 18 + app/assets/stylesheets/ci/main/mixins.scss | 31 + app/assets/stylesheets/ci/main/variables.scss | 44 + app/assets/stylesheets/ci/sections/builds.scss | 54 ++ app/assets/stylesheets/ci/sections/lint.scss | 8 + app/assets/stylesheets/ci/sections/login.scss | 13 + app/assets/stylesheets/ci/sections/navbar.scss | 54 ++ app/assets/stylesheets/ci/sections/projects.scss | 61 ++ app/assets/stylesheets/ci/sections/runners.scss | 34 + app/assets/stylesheets/ci/sections/setup.scss | 11 + app/controllers/application_controller.rb | 11 +- app/controllers/ci/admin/application_controller.rb | 10 + .../ci/admin/application_settings_controller.rb | 31 + app/controllers/ci/admin/builds_controller.rb | 12 + app/controllers/ci/admin/events_controller.rb | 9 + app/controllers/ci/admin/projects_controller.rb | 19 + .../ci/admin/runner_projects_controller.rb | 34 + app/controllers/ci/admin/runners_controller.rb | 69 ++ app/controllers/ci/application_controller.rb | 133 +++ app/controllers/ci/builds_controller.rb | 77 ++ app/controllers/ci/charts_controller.rb | 24 + app/controllers/ci/commits_controller.rb | 37 + app/controllers/ci/events_controller.rb | 21 + app/controllers/ci/helps_controller.rb | 16 + app/controllers/ci/lints_controller.rb | 26 + app/controllers/ci/projects_controller.rb | 136 ++++ app/controllers/ci/runner_projects_controller.rb | 34 + app/controllers/ci/runners_controller.rb | 71 ++ app/controllers/ci/services_controller.rb | 59 ++ app/controllers/ci/triggers_controller.rb | 43 + app/controllers/ci/user_sessions_controller.rb | 65 ++ app/controllers/ci/variables_controller.rb | 33 + app/controllers/ci/web_hooks_controller.rb | 53 ++ app/controllers/oauth/applications_controller.rb | 2 +- .../oauth/authorized_applications_controller.rb | 2 +- app/controllers/projects/network_controller.rb | 2 +- app/controllers/projects/refs_controller.rb | 2 +- app/controllers/projects/wikis_controller.rb | 2 +- app/controllers/search_controller.rb | 2 +- app/helpers/appearances_helper.rb | 21 - app/helpers/application_helper.rb | 315 ------- app/helpers/application_settings_helper.rb | 59 -- app/helpers/auth_helper.rb | 50 -- app/helpers/blob_helper.rb | 74 -- app/helpers/branches_helper.rb | 17 - app/helpers/broadcast_messages_helper.rb | 16 - app/helpers/ci/application_helper.rb | 140 ++++ app/helpers/ci/builds_helper.rb | 41 + app/helpers/ci/commits_helper.rb | 26 + app/helpers/ci/gitlab_helper.rb | 36 + app/helpers/ci/icons_helper.rb | 11 + app/helpers/ci/projects_helper.rb | 36 + app/helpers/ci/routes_helper.rb | 29 + app/helpers/ci/runners_helper.rb | 22 + app/helpers/ci/triggers_helper.rb | 7 + app/helpers/ci/user_helper.rb | 15 + app/helpers/ci/user_sessions_helper.rb | 32 + app/helpers/commits_helper.rb | 183 ----- app/helpers/compare_helper.rb | 21 - app/helpers/dashboard_helper.rb | 9 - app/helpers/diff_helper.rb | 170 ---- app/helpers/emails_helper.rb | 57 -- app/helpers/events_helper.rb | 203 ----- app/helpers/explore_helper.rb | 17 - app/helpers/external_wiki_helper.rb | 11 - app/helpers/git_helper.rb | 5 - app/helpers/gitlab/appearances_helper.rb | 23 + app/helpers/gitlab/application_helper.rb | 317 ++++++++ app/helpers/gitlab/application_settings_helper.rb | 61 ++ app/helpers/gitlab/auth_helper.rb | 52 ++ app/helpers/gitlab/blob_helper.rb | 76 ++ app/helpers/gitlab/branches_helper.rb | 19 + app/helpers/gitlab/broadcast_messages_helper.rb | 18 + app/helpers/gitlab/commits_helper.rb | 185 +++++ app/helpers/gitlab/compare_helper.rb | 23 + app/helpers/gitlab/dashboard_helper.rb | 11 + app/helpers/gitlab/diff_helper.rb | 172 ++++ app/helpers/gitlab/emails_helper.rb | 59 ++ app/helpers/gitlab/events_helper.rb | 205 +++++ app/helpers/gitlab/explore_helper.rb | 19 + app/helpers/gitlab/external_wiki_helper.rb | 13 + app/helpers/gitlab/git_helper.rb | 7 + app/helpers/gitlab/gitlab_markdown_helper.rb | 195 +++++ app/helpers/gitlab/gitlab_routing_helper.rb | 69 ++ app/helpers/gitlab/graph_helper.rb | 18 + app/helpers/gitlab/groups_helper.rb | 35 + app/helpers/gitlab/icons_helper.rb | 87 ++ app/helpers/gitlab/issues_helper.rb | 90 ++ app/helpers/gitlab/labels_helper.rb | 103 +++ app/helpers/gitlab/merge_requests_helper.rb | 76 ++ app/helpers/gitlab/milestones_helper.rb | 38 + app/helpers/gitlab/namespaces_helper.rb | 38 + app/helpers/gitlab/nav_helper.rb | 23 + app/helpers/gitlab/notes_helper.rb | 78 ++ app/helpers/gitlab/notifications_helper.rb | 17 + app/helpers/gitlab/page_layout_helper.rb | 28 + app/helpers/gitlab/preferences_helper.rb | 67 ++ app/helpers/gitlab/projects_helper.rb | 332 ++++++++ app/helpers/gitlab/search_helper.rb | 114 +++ app/helpers/gitlab/selects_helper.rb | 47 ++ app/helpers/gitlab/snippets_helper.rb | 22 + app/helpers/gitlab/sorting_helper.rb | 98 +++ app/helpers/gitlab/submodule_helper.rb | 76 ++ app/helpers/gitlab/tab_helper.rb | 133 +++ app/helpers/gitlab/tags_helper.rb | 16 + app/helpers/gitlab/tree_helper.rb | 89 ++ app/helpers/gitlab/version_check_helper.rb | 9 + app/helpers/gitlab/visibility_level_helper.rb | 97 +++ app/helpers/gitlab/wiki_helper.rb | 26 + app/helpers/gitlab_markdown_helper.rb | 193 ----- app/helpers/gitlab_routing_helper.rb | 67 -- app/helpers/graph_helper.rb | 16 - app/helpers/groups_helper.rb | 33 - app/helpers/icons_helper.rb | 85 -- app/helpers/issues_helper.rb | 88 -- app/helpers/labels_helper.rb | 101 --- app/helpers/merge_requests_helper.rb | 74 -- app/helpers/milestones_helper.rb | 36 - app/helpers/namespaces_helper.rb | 36 - app/helpers/nav_helper.rb | 21 - app/helpers/notes_helper.rb | 76 -- app/helpers/notifications_helper.rb | 15 - app/helpers/page_layout_helper.rb | 26 - app/helpers/preferences_helper.rb | 65 -- app/helpers/projects_helper.rb | 330 -------- app/helpers/search_helper.rb | 112 --- app/helpers/selects_helper.rb | 45 - app/helpers/snippets_helper.rb | 20 - app/helpers/sorting_helper.rb | 96 --- app/helpers/submodule_helper.rb | 74 -- app/helpers/tab_helper.rb | 131 --- app/helpers/tags_helper.rb | 14 - app/helpers/tree_helper.rb | 88 -- app/helpers/version_check_helper.rb | 7 - app/helpers/visibility_level_helper.rb | 95 --- app/helpers/wiki_helper.rb | 24 - app/mailers/base_mailer.rb | 4 +- app/mailers/ci/emails/builds.rb | 17 + app/mailers/ci/notify.rb | 47 ++ app/mailers/notify.rb | 4 +- app/models/ci/application_setting.rb | 27 + app/models/ci/build.rb | 285 +++++++ app/models/ci/commit.rb | 267 ++++++ app/models/ci/event.rb | 27 + app/models/ci/network.rb | 122 +++ app/models/ci/project.rb | 221 +++++ app/models/ci/project_status.rb | 47 ++ app/models/ci/runner.rb | 80 ++ app/models/ci/runner_project.rb | 21 + app/models/ci/service.rb | 105 +++ app/models/ci/trigger.rb | 39 + app/models/ci/trigger_request.rb | 23 + app/models/ci/user.rb | 97 +++ app/models/ci/user_session.rb | 23 + app/models/ci/variable.rb | 25 + app/models/ci/web_hook.rb | 44 + app/models/project.rb | 4 +- app/models/project_services/ci/hip_chat_message.rb | 78 ++ app/models/project_services/ci/hip_chat_service.rb | 93 +++ app/models/project_services/ci/mail_service.rb | 84 ++ app/models/project_services/ci/slack_message.rb | 97 +++ app/models/project_services/ci/slack_service.rb | 81 ++ .../gitlab_issue_tracker_service.rb | 2 +- app/models/project_services/jira_service.rb | 2 +- app/services/ci/create_commit_service.rb | 50 ++ app/services/ci/create_project_service.rb | 35 + app/services/ci/create_trigger_request_service.rb | 17 + app/services/ci/event_service.rb | 31 + app/services/ci/image_for_build_service.rb | 31 + app/services/ci/register_build_service.rb | 40 + app/services/ci/test_hook_service.rb | 7 + app/services/ci/web_hook_service.rb | 36 + .../ci/admin/application_settings/_form.html.haml | 24 + .../ci/admin/application_settings/show.html.haml | 3 + app/views/ci/admin/builds/_build.html.haml | 32 + app/views/ci/admin/builds/index.html.haml | 27 + app/views/ci/admin/events/index.html.haml | 17 + app/views/ci/admin/projects/_project.html.haml | 28 + app/views/ci/admin/projects/index.html.haml | 14 + app/views/ci/admin/runner_projects/index.html.haml | 57 ++ app/views/ci/admin/runners/_runner.html.haml | 48 ++ app/views/ci/admin/runners/index.html.haml | 51 ++ app/views/ci/admin/runners/show.html.haml | 118 +++ app/views/ci/admin/runners/update.js.haml | 2 + app/views/ci/builds/_build.html.haml | 45 + app/views/ci/builds/show.html.haml | 176 ++++ app/views/ci/charts/_build_times.haml | 21 + app/views/ci/charts/_builds.haml | 41 + app/views/ci/charts/_overall.haml | 21 + app/views/ci/charts/show.html.haml | 4 + app/views/ci/commits/_commit.html.haml | 32 + app/views/ci/commits/show.html.haml | 96 +++ app/views/ci/errors/show.haml | 2 + app/views/ci/events/index.html.haml | 19 + app/views/ci/helps/oauth2.html.haml | 20 + app/views/ci/helps/show.html.haml | 40 + app/views/ci/kaminari/_first_page.html.haml | 2 + app/views/ci/kaminari/_gap.html.haml | 2 + app/views/ci/kaminari/_last_page.html.haml | 2 + app/views/ci/kaminari/_next_page.html.haml | 2 + app/views/ci/kaminari/_page.html.haml | 2 + app/views/ci/kaminari/_paginator.html.haml | 11 + app/views/ci/kaminari/_prev_page.html.haml | 2 + app/views/ci/lints/_create.html.haml | 39 + app/views/ci/lints/create.js.haml | 2 + app/views/ci/lints/show.html.haml | 25 + app/views/ci/notify/build_fail_email.html.haml | 19 + app/views/ci/notify/build_fail_email.text.erb | 9 + app/views/ci/notify/build_success_email.html.haml | 20 + app/views/ci/notify/build_success_email.text.erb | 9 + app/views/ci/projects/_form.html.haml | 101 +++ app/views/ci/projects/_gl_projects.html.haml | 15 + app/views/ci/projects/_info.html.haml | 2 + app/views/ci/projects/_no_runners.html.haml | 8 + app/views/ci/projects/_project.html.haml | 22 + app/views/ci/projects/_public.html.haml | 21 + app/views/ci/projects/_search.html.haml | 18 + app/views/ci/projects/edit.html.haml | 21 + app/views/ci/projects/gitlab.html.haml | 35 + app/views/ci/projects/index.html.haml | 22 + app/views/ci/projects/show.html.haml | 59 ++ app/views/ci/runners/_runner.html.haml | 35 + app/views/ci/runners/_shared_runners.html.haml | 23 + app/views/ci/runners/_specific_runners.html.haml | 29 + app/views/ci/runners/edit.html.haml | 27 + app/views/ci/runners/index.html.haml | 25 + app/views/ci/runners/show.html.haml | 64 ++ app/views/ci/services/_form.html.haml | 57 ++ app/views/ci/services/edit.html.haml | 1 + app/views/ci/services/index.html.haml | 22 + app/views/ci/shared/_guide.html.haml | 15 + app/views/ci/shared/_no_runners.html.haml | 7 + app/views/ci/triggers/_trigger.html.haml | 14 + app/views/ci/triggers/index.html.haml | 67 ++ app/views/ci/user_sessions/new.html.haml | 8 + app/views/ci/user_sessions/show.html.haml | 15 + app/views/ci/variables/show.html.haml | 37 + app/views/ci/web_hooks/index.html.haml | 92 +++ app/views/layouts/ci/_head.html.haml | 11 + app/views/layouts/ci/_info.html.haml | 9 + app/views/layouts/ci/_nav.html.haml | 32 + app/views/layouts/ci/_nav_admin.html.haml | 28 + app/views/layouts/ci/_nav_project.html.haml | 40 + app/views/layouts/ci/admin.html.haml | 17 + app/views/layouts/ci/application.html.haml | 13 + app/views/layouts/ci/empty.html.haml | 13 + app/views/layouts/ci/notify.html.haml | 19 + app/views/layouts/ci/project.html.haml | 26 + app/workers/ci/hip_chat_notifier_worker.rb | 19 + app/workers/ci/slack_notifier_worker.rb | 10 + app/workers/ci/web_hook_worker.rb | 9 + bin/background_jobs | 2 +- bin/ci/upgrade.rb | 3 + config/environments/development.rb | 5 + config/gitlab_ci.yml | 19 + config/gitlab_ci.yml.example | 68 ++ config/gitlab_ci.yml.example.development | 19 + config/initializers/3_ci_settings.rb | 61 ++ config/initializers/3_grit_ext.rb | 5 - config/initializers/4_ci_app.rb | 10 + config/initializers/4_sidekiq.rb | 27 - config/initializers/6_rack_profiler.rb | 10 - config/initializers/7_omniauth.rb | 28 - config/initializers/8_default_url_options.rb | 11 - config/initializers/connection_fix.rb | 32 + config/initializers/cookies_serializer.rb | 3 + config/initializers/default_url_options.rb | 11 + config/initializers/rack_attack.rb.example | 14 +- config/initializers/rack_profiler.rb | 10 + config/initializers/secret_token.rb | 24 + config/initializers/session_store.rb | 2 +- config/initializers/sidekiq.rb | 27 + config/initializers/static_files.rb | 2 +- config/locales/devise.en.yml | 7 +- config/routes.rb | 99 +++ config/schedule.rb | 8 + config/secrets.yml | 3 + config/secrets.yml.example | 12 + config/sidekiq.yml.example | 2 + db/ci/migrate/20121004140911_create_projects.rb | 14 + db/ci/migrate/20121004165038_create_builds.rb | 15 + .../migrate/20121101091638_devise_create_users.rb | 46 ++ .../migrate/20121101121639_add_token_to_project.rb | 5 + .../20121106143042_add_ref_functionality.rb | 10 + .../20121108160657_add_gitlab_url_to_project.rb | 5 + .../20121108174237_add_started_at_to_build.rb | 5 + .../20121115094430_increate_trace_colunm_limit.rb | 8 + .../20121115132252_add_tmp_file_to_build.rb | 5 + .../20121116144312_add_before_sha_to_build.rb | 5 + .../20121224092350_add_schedule_to_projects.rb | 6 + .../20130114153451_change_schedule_invertal.rb | 25 + .../20130129121754_add_public_flag_to_project.rb | 5 + .../20130531112551_add_data_field_to_build.rb | 5 + ...0130531122131_remove_path_field_from_project.rb | 8 + db/ci/migrate/20130531125905_create_runners.rb | 10 + .../20130531133603_add_runner_id_to_build.rb | 5 + db/ci/migrate/20130603130920_remove_users_table.rb | 5 + .../20130603144030_add_more_fields_to_project.rb | 5 + .../20130603144959_create_runner_projects.rb | 10 + ...30603161449_add_project_gitlab_id_to_project.rb | 5 + ...0130628142321_add_index_project_id_to_builds.rb | 5 + .../20130705171042_add_description_to_runner.rb | 5 + db/ci/migrate/20130710164015_add_db_index.rb | 7 + .../20130816201200_change_push_data_limit.rb | 5 + db/ci/migrate/20130906175737_add_sessions_table.rb | 12 + ...0131023103430_add_allow_git_fetch_to_project.rb | 5 + ...545_add_email_notification_fields_to_project.rb | 7 + .../20140130121538_rename_project_fields.rb | 5 + db/ci/migrate/20140222210357_create_web_hook.rb | 9 + ...20140506091853_remove_public_key_from_runner.rb | 5 + .../20140823225019_create_commits_from_builds.rb | 22 + .../20140909142245_add_skip_refs_to_projects.rb | 5 + .../migrate/20141001125939_add_coverage_parser.rb | 5 + .../20141001132129_add_coverage_to_build.rb | 5 + .../20141028162820_add_sha_index_to_build.rb | 6 + .../20141031114419_migrate_build_to_commits.rb | 21 + .../migrate/20141031141708_add_commit_indicies.rb | 9 + .../20141103135037_add_parallel_to_build.rb | 12 + .../20141103151359_add_commands_to_build.rb | 5 + .../migrate/20141103162726_add_job_id_to_build.rb | 5 + db/ci/migrate/20141104130024_migrate_jobs.rb | 12 + db/ci/migrate/20141104153744_add_name_to_job.rb | 5 + .../20141127153745_remove_scripts_from_project.rb | 5 + .../migrate/20141201153755_remove_invalid_build.rb | 5 + db/ci/migrate/20141204133321_create_service.rb | 15 + db/ci/migrate/20150111062026_add_filter_to_jobs.rb | 6 + ...able_on_migration.acts_as_taggable_on_engine.rb | 31 + ...ng_unique_indices.acts_as_taggable_on_engine.rb | 20 + ...ter_cache_to_tags.acts_as_taggable_on_engine.rb | 15 + ...ng_taggable_index.acts_as_taggable_on_engine.rb | 10 + .../20150204001035_build_missing_services.rb | 21 + .../migrate/20150226001835_add_job_type_to_job.rb | 6 + .../20150306131416_add_contacted_at_to_runner.rb | 5 + .../migrate/20150306135341_add_active_to_runner.rb | 5 + .../20150310001733_rename_committer_to_pusher.rb | 5 + db/ci/migrate/20150320001810_create_event_table.rb | 16 + ...150324001123_add_settings_for_shared_runners.rb | 6 + .../20150324001227_migrate_shared_runners.rb | 11 + .../20150330001111_disable_shared_runners.rb | 8 + .../20150415142013_add_deleted_at_to_jobs.rb | 6 + .../20150417000045_cleanup_the_build_model.rb | 9 + .../migrate/20150504010150_migrate_url_to_path.rb | 11 + .../20150504010250_rename_gitlab_url_to_path.rb | 5 + .../20150508011360_add_info_fields_to_runner.rb | 9 + .../migrate/20150528011001_add_fields_to_builds.rb | 6 + .../20150528011012_move_job_name_to_build.rb | 10 + db/ci/migrate/20150529012113_add_tag_to_commits.rb | 5 + .../migrate/20150601043220_add_yaml_to_projects.rb | 9 + .../migrate/20150601043231_migrate_jobs_to_yaml.rb | 97 +++ .../20150602000240_change_default_build_timeout.rb | 9 + db/ci/migrate/20150605002131_create_variables.rb | 11 + .../migrate/20150616001155_add_errors_to_commit.rb | 5 + .../migrate/20150630091815_add_options_to_build.rb | 5 + ...50703125244_add_encrypted_value_to_variables.rb | 7 + db/ci/migrate/20150703125325_encrypt_variables.rb | 10 + .../20150707134456_add_allow_failure_to_builds.rb | 5 + .../20150710113836_add_job_type_to_builds.rb | 5 + ...113851_migrate_deploy_to_job_type_for_builds.rb | 6 + db/ci/migrate/20150721204649_truncate_sessions.rb | 9 + .../20150729145246_create_application_settings.rb | 10 + ...150803142346_rename_job_type_to_stage_builds.rb | 9 + .../20150806091503_add_committed_at_to_commits.rb | 6 + ...06091655_update_committed_at_with_created_at.rb | 5 + db/ci/migrate/20150806102222_create_trigger.rb | 12 + .../20150806102457_add_trigger_to_builds.rb | 5 + .../20150806105404_create_trigger_request.rb | 9 + ...0819162227_add_commit_id_to_trigger_requests.rb | 8 + db/ci/schema.rb | 226 ++++++ db/ci/seeds.rb | 0 db/migrate/20150826001931_add_ci_tables.rb | 190 +++++ db/migrate/limits_to_mysql.rb | 4 + db/schema.rb | 334 +++++++- doc/ci/README.md | 27 + doc/ci/api/README.md | 87 ++ doc/ci/api/builds.md | 41 + doc/ci/api/commits.md | 101 +++ doc/ci/api/forks.md | 23 + doc/ci/api/projects.md | 154 ++++ doc/ci/api/runners.md | 77 ++ doc/ci/deployment/README.md | 98 +++ doc/ci/docker/README.md | 4 + doc/ci/docker/using_docker_build.md | 112 +++ doc/ci/docker/using_docker_images.md | 203 +++++ doc/ci/examples/README.md | 5 + ...test-and-deploy-python-application-to-heroku.md | 72 ++ .../test-and-deploy-ruby-application-to-heroku.md | 67 ++ doc/ci/examples/test-clojure-application.md | 35 + doc/ci/install/README.md | 276 +++++++ doc/ci/install/requirements.md | 61 ++ doc/ci/migration_to_omnibus/README.md | 29 + doc/ci/permissions/README.md | 24 + doc/ci/quick_start/README.md | 119 +++ doc/ci/quick_start/build_status.png | Bin 0 -> 62140 bytes doc/ci/quick_start/commit_status.png | Bin 0 -> 33492 bytes doc/ci/quick_start/new_commit.png | Bin 0 -> 47527 bytes doc/ci/quick_start/projects.png | Bin 0 -> 37014 bytes doc/ci/quick_start/runners.png | Bin 0 -> 123048 bytes doc/ci/quick_start/runners_activated.png | Bin 0 -> 60769 bytes doc/ci/raketasks/README.md | 3 + doc/ci/raketasks/backup_restore.md | 237 ++++++ doc/ci/runners/README.md | 145 ++++ doc/ci/runners/project_specific.png | Bin 0 -> 31408 bytes doc/ci/runners/shared_runner.png | Bin 0 -> 18366 bytes doc/ci/runners/shared_to_specific_admin.png | Bin 0 -> 5897 bytes doc/ci/update/3.0-to-3.1.md | 30 + doc/ci/update/3.1-to-3.2.md | 30 + doc/ci/update/3.2-to-4.0.md | 35 + doc/ci/update/4.0-to-4.1.md | 49 ++ doc/ci/update/4.1-to-4.2.md | 47 ++ doc/ci/update/4.2-to-4.3.md | 61 ++ doc/ci/update/4.3-to-5.0.md | 42 + doc/ci/update/5.0-to-5.1.md | 42 + doc/ci/update/5.1-to-5.2.md | 42 + doc/ci/update/5.2-to-5.3.md | 42 + doc/ci/update/5.3-to-5.4.md | 60 ++ doc/ci/update/5.4-to-7.8.md | 65 ++ doc/ci/update/7.10-to-7.11.md | 45 + doc/ci/update/7.11-to-7.12.md | 67 ++ doc/ci/update/7.12-to-7.13.md | 63 ++ doc/ci/update/7.8-to-7.9.md | 66 ++ doc/ci/update/7.9-to-7.10.md | 49 ++ doc/ci/update/README.md | 2 + doc/ci/update/patch_versions.md | 59 ++ doc/ci/variables/README.md | 95 +++ doc/ci/yaml/README.md | 204 +++++ lib/api/entities.rb | 4 +- lib/ci/ansi2html.rb | 224 +++++ lib/ci/api/api.rb | 37 + lib/ci/api/builds.rb | 53 ++ lib/ci/api/commits.rb | 66 ++ lib/ci/api/entities.rb | 44 + lib/ci/api/forks.rb | 40 + lib/ci/api/helpers.rb | 114 +++ lib/ci/api/projects.rb | 209 +++++ lib/ci/api/runners.rb | 69 ++ lib/ci/api/triggers.rb | 49 ++ lib/ci/assets/.gitkeep | 0 lib/ci/backup/builds.rb | 32 + lib/ci/backup/database.rb | 94 +++ lib/ci/backup/manager.rb | 158 ++++ lib/ci/charts.rb | 71 ++ lib/ci/current_settings.rb | 22 + lib/ci/git.rb | 5 + lib/ci/gitlab_ci_yaml_processor.rb | 198 +++++ lib/ci/model.rb | 11 + lib/ci/scheduler.rb | 16 + lib/ci/static_model.rb | 49 ++ lib/ci/version_info.rb | 52 ++ .../markdown/commit_range_reference_filter.rb | 2 +- lib/gitlab/markdown/commit_reference_filter.rb | 2 +- lib/gitlab/markdown/label_reference_filter.rb | 2 +- .../markdown/merge_request_reference_filter.rb | 2 +- lib/gitlab/markdown/snippet_reference_filter.rb | 2 +- lib/gitlab/markdown/user_reference_filter.rb | 2 +- lib/gitlab/url_builder.rb | 4 +- lib/tasks/ci/.gitkeep | 0 lib/tasks/ci/backup.rake | 62 ++ lib/tasks/ci/cleanup.rake | 8 + lib/tasks/ci/schedule_builds.rake | 6 + lib/tasks/ci/setup.rake | 7 + lib/tasks/ci/sidekiq.rake | 13 + public/ci/build-canceled.svg | 1 + public/ci/build-failed.svg | 1 + public/ci/build-pending.svg | 1 + public/ci/build-running.svg | 1 + public/ci/build-success.svg | 1 + public/ci/build-unknown.svg | 1 + public/ci/favicon.ico | Bin 0 -> 5430 bytes scripts/ci/prepare_build.sh | 22 + spec/ci/controllers/commits_controller_spec.rb | 27 + spec/ci/controllers/projects_controller_spec.rb | 108 +++ spec/ci/factories/builds.rb | 45 + spec/ci/factories/commits.rb | 75 ++ spec/ci/factories/events.rb | 24 + spec/ci/factories/projects.rb | 56 ++ spec/ci/factories/runner_projects.rb | 19 + spec/ci/factories/runners.rb | 38 + spec/ci/factories/trigger_requests.rb | 13 + spec/ci/factories/triggers.rb | 9 + spec/ci/factories/users.rb | 6 + spec/ci/factories/web_hook.rb | 6 + spec/ci/features/admin/builds_spec.rb | 71 ++ spec/ci/features/admin/events_spec.rb | 20 + spec/ci/features/admin/projects_spec.rb | 19 + spec/ci/features/admin/runners_spec.rb | 63 ++ spec/ci/features/builds_spec.rb | 57 ++ spec/ci/features/commits_spec.rb | 66 ++ spec/ci/features/events_spec.rb | 20 + spec/ci/features/lint_spec.rb | 28 + spec/ci/features/projects_spec.rb | 57 ++ spec/ci/features/runners_spec.rb | 98 +++ spec/ci/features/triggers_spec.rb | 26 + spec/ci/features/variables_spec.rb | 26 + spec/ci/helpers/application_helper_spec.rb | 37 + spec/ci/helpers/runners_helper_spec.rb | 18 + spec/ci/helpers/user_helper_spec.rb | 49 ++ spec/ci/helpers/user_sessions_helper_spec.rb | 69 ++ spec/ci/lib/ansi2html_spec.rb | 133 +++ spec/ci/lib/charts_spec.rb | 17 + spec/ci/lib/gitlab_ci_yaml_processor_spec.rb | 311 +++++++ spec/ci/lib/upgrader_spec.rb | 39 + spec/ci/mailers/notify_spec.rb | 36 + spec/ci/models/build_spec.rb | 350 ++++++++ spec/ci/models/commit_spec.rb | 264 ++++++ spec/ci/models/mail_service_spec.rb | 184 +++++ spec/ci/models/network_spec.rb | 54 ++ .../project_services/hip_chat_message_spec.rb | 74 ++ .../project_services/hip_chat_service_spec.rb | 75 ++ .../models/project_services/slack_message_spec.rb | 84 ++ .../models/project_services/slack_service_spec.rb | 58 ++ spec/ci/models/project_spec.rb | 185 +++++ spec/ci/models/runner_project_spec.rb | 16 + spec/ci/models/runner_spec.rb | 70 ++ spec/ci/models/service_spec.rb | 49 ++ spec/ci/models/trigger_spec.rb | 17 + spec/ci/models/user_spec.rb | 100 +++ spec/ci/models/variable_spec.rb | 44 + spec/ci/models/web_hook_spec.rb | 64 ++ spec/ci/requests/api/builds_spec.rb | 115 +++ spec/ci/requests/api/commits_spec.rb | 65 ++ spec/ci/requests/api/forks_spec.rb | 60 ++ spec/ci/requests/api/projects_spec.rb | 251 ++++++ spec/ci/requests/api/runners_spec.rb | 83 ++ spec/ci/requests/api/triggers_spec.rb | 78 ++ spec/ci/requests/builds_spec.rb | 18 + spec/ci/requests/commits_spec.rb | 17 + spec/ci/services/create_commit_service_spec.rb | 130 +++ spec/ci/services/create_project_service_spec.rb | 40 + .../create_trigger_request_service_spec.rb | 52 ++ spec/ci/services/event_service_spec.rb | 34 + spec/ci/services/image_for_build_service_spec.rb | 46 ++ spec/ci/services/register_build_service_spec.rb | 89 ++ spec/ci/services/web_hook_service_spec.rb | 36 + spec/ci/six.tar.gz | Bin 0 -> 61937 bytes spec/ci/spec_helper.rb | 60 ++ spec/ci/support/api_helpers.rb | 35 + spec/ci/support/db_cleaner.rb | 39 + spec/ci/support/gitlab_stubs/gitlab_ci.yml | 63 ++ spec/ci/support/gitlab_stubs/project_8.json | 45 + spec/ci/support/gitlab_stubs/project_8_hooks.json | 1 + spec/ci/support/gitlab_stubs/projects.json | 1 + spec/ci/support/gitlab_stubs/raw_project.yml | 36 + spec/ci/support/gitlab_stubs/session.json | 20 + spec/ci/support/gitlab_stubs/user.json | 20 + spec/ci/support/login_helpers.rb | 22 + spec/ci/support/monkey_patches/oauth2.rb | 7 + spec/ci/support/setup_builds_storage.rb | 16 + spec/ci/support/stub_gitlab_calls.rb | 77 ++ spec/ci/support/stub_gitlab_data.rb | 5 + spec/lib/extracts_path_spec.rb | 2 +- spec/support/filter_spec_helper.rb | 2 +- 576 files changed, 23545 insertions(+), 3690 deletions(-) create mode 100644 CHANGELOG-CI create mode 100644 app/assets/images/ci/arch.jpg create mode 100644 app/assets/images/ci/favicon.ico create mode 100644 app/assets/images/ci/loader.gif create mode 100644 app/assets/images/ci/no_avatar.png create mode 100644 app/assets/images/ci/rails.png create mode 100644 app/assets/images/ci/service_sample.png create mode 100644 app/assets/javascripts/ci/Chart.min.js create mode 100644 app/assets/javascripts/ci/application.js.coffee create mode 100644 app/assets/javascripts/ci/build.coffee create mode 100644 app/assets/javascripts/ci/pager.js.coffee create mode 100644 app/assets/javascripts/ci/projects.js.coffee create mode 100644 app/assets/stylesheets/ci/application.scss create mode 100644 app/assets/stylesheets/ci/generic/avatar.scss create mode 100644 app/assets/stylesheets/ci/generic/buttons.scss create mode 100644 app/assets/stylesheets/ci/generic/callout.scss create mode 100644 app/assets/stylesheets/ci/generic/common.scss create mode 100644 app/assets/stylesheets/ci/generic/forms.scss create mode 100644 app/assets/stylesheets/ci/generic/tables.scss create mode 100644 app/assets/stylesheets/ci/generic/typography.scss create mode 100644 app/assets/stylesheets/ci/generic/xterm.scss create mode 100644 app/assets/stylesheets/ci/main/fonts.scss create mode 100644 app/assets/stylesheets/ci/main/layout.scss create mode 100644 app/assets/stylesheets/ci/main/mixins.scss create mode 100644 app/assets/stylesheets/ci/main/variables.scss create mode 100644 app/assets/stylesheets/ci/sections/builds.scss create mode 100644 app/assets/stylesheets/ci/sections/lint.scss create mode 100644 app/assets/stylesheets/ci/sections/login.scss create mode 100644 app/assets/stylesheets/ci/sections/navbar.scss create mode 100644 app/assets/stylesheets/ci/sections/projects.scss create mode 100644 app/assets/stylesheets/ci/sections/runners.scss create mode 100644 app/assets/stylesheets/ci/sections/setup.scss create mode 100644 app/controllers/ci/admin/application_controller.rb create mode 100644 app/controllers/ci/admin/application_settings_controller.rb create mode 100644 app/controllers/ci/admin/builds_controller.rb create mode 100644 app/controllers/ci/admin/events_controller.rb create mode 100644 app/controllers/ci/admin/projects_controller.rb create mode 100644 app/controllers/ci/admin/runner_projects_controller.rb create mode 100644 app/controllers/ci/admin/runners_controller.rb create mode 100644 app/controllers/ci/application_controller.rb create mode 100644 app/controllers/ci/builds_controller.rb create mode 100644 app/controllers/ci/charts_controller.rb create mode 100644 app/controllers/ci/commits_controller.rb create mode 100644 app/controllers/ci/events_controller.rb create mode 100644 app/controllers/ci/helps_controller.rb create mode 100644 app/controllers/ci/lints_controller.rb create mode 100644 app/controllers/ci/projects_controller.rb create mode 100644 app/controllers/ci/runner_projects_controller.rb create mode 100644 app/controllers/ci/runners_controller.rb create mode 100644 app/controllers/ci/services_controller.rb create mode 100644 app/controllers/ci/triggers_controller.rb create mode 100644 app/controllers/ci/user_sessions_controller.rb create mode 100644 app/controllers/ci/variables_controller.rb create mode 100644 app/controllers/ci/web_hooks_controller.rb delete mode 100644 app/helpers/appearances_helper.rb delete mode 100644 app/helpers/application_helper.rb delete mode 100644 app/helpers/application_settings_helper.rb delete mode 100644 app/helpers/auth_helper.rb delete mode 100644 app/helpers/blob_helper.rb delete mode 100644 app/helpers/branches_helper.rb delete mode 100644 app/helpers/broadcast_messages_helper.rb create mode 100644 app/helpers/ci/application_helper.rb create mode 100644 app/helpers/ci/builds_helper.rb create mode 100644 app/helpers/ci/commits_helper.rb create mode 100644 app/helpers/ci/gitlab_helper.rb create mode 100644 app/helpers/ci/icons_helper.rb create mode 100644 app/helpers/ci/projects_helper.rb create mode 100644 app/helpers/ci/routes_helper.rb create mode 100644 app/helpers/ci/runners_helper.rb create mode 100644 app/helpers/ci/triggers_helper.rb create mode 100644 app/helpers/ci/user_helper.rb create mode 100644 app/helpers/ci/user_sessions_helper.rb delete mode 100644 app/helpers/commits_helper.rb delete mode 100644 app/helpers/compare_helper.rb delete mode 100644 app/helpers/dashboard_helper.rb delete mode 100644 app/helpers/diff_helper.rb delete mode 100644 app/helpers/emails_helper.rb delete mode 100644 app/helpers/events_helper.rb delete mode 100644 app/helpers/explore_helper.rb delete mode 100644 app/helpers/external_wiki_helper.rb delete mode 100644 app/helpers/git_helper.rb create mode 100644 app/helpers/gitlab/appearances_helper.rb create mode 100644 app/helpers/gitlab/application_helper.rb create mode 100644 app/helpers/gitlab/application_settings_helper.rb create mode 100644 app/helpers/gitlab/auth_helper.rb create mode 100644 app/helpers/gitlab/blob_helper.rb create mode 100644 app/helpers/gitlab/branches_helper.rb create mode 100644 app/helpers/gitlab/broadcast_messages_helper.rb create mode 100644 app/helpers/gitlab/commits_helper.rb create mode 100644 app/helpers/gitlab/compare_helper.rb create mode 100644 app/helpers/gitlab/dashboard_helper.rb create mode 100644 app/helpers/gitlab/diff_helper.rb create mode 100644 app/helpers/gitlab/emails_helper.rb create mode 100644 app/helpers/gitlab/events_helper.rb create mode 100644 app/helpers/gitlab/explore_helper.rb create mode 100644 app/helpers/gitlab/external_wiki_helper.rb create mode 100644 app/helpers/gitlab/git_helper.rb create mode 100644 app/helpers/gitlab/gitlab_markdown_helper.rb create mode 100644 app/helpers/gitlab/gitlab_routing_helper.rb create mode 100644 app/helpers/gitlab/graph_helper.rb create mode 100644 app/helpers/gitlab/groups_helper.rb create mode 100644 app/helpers/gitlab/icons_helper.rb create mode 100644 app/helpers/gitlab/issues_helper.rb create mode 100644 app/helpers/gitlab/labels_helper.rb create mode 100644 app/helpers/gitlab/merge_requests_helper.rb create mode 100644 app/helpers/gitlab/milestones_helper.rb create mode 100644 app/helpers/gitlab/namespaces_helper.rb create mode 100644 app/helpers/gitlab/nav_helper.rb create mode 100644 app/helpers/gitlab/notes_helper.rb create mode 100644 app/helpers/gitlab/notifications_helper.rb create mode 100644 app/helpers/gitlab/page_layout_helper.rb create mode 100644 app/helpers/gitlab/preferences_helper.rb create mode 100644 app/helpers/gitlab/projects_helper.rb create mode 100644 app/helpers/gitlab/search_helper.rb create mode 100644 app/helpers/gitlab/selects_helper.rb create mode 100644 app/helpers/gitlab/snippets_helper.rb create mode 100644 app/helpers/gitlab/sorting_helper.rb create mode 100644 app/helpers/gitlab/submodule_helper.rb create mode 100644 app/helpers/gitlab/tab_helper.rb create mode 100644 app/helpers/gitlab/tags_helper.rb create mode 100644 app/helpers/gitlab/tree_helper.rb create mode 100644 app/helpers/gitlab/version_check_helper.rb create mode 100644 app/helpers/gitlab/visibility_level_helper.rb create mode 100644 app/helpers/gitlab/wiki_helper.rb delete mode 100644 app/helpers/gitlab_markdown_helper.rb delete mode 100644 app/helpers/gitlab_routing_helper.rb delete mode 100644 app/helpers/graph_helper.rb delete mode 100644 app/helpers/groups_helper.rb delete mode 100644 app/helpers/icons_helper.rb delete mode 100644 app/helpers/issues_helper.rb delete mode 100644 app/helpers/labels_helper.rb delete mode 100644 app/helpers/merge_requests_helper.rb delete mode 100644 app/helpers/milestones_helper.rb delete mode 100644 app/helpers/namespaces_helper.rb delete mode 100644 app/helpers/nav_helper.rb delete mode 100644 app/helpers/notes_helper.rb delete mode 100644 app/helpers/notifications_helper.rb delete mode 100644 app/helpers/page_layout_helper.rb delete mode 100644 app/helpers/preferences_helper.rb delete mode 100644 app/helpers/projects_helper.rb delete mode 100644 app/helpers/search_helper.rb delete mode 100644 app/helpers/selects_helper.rb delete mode 100644 app/helpers/snippets_helper.rb delete mode 100644 app/helpers/sorting_helper.rb delete mode 100644 app/helpers/submodule_helper.rb delete mode 100644 app/helpers/tab_helper.rb delete mode 100644 app/helpers/tags_helper.rb delete mode 100644 app/helpers/tree_helper.rb delete mode 100644 app/helpers/version_check_helper.rb delete mode 100644 app/helpers/visibility_level_helper.rb delete mode 100644 app/helpers/wiki_helper.rb create mode 100644 app/mailers/ci/emails/builds.rb create mode 100644 app/mailers/ci/notify.rb create mode 100644 app/models/ci/application_setting.rb create mode 100644 app/models/ci/build.rb create mode 100644 app/models/ci/commit.rb create mode 100644 app/models/ci/event.rb create mode 100644 app/models/ci/network.rb create mode 100644 app/models/ci/project.rb create mode 100644 app/models/ci/project_status.rb create mode 100644 app/models/ci/runner.rb create mode 100644 app/models/ci/runner_project.rb create mode 100644 app/models/ci/service.rb create mode 100644 app/models/ci/trigger.rb create mode 100644 app/models/ci/trigger_request.rb create mode 100644 app/models/ci/user.rb create mode 100644 app/models/ci/user_session.rb create mode 100644 app/models/ci/variable.rb create mode 100644 app/models/ci/web_hook.rb create mode 100644 app/models/project_services/ci/hip_chat_message.rb create mode 100644 app/models/project_services/ci/hip_chat_service.rb create mode 100644 app/models/project_services/ci/mail_service.rb create mode 100644 app/models/project_services/ci/slack_message.rb create mode 100644 app/models/project_services/ci/slack_service.rb create mode 100644 app/services/ci/create_commit_service.rb create mode 100644 app/services/ci/create_project_service.rb create mode 100644 app/services/ci/create_trigger_request_service.rb create mode 100644 app/services/ci/event_service.rb create mode 100644 app/services/ci/image_for_build_service.rb create mode 100644 app/services/ci/register_build_service.rb create mode 100644 app/services/ci/test_hook_service.rb create mode 100644 app/services/ci/web_hook_service.rb create mode 100644 app/views/ci/admin/application_settings/_form.html.haml create mode 100644 app/views/ci/admin/application_settings/show.html.haml create mode 100644 app/views/ci/admin/builds/_build.html.haml create mode 100644 app/views/ci/admin/builds/index.html.haml create mode 100644 app/views/ci/admin/events/index.html.haml create mode 100644 app/views/ci/admin/projects/_project.html.haml create mode 100644 app/views/ci/admin/projects/index.html.haml create mode 100644 app/views/ci/admin/runner_projects/index.html.haml create mode 100644 app/views/ci/admin/runners/_runner.html.haml create mode 100644 app/views/ci/admin/runners/index.html.haml create mode 100644 app/views/ci/admin/runners/show.html.haml create mode 100644 app/views/ci/admin/runners/update.js.haml create mode 100644 app/views/ci/builds/_build.html.haml create mode 100644 app/views/ci/builds/show.html.haml create mode 100644 app/views/ci/charts/_build_times.haml create mode 100644 app/views/ci/charts/_builds.haml create mode 100644 app/views/ci/charts/_overall.haml create mode 100644 app/views/ci/charts/show.html.haml create mode 100644 app/views/ci/commits/_commit.html.haml create mode 100644 app/views/ci/commits/show.html.haml create mode 100644 app/views/ci/errors/show.haml create mode 100644 app/views/ci/events/index.html.haml create mode 100644 app/views/ci/helps/oauth2.html.haml create mode 100644 app/views/ci/helps/show.html.haml create mode 100644 app/views/ci/kaminari/_first_page.html.haml create mode 100644 app/views/ci/kaminari/_gap.html.haml create mode 100644 app/views/ci/kaminari/_last_page.html.haml create mode 100644 app/views/ci/kaminari/_next_page.html.haml create mode 100644 app/views/ci/kaminari/_page.html.haml create mode 100644 app/views/ci/kaminari/_paginator.html.haml create mode 100644 app/views/ci/kaminari/_prev_page.html.haml create mode 100644 app/views/ci/lints/_create.html.haml create mode 100644 app/views/ci/lints/create.js.haml create mode 100644 app/views/ci/lints/show.html.haml create mode 100644 app/views/ci/notify/build_fail_email.html.haml create mode 100644 app/views/ci/notify/build_fail_email.text.erb create mode 100644 app/views/ci/notify/build_success_email.html.haml create mode 100644 app/views/ci/notify/build_success_email.text.erb create mode 100644 app/views/ci/projects/_form.html.haml create mode 100644 app/views/ci/projects/_gl_projects.html.haml create mode 100644 app/views/ci/projects/_info.html.haml create mode 100644 app/views/ci/projects/_no_runners.html.haml create mode 100644 app/views/ci/projects/_project.html.haml create mode 100644 app/views/ci/projects/_public.html.haml create mode 100644 app/views/ci/projects/_search.html.haml create mode 100644 app/views/ci/projects/edit.html.haml create mode 100644 app/views/ci/projects/gitlab.html.haml create mode 100644 app/views/ci/projects/index.html.haml create mode 100644 app/views/ci/projects/show.html.haml create mode 100644 app/views/ci/runners/_runner.html.haml create mode 100644 app/views/ci/runners/_shared_runners.html.haml create mode 100644 app/views/ci/runners/_specific_runners.html.haml create mode 100644 app/views/ci/runners/edit.html.haml create mode 100644 app/views/ci/runners/index.html.haml create mode 100644 app/views/ci/runners/show.html.haml create mode 100644 app/views/ci/services/_form.html.haml create mode 100644 app/views/ci/services/edit.html.haml create mode 100644 app/views/ci/services/index.html.haml create mode 100644 app/views/ci/shared/_guide.html.haml create mode 100644 app/views/ci/shared/_no_runners.html.haml create mode 100644 app/views/ci/triggers/_trigger.html.haml create mode 100644 app/views/ci/triggers/index.html.haml create mode 100644 app/views/ci/user_sessions/new.html.haml create mode 100644 app/views/ci/user_sessions/show.html.haml create mode 100644 app/views/ci/variables/show.html.haml create mode 100644 app/views/ci/web_hooks/index.html.haml create mode 100644 app/views/layouts/ci/_head.html.haml create mode 100644 app/views/layouts/ci/_info.html.haml create mode 100644 app/views/layouts/ci/_nav.html.haml create mode 100644 app/views/layouts/ci/_nav_admin.html.haml create mode 100644 app/views/layouts/ci/_nav_project.html.haml create mode 100644 app/views/layouts/ci/admin.html.haml create mode 100644 app/views/layouts/ci/application.html.haml create mode 100644 app/views/layouts/ci/empty.html.haml create mode 100644 app/views/layouts/ci/notify.html.haml create mode 100644 app/views/layouts/ci/project.html.haml create mode 100644 app/workers/ci/hip_chat_notifier_worker.rb create mode 100644 app/workers/ci/slack_notifier_worker.rb create mode 100644 app/workers/ci/web_hook_worker.rb create mode 100644 bin/ci/upgrade.rb create mode 100644 config/gitlab_ci.yml create mode 100644 config/gitlab_ci.yml.example create mode 100644 config/gitlab_ci.yml.example.development create mode 100644 config/initializers/3_ci_settings.rb delete mode 100644 config/initializers/3_grit_ext.rb create mode 100644 config/initializers/4_ci_app.rb delete mode 100644 config/initializers/4_sidekiq.rb delete mode 100644 config/initializers/6_rack_profiler.rb delete mode 100644 config/initializers/7_omniauth.rb delete mode 100644 config/initializers/8_default_url_options.rb create mode 100644 config/initializers/connection_fix.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/initializers/default_url_options.rb create mode 100644 config/initializers/rack_profiler.rb create mode 100644 config/initializers/sidekiq.rb create mode 100644 config/schedule.rb create mode 100644 config/secrets.yml create mode 100644 config/secrets.yml.example create mode 100644 config/sidekiq.yml.example create mode 100644 db/ci/migrate/20121004140911_create_projects.rb create mode 100644 db/ci/migrate/20121004165038_create_builds.rb create mode 100644 db/ci/migrate/20121101091638_devise_create_users.rb create mode 100644 db/ci/migrate/20121101121639_add_token_to_project.rb create mode 100644 db/ci/migrate/20121106143042_add_ref_functionality.rb create mode 100644 db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb create mode 100644 db/ci/migrate/20121108174237_add_started_at_to_build.rb create mode 100644 db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb create mode 100644 db/ci/migrate/20121115132252_add_tmp_file_to_build.rb create mode 100644 db/ci/migrate/20121116144312_add_before_sha_to_build.rb create mode 100644 db/ci/migrate/20121224092350_add_schedule_to_projects.rb create mode 100644 db/ci/migrate/20130114153451_change_schedule_invertal.rb create mode 100644 db/ci/migrate/20130129121754_add_public_flag_to_project.rb create mode 100644 db/ci/migrate/20130531112551_add_data_field_to_build.rb create mode 100644 db/ci/migrate/20130531122131_remove_path_field_from_project.rb create mode 100644 db/ci/migrate/20130531125905_create_runners.rb create mode 100644 db/ci/migrate/20130531133603_add_runner_id_to_build.rb create mode 100644 db/ci/migrate/20130603130920_remove_users_table.rb create mode 100644 db/ci/migrate/20130603144030_add_more_fields_to_project.rb create mode 100644 db/ci/migrate/20130603144959_create_runner_projects.rb create mode 100644 db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb create mode 100644 db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb create mode 100644 db/ci/migrate/20130705171042_add_description_to_runner.rb create mode 100644 db/ci/migrate/20130710164015_add_db_index.rb create mode 100644 db/ci/migrate/20130816201200_change_push_data_limit.rb create mode 100644 db/ci/migrate/20130906175737_add_sessions_table.rb create mode 100644 db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb create mode 100644 db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb create mode 100644 db/ci/migrate/20140130121538_rename_project_fields.rb create mode 100644 db/ci/migrate/20140222210357_create_web_hook.rb create mode 100644 db/ci/migrate/20140506091853_remove_public_key_from_runner.rb create mode 100644 db/ci/migrate/20140823225019_create_commits_from_builds.rb create mode 100644 db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb create mode 100644 db/ci/migrate/20141001125939_add_coverage_parser.rb create mode 100644 db/ci/migrate/20141001132129_add_coverage_to_build.rb create mode 100644 db/ci/migrate/20141028162820_add_sha_index_to_build.rb create mode 100644 db/ci/migrate/20141031114419_migrate_build_to_commits.rb create mode 100644 db/ci/migrate/20141031141708_add_commit_indicies.rb create mode 100644 db/ci/migrate/20141103135037_add_parallel_to_build.rb create mode 100644 db/ci/migrate/20141103151359_add_commands_to_build.rb create mode 100644 db/ci/migrate/20141103162726_add_job_id_to_build.rb create mode 100644 db/ci/migrate/20141104130024_migrate_jobs.rb create mode 100644 db/ci/migrate/20141104153744_add_name_to_job.rb create mode 100644 db/ci/migrate/20141127153745_remove_scripts_from_project.rb create mode 100644 db/ci/migrate/20141201153755_remove_invalid_build.rb create mode 100644 db/ci/migrate/20141204133321_create_service.rb create mode 100644 db/ci/migrate/20150111062026_add_filter_to_jobs.rb create mode 100644 db/ci/migrate/20150113001832_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb create mode 100644 db/ci/migrate/20150113001833_add_missing_unique_indices.acts_as_taggable_on_engine.rb create mode 100644 db/ci/migrate/20150113001834_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb create mode 100644 db/ci/migrate/20150113001835_add_missing_taggable_index.acts_as_taggable_on_engine.rb create mode 100644 db/ci/migrate/20150204001035_build_missing_services.rb create mode 100644 db/ci/migrate/20150226001835_add_job_type_to_job.rb create mode 100644 db/ci/migrate/20150306131416_add_contacted_at_to_runner.rb create mode 100644 db/ci/migrate/20150306135341_add_active_to_runner.rb create mode 100644 db/ci/migrate/20150310001733_rename_committer_to_pusher.rb create mode 100644 db/ci/migrate/20150320001810_create_event_table.rb create mode 100644 db/ci/migrate/20150324001123_add_settings_for_shared_runners.rb create mode 100644 db/ci/migrate/20150324001227_migrate_shared_runners.rb create mode 100644 db/ci/migrate/20150330001111_disable_shared_runners.rb create mode 100644 db/ci/migrate/20150415142013_add_deleted_at_to_jobs.rb create mode 100644 db/ci/migrate/20150417000045_cleanup_the_build_model.rb create mode 100644 db/ci/migrate/20150504010150_migrate_url_to_path.rb create mode 100644 db/ci/migrate/20150504010250_rename_gitlab_url_to_path.rb create mode 100644 db/ci/migrate/20150508011360_add_info_fields_to_runner.rb create mode 100644 db/ci/migrate/20150528011001_add_fields_to_builds.rb create mode 100644 db/ci/migrate/20150528011012_move_job_name_to_build.rb create mode 100644 db/ci/migrate/20150529012113_add_tag_to_commits.rb create mode 100644 db/ci/migrate/20150601043220_add_yaml_to_projects.rb create mode 100644 db/ci/migrate/20150601043231_migrate_jobs_to_yaml.rb create mode 100644 db/ci/migrate/20150602000240_change_default_build_timeout.rb create mode 100644 db/ci/migrate/20150605002131_create_variables.rb create mode 100644 db/ci/migrate/20150616001155_add_errors_to_commit.rb create mode 100644 db/ci/migrate/20150630091815_add_options_to_build.rb create mode 100644 db/ci/migrate/20150703125244_add_encrypted_value_to_variables.rb create mode 100644 db/ci/migrate/20150703125325_encrypt_variables.rb create mode 100644 db/ci/migrate/20150707134456_add_allow_failure_to_builds.rb create mode 100644 db/ci/migrate/20150710113836_add_job_type_to_builds.rb create mode 100644 db/ci/migrate/20150710113851_migrate_deploy_to_job_type_for_builds.rb create mode 100644 db/ci/migrate/20150721204649_truncate_sessions.rb create mode 100644 db/ci/migrate/20150729145246_create_application_settings.rb create mode 100644 db/ci/migrate/20150803142346_rename_job_type_to_stage_builds.rb create mode 100644 db/ci/migrate/20150806091503_add_committed_at_to_commits.rb create mode 100644 db/ci/migrate/20150806091655_update_committed_at_with_created_at.rb create mode 100644 db/ci/migrate/20150806102222_create_trigger.rb create mode 100644 db/ci/migrate/20150806102457_add_trigger_to_builds.rb create mode 100644 db/ci/migrate/20150806105404_create_trigger_request.rb create mode 100644 db/ci/migrate/20150819162227_add_commit_id_to_trigger_requests.rb create mode 100644 db/ci/schema.rb create mode 100644 db/ci/seeds.rb create mode 100644 db/migrate/20150826001931_add_ci_tables.rb create mode 100644 doc/ci/README.md create mode 100644 doc/ci/api/README.md create mode 100644 doc/ci/api/builds.md create mode 100644 doc/ci/api/commits.md create mode 100644 doc/ci/api/forks.md create mode 100644 doc/ci/api/projects.md create mode 100644 doc/ci/api/runners.md create mode 100644 doc/ci/deployment/README.md create mode 100644 doc/ci/docker/README.md create mode 100644 doc/ci/docker/using_docker_build.md create mode 100644 doc/ci/docker/using_docker_images.md create mode 100644 doc/ci/examples/README.md create mode 100644 doc/ci/examples/test-and-deploy-python-application-to-heroku.md create mode 100644 doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md create mode 100644 doc/ci/examples/test-clojure-application.md create mode 100644 doc/ci/install/README.md create mode 100644 doc/ci/install/requirements.md create mode 100644 doc/ci/migration_to_omnibus/README.md create mode 100644 doc/ci/permissions/README.md create mode 100644 doc/ci/quick_start/README.md create mode 100644 doc/ci/quick_start/build_status.png create mode 100644 doc/ci/quick_start/commit_status.png create mode 100644 doc/ci/quick_start/new_commit.png create mode 100644 doc/ci/quick_start/projects.png create mode 100644 doc/ci/quick_start/runners.png create mode 100644 doc/ci/quick_start/runners_activated.png create mode 100644 doc/ci/raketasks/README.md create mode 100644 doc/ci/raketasks/backup_restore.md create mode 100644 doc/ci/runners/README.md create mode 100644 doc/ci/runners/project_specific.png create mode 100644 doc/ci/runners/shared_runner.png create mode 100644 doc/ci/runners/shared_to_specific_admin.png create mode 100644 doc/ci/update/3.0-to-3.1.md create mode 100644 doc/ci/update/3.1-to-3.2.md create mode 100644 doc/ci/update/3.2-to-4.0.md create mode 100644 doc/ci/update/4.0-to-4.1.md create mode 100644 doc/ci/update/4.1-to-4.2.md create mode 100644 doc/ci/update/4.2-to-4.3.md create mode 100644 doc/ci/update/4.3-to-5.0.md create mode 100644 doc/ci/update/5.0-to-5.1.md create mode 100644 doc/ci/update/5.1-to-5.2.md create mode 100644 doc/ci/update/5.2-to-5.3.md create mode 100644 doc/ci/update/5.3-to-5.4.md create mode 100644 doc/ci/update/5.4-to-7.8.md create mode 100644 doc/ci/update/7.10-to-7.11.md create mode 100644 doc/ci/update/7.11-to-7.12.md create mode 100644 doc/ci/update/7.12-to-7.13.md create mode 100644 doc/ci/update/7.8-to-7.9.md create mode 100644 doc/ci/update/7.9-to-7.10.md create mode 100644 doc/ci/update/README.md create mode 100644 doc/ci/update/patch_versions.md create mode 100644 doc/ci/variables/README.md create mode 100644 doc/ci/yaml/README.md create mode 100644 lib/ci/ansi2html.rb create mode 100644 lib/ci/api/api.rb create mode 100644 lib/ci/api/builds.rb create mode 100644 lib/ci/api/commits.rb create mode 100644 lib/ci/api/entities.rb create mode 100644 lib/ci/api/forks.rb create mode 100644 lib/ci/api/helpers.rb create mode 100644 lib/ci/api/projects.rb create mode 100644 lib/ci/api/runners.rb create mode 100644 lib/ci/api/triggers.rb create mode 100644 lib/ci/assets/.gitkeep create mode 100644 lib/ci/backup/builds.rb create mode 100644 lib/ci/backup/database.rb create mode 100644 lib/ci/backup/manager.rb create mode 100644 lib/ci/charts.rb create mode 100644 lib/ci/current_settings.rb create mode 100644 lib/ci/git.rb create mode 100644 lib/ci/gitlab_ci_yaml_processor.rb create mode 100644 lib/ci/model.rb create mode 100644 lib/ci/scheduler.rb create mode 100644 lib/ci/static_model.rb create mode 100644 lib/ci/version_info.rb create mode 100644 lib/tasks/ci/.gitkeep create mode 100644 lib/tasks/ci/backup.rake create mode 100644 lib/tasks/ci/cleanup.rake create mode 100644 lib/tasks/ci/schedule_builds.rake create mode 100644 lib/tasks/ci/setup.rake create mode 100644 lib/tasks/ci/sidekiq.rake create mode 100644 public/ci/build-canceled.svg create mode 100644 public/ci/build-failed.svg create mode 100644 public/ci/build-pending.svg create mode 100644 public/ci/build-running.svg create mode 100644 public/ci/build-success.svg create mode 100644 public/ci/build-unknown.svg create mode 100644 public/ci/favicon.ico create mode 100755 scripts/ci/prepare_build.sh create mode 100644 spec/ci/controllers/commits_controller_spec.rb create mode 100644 spec/ci/controllers/projects_controller_spec.rb create mode 100644 spec/ci/factories/builds.rb create mode 100644 spec/ci/factories/commits.rb create mode 100644 spec/ci/factories/events.rb create mode 100644 spec/ci/factories/projects.rb create mode 100644 spec/ci/factories/runner_projects.rb create mode 100644 spec/ci/factories/runners.rb create mode 100644 spec/ci/factories/trigger_requests.rb create mode 100644 spec/ci/factories/triggers.rb create mode 100644 spec/ci/factories/users.rb create mode 100644 spec/ci/factories/web_hook.rb create mode 100644 spec/ci/features/admin/builds_spec.rb create mode 100644 spec/ci/features/admin/events_spec.rb create mode 100644 spec/ci/features/admin/projects_spec.rb create mode 100644 spec/ci/features/admin/runners_spec.rb create mode 100644 spec/ci/features/builds_spec.rb create mode 100644 spec/ci/features/commits_spec.rb create mode 100644 spec/ci/features/events_spec.rb create mode 100644 spec/ci/features/lint_spec.rb create mode 100644 spec/ci/features/projects_spec.rb create mode 100644 spec/ci/features/runners_spec.rb create mode 100644 spec/ci/features/triggers_spec.rb create mode 100644 spec/ci/features/variables_spec.rb create mode 100644 spec/ci/helpers/application_helper_spec.rb create mode 100644 spec/ci/helpers/runners_helper_spec.rb create mode 100644 spec/ci/helpers/user_helper_spec.rb create mode 100644 spec/ci/helpers/user_sessions_helper_spec.rb create mode 100644 spec/ci/lib/ansi2html_spec.rb create mode 100644 spec/ci/lib/charts_spec.rb create mode 100644 spec/ci/lib/gitlab_ci_yaml_processor_spec.rb create mode 100644 spec/ci/lib/upgrader_spec.rb create mode 100644 spec/ci/mailers/notify_spec.rb create mode 100644 spec/ci/models/build_spec.rb create mode 100644 spec/ci/models/commit_spec.rb create mode 100644 spec/ci/models/mail_service_spec.rb create mode 100644 spec/ci/models/network_spec.rb create mode 100644 spec/ci/models/project_services/hip_chat_message_spec.rb create mode 100644 spec/ci/models/project_services/hip_chat_service_spec.rb create mode 100644 spec/ci/models/project_services/slack_message_spec.rb create mode 100644 spec/ci/models/project_services/slack_service_spec.rb create mode 100644 spec/ci/models/project_spec.rb create mode 100644 spec/ci/models/runner_project_spec.rb create mode 100644 spec/ci/models/runner_spec.rb create mode 100644 spec/ci/models/service_spec.rb create mode 100644 spec/ci/models/trigger_spec.rb create mode 100644 spec/ci/models/user_spec.rb create mode 100644 spec/ci/models/variable_spec.rb create mode 100644 spec/ci/models/web_hook_spec.rb create mode 100644 spec/ci/requests/api/builds_spec.rb create mode 100644 spec/ci/requests/api/commits_spec.rb create mode 100644 spec/ci/requests/api/forks_spec.rb create mode 100644 spec/ci/requests/api/projects_spec.rb create mode 100644 spec/ci/requests/api/runners_spec.rb create mode 100644 spec/ci/requests/api/triggers_spec.rb create mode 100644 spec/ci/requests/builds_spec.rb create mode 100644 spec/ci/requests/commits_spec.rb create mode 100644 spec/ci/services/create_commit_service_spec.rb create mode 100644 spec/ci/services/create_project_service_spec.rb create mode 100644 spec/ci/services/create_trigger_request_service_spec.rb create mode 100644 spec/ci/services/event_service_spec.rb create mode 100644 spec/ci/services/image_for_build_service_spec.rb create mode 100644 spec/ci/services/register_build_service_spec.rb create mode 100644 spec/ci/services/web_hook_service_spec.rb create mode 100644 spec/ci/six.tar.gz create mode 100644 spec/ci/spec_helper.rb create mode 100644 spec/ci/support/api_helpers.rb create mode 100644 spec/ci/support/db_cleaner.rb create mode 100644 spec/ci/support/gitlab_stubs/gitlab_ci.yml create mode 100644 spec/ci/support/gitlab_stubs/project_8.json create mode 100644 spec/ci/support/gitlab_stubs/project_8_hooks.json create mode 100644 spec/ci/support/gitlab_stubs/projects.json create mode 100644 spec/ci/support/gitlab_stubs/raw_project.yml create mode 100644 spec/ci/support/gitlab_stubs/session.json create mode 100644 spec/ci/support/gitlab_stubs/user.json create mode 100644 spec/ci/support/login_helpers.rb create mode 100644 spec/ci/support/monkey_patches/oauth2.rb create mode 100644 spec/ci/support/setup_builds_storage.rb create mode 100644 spec/ci/support/stub_gitlab_calls.rb create mode 100644 spec/ci/support/stub_gitlab_data.rb diff --git a/CHANGELOG-CI b/CHANGELOG-CI new file mode 100644 index 00000000000..d1ad661d88b --- /dev/null +++ b/CHANGELOG-CI @@ -0,0 +1,298 @@ +v7.14.0 (unreleased) + - Truncate commit messages after subject line in table + - Adjust CI config to support Docker executors + - Added Application Settings + - Randomize test database for CI tests + - Make YAML validation stricter + - Use avatars received from GitLab + - Refactor GitLab API usage to use either access_token or private_token depending on what was specified during login + - Allow to use access_token for API requests + - Fix project API listing returning empty list when first projects are not added to CI + - Allow to define variables from YAML + - Added support for CI skipped status + - Fix broken yaml error saving + - Add committed_at to commits to properly order last commit (the force push issue) + - Rename type(s) to stage(s) + - Fix navigation icons + - Add missing stage when doing retry + - Require variable keys to be not-empty and unique + - Fix variable saving issue + - Display variable saving errors in variables page not the project's + - Added Build Triggers API + +v7.13.1 + - Fix: user could steal specific runner + - Fix: don't send notifications for jobs with allow_failure set + - Fix invalid link to doc.gitlab.com + +v7.13.0 + - Fix inline edit runner-description + - Allow to specify image and services in yml that can be used with docker + - Fix: No runner notification can see managers only + - Fix service testing for slack + - Ability to cancel all builds in commit at once + - Disable colors in rake tasks automatically (if IO is not a TTY) + - Implemented "rake env:info". Rake task to receive system information + - Fix coverage calculation on commit page + - Enhance YAML validation + - Redirect back after authorization + - Change favicon + - Refactoring: Get rid of private_token usage in the frontend. + - Allow to specify allow_failure for job + - Build traces is stored in the file instead of database + - Make the builds path configurable + - Disable link to runner if it's not assigned to specific project + - Store all secrets in config/secrets.yml + - Encrypt variables + - Allow to specify flexible list of types in yaml + +v7.12.2 + - Revert: Runner without tag should pick builds without tag only + +v7.12.1 + - Runner without tag should pick builds without tag only + - Explicit error in the GitLab when commit not found. + - Fix: lint with relative subpath + - Update webhook example + - Improved Lint stability + - Add warning when .gitlab-ci.yml not found + - Improved validation for .gitlab-ci.yml + - Fix list of branches in only section + - Fix "Status Badge" button + +v7.12.0 + - Endless scroll on the dashboard + - Add notification if there are no runners + - Fix pagination on dashboard + - Remove ID column from runners list in the admin area + - Increase default timeout for builds to 60 minutes + - Using .gitlab-ci.yml file instead of jobs + - Link to the runner from the build page for admin user + - Ability to set secret variables for runner + - Dont retry build when push same commit in same ref twice + - Admin area: show amount of runners with last contact less than a minute ago + - Fix re-adding project with the same name but different gitlab_id + - Implementation of Lint (.gitlab-ci.yml validation tool) + - Updated rails to 4.1.11 + - API fix: project create call + - Link to web-editor with .gitlab-ci.yml + - Updated examples in the documentation + +v7.11.0 + - Deploy Jobs API calls + - Projects search on dashboard page + - Improved runners page + - Running and Pending tabs on admin builds page + - Fix [ci skip] tag, so you can skip CI triggering now + - Add HipChat notifications + - Clean up project advanced settings. + - Add a GitLab project path parameter to the project API + - Remove projects IDs from dashboard + - UI fix: Remove page headers from the admin area + - Improve Email templates + - Add backup/restore utility + - Coordinator stores information(version, platform, revision, etc.) about runners. + - Fixed pagination on dashboard + - Public accessible build and commit pages of public projects + - Fix vulnerability in the API when MySQL is used + +v7.10.1 + - Fix failing migration when update to 7.10 from 7.8 and older versions + +sidekiq_wirker_fix + - added sidekiq.yml + - integrated in script/background_jobs +v7.10.0 + - Projects sorting by last commit date + - Add project search at runner page + - Fix GitLab and CI projects collision + - Events for admin + - Events per projects + - Search for runners in admin area + - UI improvements: created separated admin section, removed useless project show page + - Runners sorting in admin area (by id) + - Remove protected_attributes gem + - Skip commit creation if there is no appropriate job + +v7.9.3 + - Contains no changes + - Developers can cancel and retry jobs + +v7.9.2 + - [Security] Already existing projects should not be served by shared runners + - Ability to run deploy job without test jobs (every push will trigger deploy job) + +v7.9.1 + - [Security] Adding explicit is_shared parameter to runner + - [Security] By default new projects are not served by shared runners + +v7.9.0 + - Reset user session if token is invalid + - Runner delete api endpoint + - Fix bug about showing edit button on commit page if user does not have permissions + - Allow to pass description and tag list during Runner's registration + - Added api for project jobs + - Implementation of deploy jobs after all parallel jobs(tests). + - Add scroll up/down buttons for better mobile experience with large build traces + - Add runner last contact (Kamil Trzciński) + - Allow to pause runners - when paused runner will not receive any new build (Kamil Trzciński) + - Add brakeman (security scanner for Ruby on Rails) + - Changed a color of the canceled builds + - Fix of show the same commits in different branches + +v7.8.2 + - Fix the broken build failed email + - Notify only pusher instead of commiter + +v7.8.0 + - Fix OAuth login with GitLab installed in relative URL + - GitLab CI has same version as GitLab since now + - Allow to pass description and tag list during Runner's registration (Kamil Trzciński) + - Update documentation (API, Install, Update) + - Skip refs field supports for wildcard branch name (ex. feature/*) + - Migrate E-mail notification to Services menu (Kamil Trzciński) + - Added Slack notifications (Kamil Trzciński) + - Disable turbolink on links pointing out to GitLab server + - Add test coverage parsing example for pytest-cov + - Upgrade raindrops gem + +v5.4.2 + - Fix exposure of project token via build data + +v5.4.1 + - Fix 500 if on builds page if build has no job + - Truncate project token from build trace + - Allow users with access to project see build trace + +v5.4.0 (Requires GitLab 7.7) + - Fixed 500 error for badge if build is pending + - Non-admin users can now register specific runners for their projects + - Project specific runners page which users can access + - Remove progress output from schedule_builds cron job + - Fix schedule_builds rake task + - Fix test webhook button + - Job can be branch specific or tag specific or both + - Shared runners builds projects which are not assigned to specific ones + - Job can be runner specific through tags + - Runner have tags + - Move job settings to separate page + - Add authorization level managing projects + - OAuth authentication via GitLab. + +v5.3 + - Remove annoying 'Done' message from schedule_builds cron job + - Fix a style issue with the navbar + - Skip CSRF check on the project's build page + - Fix showing wrong build script on admin projects page + - Add branch and commit message to build result emails + +v5.2 + - Improve performance by adding new indicies + - Separate Commit logic from Build logic in prep for Parallel Builds + - Parallel builds + - You can have multiple build scripts per project + +v5.1 + - Registration token and runner token are named differently + - Redirect to previous page after sign-in + - Dont show archived projects + - Add support for skip branches from build + - Add coverage parsing feature + - Update rails to 4.0.10 + - Look for a REVISION file before running `git log` + - All builds page for admin + +v5.0.1 + - Update rails to 4.0.5 + +v5.0.0 + - Set build timeout in minutes + - Web Hooks for builds + - Nprogress bar + - Remove extra spaces in build script + - Requires runner v5 + * All script commands executed as one file + * Cancel button works correctly now + * Runner stability increased + * Timeout applies to build now instead of line of script + +v4.3.0 + - Refactor build js + - Redirect to build page with sha + bid if build id is not provided + - Update rails to 4.0.3 + - Restyle project settings page + - Improve help page + - Replaced puma with unicorn + - Improved init.d script + - Add submodule init to default build script for new projects + +v4.2.0 + - Build duration chart + - Bootstrap 3 with responsive UI + - Improved init.d script + - Refactoring + - Changed http codes for POST /projects/:id/build action + - Turbolinks + +v4.1.0 + - Rails 4 + - Click on build branch to see other builds for this branch + - Email notifications (Jeroen Knoops) + +v4.0.0 + - Shared runners (no need to add runner to every project) + - Admin area (only available for GitLab admins) + - Hide all runners management into admin area + - Use http cloning for builds instead of deploy keys + - Allow choose between git clone and git fetch when get code for build + - Make build timeout actually works + - Requires GitLab 6.3 or higher + - GitLab CI settings go to GitLab project via api on creation + +v3.2.0 + - Limit visibility of projects by gitlab authorized projects + - Use one page for both gitlab and gitlab-ci projects + +v3.1.0 + - Login with both username, email or LDAP credentials (if GitLab 6.0+) + - Retry build button functionality + - UI fixes for resolution 1366px and lower + - Fix gravatar ssl warning + +v3.0.0 + - Build running functionality extracted in gitlab-ci-runner + - Added API for runners and builds + - Redesigned application + - Added charts + - Use GitLab auth + - Add projects via UI with few clicks + +v2.2.0 + - replaced unicorn with puma + - replaced grit with rugged + - Runner.rb more transactional safe now + - updated rails to 3.2.13 + - updated devise to 2.2 + - fixed issue when build left in running status if exception triggered + - rescue build timeout correctly + - badge helper with markdown & html + - increased test coverage to 85% + +v2.1.0 + - Removed horizontal scroll for build trace + - new status badges + - better encode + - added several CI_* env variables + +v2.0.0 + - Replace resque with sidekiq + - Run only one build at time per project + - Added whenever for schedule jobs + +v1.2.0 + - Added Github web hook support + - Added build schedule + +v1.1.0 + - Added JSON response for builds status + - Compatible with GitLab v4.0.0 \ No newline at end of file diff --git a/Gemfile b/Gemfile index 3aa3c72e088..6933fc92ddc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,14 @@ source "https://rubygems.org" -gem 'rails', '4.1.11' +def darwin_only(require_as) + RUBY_PLATFORM.include?('darwin') && require_as +end + +def linux_only(require_as) + RUBY_PLATFORM.include?('linux') && require_as +end + +gem 'rails', '4.1.12' # Specify a sprockets version due to security issue # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY @@ -10,28 +18,28 @@ gem 'sprockets', '~> 2.12.3' gem "default_value_for", "~> 3.0.0" # Supported DBs -gem "mysql2", group: :mysql -gem "pg", group: :postgres +gem "mysql2", '~> 0.3.16', group: :mysql +gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries -gem "devise", '3.2.4' -gem "devise-async", '0.9.0' +gem "devise", '~> 3.2.4' +gem "devise-async", '~> 0.9.0' gem 'omniauth', "~> 1.2.2" -gem 'omniauth-google-oauth2' -gem 'omniauth-twitter' -gem 'omniauth-github' -gem 'omniauth-shibboleth' -gem 'omniauth-kerberos', group: :kerberos -gem 'omniauth-gitlab' -gem 'omniauth-bitbucket' +gem 'omniauth-google-oauth2', '~> 0.2.5' +gem 'omniauth-twitter', '~> 1.0.1' +gem 'omniauth-github', '~> 1.1.1' +gem 'omniauth-shibboleth', '~> 1.1.1' +gem 'omniauth-kerberos', '~> 0.2.0', group: :kerberos +gem 'omniauth-gitlab', '~> 1.0.0' +gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-saml', '~> 1.4.0' -gem 'doorkeeper', '2.1.3' +gem 'doorkeeper', '~> 2.1.3' gem "rack-oauth2", "~> 1.0.5" # Two-factor authentication -gem 'devise-two-factor' -gem 'rqrcode-rails3' -gem 'attr_encrypted', '1.3.4' +gem 'devise-two-factor', '~> 1.0.1' +gem 'rqrcode-rails3', '~> 0.1.7' +gem 'attr_encrypted', '~> 1.3.4' # Browser detection gem "browser", '~> 0.8.0' @@ -48,7 +56,7 @@ gem 'gitlab-grack', '~> 2.0.2', require: 'grack' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master -gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap" +gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap" # Git Wiki gem 'gollum-lib', '~> 4.0.2' @@ -63,47 +71,47 @@ gem "gitlab-linguist", "~> 3.0.1", require: "linguist" # API gem "grape", "~> 0.6.1" gem "grape-entity", "~> 0.4.2" -gem 'rack-cors', require: 'rack/cors' +gem 'rack-cors', '~> 0.2.9', require: 'rack/cors' # Format dates and times # based on human-friendly examples -gem "stamp" +gem "stamp", '~> 0.5.0' # Enumeration fields -gem 'enumerize' +gem 'enumerize', '~> 0.7.0' # Pagination gem "kaminari", "~> 0.15.1" # HAML -gem "haml-rails" +gem "haml-rails", '~> 0.5.3' # Files attachments -gem "carrierwave" +gem "carrierwave", '~> 0.9.0' # Drag and Drop UI -gem 'dropzonejs-rails' +gem 'dropzonejs-rails', '~> 0.7.1' # for aws storage gem "fog", "~> 1.25.0" -gem "unf" +gem "unf", '~> 0.1.4' # Authorization -gem "six" +gem "six", '~> 0.2.0' # Seed data -gem "seed-fu" +gem "seed-fu", '~> 2.3.5' # Markdown and HTML processing gem 'html-pipeline', '~> 1.11.0' -gem 'task_list', '1.0.2', require: 'task_list/railtie' -gem 'github-markup' +gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' +gem 'github-markup', '~> 1.3.1' gem 'redcarpet', '~> 3.3.2' -gem 'RedCloth' +gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' -gem 'org-ruby', '= 0.9.12' +gem 'org-ruby', '~> 0.9.12' gem 'creole', '~>0.3.6' -gem 'wikicloth', '=0.8.1' +gem 'wikicloth', '~> 0.8.1' gem 'asciidoctor', '~> 1.5.2' # Diffs @@ -111,37 +119,37 @@ gem 'diffy', '~> 3.0.3' # Application server group :unicorn do - gem "unicorn", '~> 4.6.3' - gem 'unicorn-worker-killer' + gem "unicorn", '~> 4.8.2' + gem 'unicorn-worker-killer', '~> 0.4.2' end # State machine -gem "state_machine" +gem "state_machine", '~> 1.2.0' # Issue tags gem 'acts-as-taggable-on', '~> 3.4' # Background jobs -gem 'slim' -gem 'sinatra', require: nil +gem 'slim', '~> 2.0.2' +gem 'sinatra', '~> 1.4.4', require: nil gem 'sidekiq', '~> 3.3' -gem 'sidetiq', '0.6.3' +gem 'sidetiq', '~> 0.6.3' # HTTP requests -gem "httparty" +gem "httparty", '~> 0.13.3' # Colored output to console -gem "colored" +gem "colored", '~> 1.2' # GitLab settings -gem 'settingslogic' +gem 'settingslogic', '~> 2.0.9' # Misc -gem "foreman" -gem 'version_sorter' + +gem 'version_sorter', '~> 2.0.0' # Cache -gem "redis-rails" +gem "redis-rails", '~> 4.0.0' # Campfire integration gem 'tinder', '~> 1.9.2' @@ -177,69 +185,70 @@ gem "sanitize", '~> 2.0' gem "rack-attack", '~> 4.3.0' # Ace editor -gem 'ace-rails-ap' +gem 'ace-rails-ap', '~> 2.0.1' # Keyboard shortcuts -gem 'mousetrap-rails' +gem 'mousetrap-rails', '~> 1.4.6' # Detect and convert string character encoding -gem 'charlock_holmes' +gem 'charlock_holmes', '~> 0.6.9.4' gem "sass-rails", '~> 4.0.5' -gem "coffee-rails" -gem "uglifier" +gem "coffee-rails", '~> 4.1.0' +gem "uglifier", '~> 2.3.2' gem 'turbolinks', '~> 2.5.0' -gem 'jquery-turbolinks' +gem 'jquery-turbolinks', '~> 2.0.1' -gem 'addressable' +gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' gem 'font-awesome-rails', '~> 4.2' gem 'gitlab_emoji', '~> 0.1' gem 'gon', '~> 5.0.0' gem 'jquery-atwho-rails', '~> 1.0.0' -gem 'jquery-rails', '3.1.3' -gem 'jquery-scrollto-rails' -gem 'jquery-ui-rails' -gem 'nprogress-rails' +gem 'jquery-rails', '~> 3.1.3' +gem 'jquery-scrollto-rails', '~> 1.4.3' +gem 'jquery-ui-rails', '~> 4.2.1' +gem 'nprogress-rails', '~> 0.1.2.3' gem 'raphael-rails', '~> 2.1.2' -gem 'request_store' +gem 'request_store', '~> 1.2.0' gem 'select2-rails', '~> 3.5.9' -gem 'virtus' +gem 'virtus', '~> 1.0.1' group :development do + gem "foreman" gem 'brakeman', require: false - gem "annotate", "~> 2.6.0.beta2" - gem "letter_opener" - gem 'quiet_assets', '~> 1.0.1' - gem 'rack-mini-profiler', require: false + + gem "annotate", "~> 2.6.0" + gem "letter_opener", '~> 1.1.2' + gem 'quiet_assets', '~> 1.0.2' + gem 'rack-mini-profiler', '~> 0.9.0', require: false gem 'rerun', '~> 0.10.0' # Better errors handler - gem 'better_errors' - gem 'binding_of_caller' + gem 'better_errors', '~> 1.0.1' + gem 'binding_of_caller', '~> 0.7.2' # Docs generator - gem "sdoc" + gem "sdoc", '~> 0.3.20' # thin instead webrick - gem 'thin' + gem 'thin', '~> 1.6.1' end group :development, :test do - gem 'awesome_print' gem 'byebug', platform: :mri - gem 'fuubar', '~> 2.0.0' gem 'pry-rails' - gem 'coveralls', '~> 0.8.2', require: false + gem 'awesome_print', '~> 1.2.0' + gem 'fuubar', '~> 2.0.0' + gem 'database_cleaner', '~> 1.4.0' - gem 'factory_girl_rails' + gem 'factory_girl_rails', '~> 4.3.0' gem 'rspec-rails', '~> 3.3.0' - gem 'rubocop', '0.28.0', require: false - gem 'spinach-rails' + gem 'spinach-rails', '~> 0.2.1' # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) - gem 'minitest', '~> 5.3.0' + gem 'minitest', '~> 5.7.0' # Generate Fake data gem 'ffaker', '~> 2.0.0' @@ -249,30 +258,57 @@ group :development, :test do gem 'poltergeist', '~> 1.6.0' gem 'teaspoon', '~> 1.0.0' - gem 'teaspoon-jasmine' + gem 'teaspoon-jasmine', '~> 2.2.0' - gem 'spring', '~> 1.3.1' - gem 'spring-commands-rspec', '~> 1.0.0' + gem 'spring', '~> 1.3.6' + gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-teaspoon', '~> 0.0.2' + + gem 'rubocop', '~> 0.28.0', require: false + gem 'coveralls', '~> 0.8.2', require: false + gem 'simplecov', '~> 0.10.0', require: false end group :test do - gem 'simplecov', require: false gem 'shoulda-matchers', '~> 2.8.0', require: false gem 'email_spec', '~> 1.6.0' gem 'webmock', '~> 1.21.0' - gem 'test_after_commit' + gem 'test_after_commit', '~> 0.2.2' end group :production do gem "gitlab_meta", '7.0' end -gem "newrelic_rpm" +gem "newrelic_rpm", '~> 3.9.4.245' -gem 'octokit', '3.7.0' +gem 'octokit', '~> 3.7.0' gem "mail_room", "~> 0.4.0" -gem 'email_reply_parser' +gem 'email_reply_parser', '~> 0.5.8' + +## CI +gem 'activerecord-deprecated_finders', '~> 1.0.3' +gem 'activerecord-session_store', '~> 0.1.0' +gem "nested_form", '~> 0.3.2' + +# Scheduled +gem 'whenever', '~> 0.8.4', require: false + +# OAuth +gem 'oauth2', '~> 1.0.0' + +gem 'gitlab_ci_meta', '~> 4.0' + +# Soft deletion +gem "paranoia", "~> 2.0" + +group :development, :test do + gem 'guard-rspec', '~> 4.2.0' + + gem 'rb-fsevent', require: darwin_only('rb-fsevent') + gem 'growl', require: darwin_only('growl') + gem 'rb-inotify', require: linux_only('rb-inotify') +end diff --git a/Gemfile.lock b/Gemfile.lock index 5278fe243a8..b810f2fdd4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,31 +4,36 @@ GEM CFPropertyList (2.3.1) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.11) - actionpack (= 4.1.11) - actionview (= 4.1.11) + actionmailer (4.1.12) + actionpack (= 4.1.12) + actionview (= 4.1.12) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.11) - actionview (= 4.1.11) - activesupport (= 4.1.11) + actionpack (4.1.12) + actionview (= 4.1.12) + activesupport (= 4.1.12) rack (~> 1.5.2) rack-test (~> 0.6.2) - actionview (4.1.11) - activesupport (= 4.1.11) + actionview (4.1.12) + activesupport (= 4.1.12) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.11) - activesupport (= 4.1.11) + activemodel (4.1.12) + activesupport (= 4.1.12) builder (~> 3.1) - activerecord (4.1.11) - activemodel (= 4.1.11) - activesupport (= 4.1.11) + activerecord (4.1.12) + activemodel (= 4.1.12) + activesupport (= 4.1.12) arel (~> 5.0.0) + activerecord-deprecated_finders (1.0.4) + activerecord-session_store (0.1.1) + actionpack (>= 4.0.0, < 5) + activerecord (>= 4.0.0, < 5) + railties (>= 4.0.0, < 5) activeresource (4.0.0) activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.11) + activesupport (4.1.12) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -37,50 +42,49 @@ GEM acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) addressable (2.3.8) - annotate (2.6.0) - activerecord (>= 2.3.0) - rake (>= 0.8.7) + annotate (2.6.10) + activerecord (>= 3.2, <= 4.3) + rake (~> 10.4) arel (5.0.1.20140414130214) asana (0.0.6) activeresource (>= 3.2.3) asciidoctor (1.5.2) - ast (2.0.0) - astrolabe (1.3.0) - parser (>= 2.2.0.pre.3, < 3.0) + ast (2.1.0) + astrolabe (1.3.1) + parser (~> 2.2) attr_encrypted (1.3.4) encryptor (>= 1.3.0) attr_required (1.0.0) - autoprefixer-rails (5.1.11) + autoprefixer-rails (5.2.1.2) execjs json awesome_print (1.2.0) - axiom-types (0.0.5) - descendants_tracker (~> 0.0.1) - ice_nine (~> 0.9) - bcrypt (3.1.7) + axiom-types (0.1.1) + descendants_tracker (~> 0.0.4) + ice_nine (~> 0.11.0) + thread_safe (~> 0.3, >= 0.3.1) + bcrypt (3.1.10) better_errors (1.0.1) coderay (>= 1.0.0) erubis (>= 2.6.6) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bootstrap-sass (3.3.4.1) + bootstrap-sass (3.3.5) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.2.19) - brakeman (3.0.1) + brakeman (3.0.5) erubis (~> 2.6) fastercsv (~> 1.5) haml (>= 3.0, < 5.0) highline (~> 1.6.20) multi_json (~> 1.2) ruby2ruby (~> 2.1.1) - ruby_parser (~> 3.5.0) + ruby_parser (~> 3.7.0) sass (~> 3.0) terminal-table (~> 1.4) browser (0.8.0) builder (3.2.2) - byebug (3.2.0) - columnize (~> 0.8) - debugger-linecache (~> 1.2) + byebug (6.0.2) cal-heatmap-rails (0.0.1) capybara (2.4.4) mime-types (>= 1.16) @@ -88,7 +92,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-screenshot (1.0.9) + capybara-screenshot (1.0.11) capybara (>= 1.0, < 3) launchy carrierwave (0.9.0) @@ -98,6 +102,8 @@ GEM celluloid (0.16.0) timers (~> 4.0.0) charlock_holmes (0.6.9.4) + chronic (0.10.2) + chunky_png (1.3.4) cliver (0.3.2) coderay (1.1.0) coercible (1.0.0) @@ -110,9 +116,8 @@ GEM execjs coffee-script-source (1.9.1.1) colored (1.2) - colorize (0.5.8) - columnize (0.9.0) - connection_pool (2.1.0) + colorize (0.7.7) + connection_pool (2.2.0) coveralls (0.8.2) json (~> 1.8) rest-client (>= 1.6.8, < 2) @@ -122,15 +127,15 @@ GEM crack (0.4.2) safe_yaml (~> 1.0.0) creole (0.3.8) - d3_rails (3.5.5) + d3_rails (3.5.6) railties (>= 3.1.0) - daemons (1.1.9) + daemons (1.2.3) database_cleaner (1.4.1) debug_inspector (0.0.2) - debugger-linecache (1.2.0) - default_value_for (3.0.0) + default_value_for (3.0.1) activerecord (>= 3.2.0, < 5.0) - descendants_tracker (0.0.3) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) devise (3.2.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -139,21 +144,20 @@ GEM warden (~> 1.2.3) devise-async (0.9.0) devise (~> 3.2) - devise-two-factor (1.0.1) + devise-two-factor (1.0.2) activemodel activesupport attr_encrypted (~> 1.3.2) - devise (~> 3.2.4) - rails - rotp (~> 1.6.1) + devise (>= 3.2.4, < 3.5) + railties + rotp (< 2) diff-lcs (1.2.5) - diffy (3.0.3) + diffy (3.0.7) docile (1.1.5) domain_name (0.5.24) unf (>= 0.0.5, < 1.0.0) - doorkeeper (2.1.3) + doorkeeper (2.1.4) railties (>= 3.2) - dotenv (0.9.0) dropzonejs-rails (0.7.1) rails (> 3.1) email_reply_parser (0.5.8) @@ -163,25 +167,25 @@ GEM encryptor (1.3.0) enumerize (0.7.0) activesupport (>= 3.2) - equalizer (0.0.8) + equalizer (0.0.11) erubis (2.7.0) escape_utils (0.2.4) - eventmachine (1.0.4) - excon (0.45.3) - execjs (2.5.2) + eventmachine (1.0.8) + excon (0.45.4) + execjs (2.6.0) expression_parser (0.9.0) factory_girl (4.3.0) activesupport (>= 3.0.0) factory_girl_rails (4.3.0) factory_girl (~> 4.3.0) railties (>= 3.0.0) - faraday (0.8.9) + faraday (0.8.10) multipart-post (~> 1.2.0) - faraday_middleware (0.9.0) - faraday (>= 0.7.4, < 0.9) + faraday_middleware (0.10.0) + faraday (>= 0.7.4, < 0.10) fastercsv (1.5.5) ffaker (2.0.0) - ffi (1.9.8) + ffi (1.9.10) fission (0.5.0) CFPropertyList (~> 2.2) flowdock (0.7.0) @@ -202,11 +206,11 @@ GEM ipaddress (~> 0.5) nokogiri (~> 1.5, >= 1.5.11) opennebula - fog-brightbox (0.7.1) + fog-brightbox (0.9.0) fog-core (~> 1.22) fog-json inflecto (~> 0.0.2) - fog-core (1.30.0) + fog-core (1.32.1) builder excon (~> 0.45) formatador (~> 0.2) @@ -216,7 +220,7 @@ GEM fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) - fog-profitbricks (0.0.3) + fog-profitbricks (0.0.5) fog-core fog-xml nokogiri @@ -227,7 +231,7 @@ GEM fog-sakuracloud (1.0.1) fog-core fog-json - fog-softlayer (0.4.6) + fog-softlayer (0.4.7) fog-core fog-json fog-terremark (0.1.0) @@ -242,30 +246,28 @@ GEM fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) - font-awesome-rails (4.2.0.0) + font-awesome-rails (4.4.0.0) railties (>= 3.2, < 5.0) - foreman (0.63.0) - dotenv (>= 0.7) - thor (>= 0.13.6) + foreman (0.78.0) + thor (~> 0.19.1) formatador (0.2.5) fuubar (2.0.0) rspec (~> 3.0) ruby-progressbar (~> 1.4) gemnasium-gitlab-service (0.2.6) rugged (~> 0.21) - gemojione (2.0.0) + gemojione (2.0.1) json - gherkin-ruby (0.3.1) - racc - github-markup (1.3.1) - posix-spawn (~> 0.3.8) + get_process_mem (0.2.0) + gherkin-ruby (0.3.2) + github-markup (1.3.3) gitlab-flowdock-git-hook (1.0.1) flowdock (~> 0.7) gitlab-grit (>= 2.4.1) multi_json gitlab-grack (2.0.2) rack (~> 1.5.1) - gitlab-grit (2.7.2) + gitlab-grit (2.7.3) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) @@ -274,6 +276,7 @@ GEM charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) + gitlab_ci_meta (4.0) gitlab_emoji (0.1.0) gemojione (~> 2.0) gitlab_git (7.2.14) @@ -287,16 +290,16 @@ GEM omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.3) - gollum-grit_adapter (0.1.3) + gollum-grit_adapter (1.0.0) gitlab-grit (~> 2.7, >= 2.7.1) - gollum-lib (4.0.2) - github-markup (~> 1.3.1) - gollum-grit_adapter (~> 0.1, >= 0.1.1) + gollum-lib (4.0.3) + github-markup (~> 1.3.3) + gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.9) + rouge (~> 1.7.4) sanitize (~> 2.1.0) stringex (~> 2.5.1) - gon (5.0.1) + gon (5.0.4) actionpack (>= 2.3.0) json grape (0.6.1) @@ -309,9 +312,22 @@ GEM rack-accept rack-mount virtus (>= 1.0.0) - grape-entity (0.4.2) + grape-entity (0.4.8) activesupport multi_json (>= 1.3.2) + growl (1.0.3) + guard (2.13.0) + formatador (>= 0.2.4) + listen (>= 2.7, <= 4.0) + lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-rspec (4.2.10) + guard (~> 2.1) + rspec (>= 2.14, < 4.0) haml (4.0.7) tilt haml-rails (0.5.3) @@ -322,24 +338,24 @@ GEM hashie (2.1.2) highline (1.6.21) hike (1.2.3) - hipchat (1.5.0) + hipchat (1.5.2) httparty mimemagic hitimes (1.2.2) html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) + htmlentities (4.3.4) http-cookie (1.0.2) domain_name (~> 0.5) http_parser.rb (0.5.3) - httparty (0.13.3) + httparty (0.13.5) json (~> 1.8) multi_xml (>= 0.5.2) - httpauth (0.2.1) - httpclient (2.5.3.3) + httpclient (2.6.0.1) i18n (0.7.0) ice_cube (0.11.1) - ice_nine (0.10.0) + ice_nine (0.11.1) inflecto (0.0.2) ipaddress (0.8.0) jquery-atwho-rails (1.0.1) @@ -348,26 +364,26 @@ GEM thor (>= 0.14, < 2.0) jquery-scrollto-rails (1.4.3) railties (> 3.1, < 5.0) - jquery-turbolinks (2.0.1) + jquery-turbolinks (2.0.2) railties (>= 3.1.0) turbolinks jquery-ui-rails (4.2.1) railties (>= 3.2.16) json (1.8.3) - jwt (0.1.13) - multi_json (>= 1.5) + jwt (1.5.1) kaminari (0.15.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.9.2) + kgio (2.9.3) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.1.2) launchy (~> 2.2) - listen (2.10.0) + listen (2.10.1) celluloid (~> 0.16.0) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) + lumberjack (1.0.9) macaddr (1.7.1) systemu (~> 2.6.2) mail (2.6.3) @@ -377,12 +393,14 @@ GEM mime-types (1.25.1) mimemagic (0.3.0) mini_portile (0.6.2) - minitest (5.3.5) + minitest (5.7.0) mousetrap-rails (1.4.6) multi_json (1.11.2) multi_xml (0.5.5) multipart-post (1.2.0) - mysql2 (0.3.16) + mysql2 (0.3.20) + nenv (0.2.0) + nested_form (0.3.2) net-ldap (0.11) net-scp (1.2.1) net-ssh (>= 2.6.5) @@ -391,15 +409,18 @@ GEM newrelic_rpm (3.9.4.245) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) + notiffany (0.0.7) + nenv (~> 0.1) + shellany (~> 0.0) nprogress-rails (0.1.2.3) oauth (0.4.7) - oauth2 (0.8.1) - faraday (~> 0.8) - httpauth (~> 0.1) - jwt (~> 0.1.4) - multi_json (~> 1.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) rack (~> 1.2) - octokit (3.7.0) + octokit (3.7.1) sawyer (~> 0.6.0, >= 0.5.3) omniauth (1.2.2) hashie (>= 1.2, < 4) @@ -408,30 +429,30 @@ GEM multi_json (~> 1.7) omniauth (~> 1.1) omniauth-oauth (~> 1.0) - omniauth-github (1.1.1) + omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) omniauth-gitlab (1.0.0) omniauth (~> 1.0) omniauth-oauth2 (~> 1.0) - omniauth-google-oauth2 (0.2.5) + omniauth-google-oauth2 (0.2.6) omniauth (> 1.0) omniauth-oauth2 (~> 1.1) omniauth-kerberos (0.2.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) - omniauth-multipassword (0.4.1) + omniauth-multipassword (0.4.2) omniauth (~> 1.0) - omniauth-oauth (1.0.1) + omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.1.1) - oauth2 (~> 0.8.0) - omniauth (~> 1.0) + omniauth-oauth2 (1.3.1) + oauth2 (~> 1.0) + omniauth (~> 1.2) omniauth-saml (1.4.1) omniauth (~> 1.1) ruby-saml (~> 1.0.0) - omniauth-shibboleth (1.1.1) + omniauth-shibboleth (1.1.2) omniauth (>= 1.0.0) omniauth-twitter (1.0.1) multi_json (~> 1.3) @@ -443,7 +464,9 @@ GEM org-ruby (0.9.12) rubypants (~> 0.2) orm_adapter (0.5.0) - parser (2.2.0.2) + paranoia (2.1.3) + activerecord (~> 4.0) + parser (2.2.2.6) ast (>= 1.1, < 3.0) pg (0.18.2) poltergeist (1.6.0) @@ -451,60 +474,59 @@ GEM cliver (~> 0.3.1) multi_json (~> 1.0) websocket-driver (>= 0.2.0) - posix-spawn (0.3.9) + posix-spawn (0.3.11) powerpack (0.0.9) - pry (0.9.12.4) - coderay (~> 1.0) - method_source (~> 0.8) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - pry-rails (0.3.2) + pry-rails (0.3.4) pry (>= 0.9.10) pyu-ruby-sasl (0.0.3.3) - quiet_assets (1.0.2) + quiet_assets (1.0.3) railties (>= 3.1, < 5.0) - racc (1.4.10) rack (1.5.5) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.3.0) rack rack-cors (0.2.9) - rack-mini-profiler (0.9.0) + rack-mini-profiler (0.9.7) rack (>= 1.1.3) rack-mount (0.8.3) rack (>= 1.0.0) - rack-oauth2 (1.0.8) + rack-oauth2 (1.0.10) activesupport (>= 2.3) attr_required (>= 0.0.5) - httpclient (>= 2.2.0.2) + httpclient (>= 2.4) multi_json (>= 1.3.6) rack (>= 1.1) - rack-protection (1.5.1) + rack-protection (1.5.3) rack rack-test (0.6.3) rack (>= 1.0) - rails (4.1.11) - actionmailer (= 4.1.11) - actionpack (= 4.1.11) - actionview (= 4.1.11) - activemodel (= 4.1.11) - activerecord (= 4.1.11) - activesupport (= 4.1.11) + rails (4.1.12) + actionmailer (= 4.1.12) + actionpack (= 4.1.12) + actionview (= 4.1.12) + activemodel (= 4.1.12) + activerecord (= 4.1.12) + activesupport (= 4.1.12) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.11) + railties (= 4.1.12) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.11) - actionpack (= 4.1.11) - activesupport (= 4.1.11) + railties (4.1.12) + actionpack (= 4.1.12) + activesupport (= 4.1.12) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) - raindrops (0.13.0) + raindrops (0.15.0) rake (10.4.2) raphael-rails (2.1.2) - rb-fsevent (0.9.4) + rb-fsevent (0.9.5) rb-inotify (0.9.5) ffi (>= 0.5.0) rbvmomi (1.8.2) @@ -519,10 +541,10 @@ GEM actionpack (~> 4) redis-rack (~> 1.5.0) redis-store (~> 1.1.0) - redis-activesupport (4.0.0) + redis-activesupport (4.1.1) activesupport (~> 4) redis-store (~> 1.1.0) - redis-namespace (1.5.1) + redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) redis-rack (1.5.0) rack (~> 1.5) @@ -533,32 +555,32 @@ GEM redis-store (~> 1.1.0) redis-store (1.1.6) redis (>= 2.2) - request_store (1.0.5) + request_store (1.2.0) rerun (0.10.0) listen (~> 2.7, >= 2.7.3) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) - rinku (1.7.3) rotp (1.6.1) - rouge (1.9.1) - rqrcode (0.4.2) + rouge (1.7.7) + rqrcode (0.7.0) + chunky_png rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) rspec (3.3.0) rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) rspec-mocks (~> 3.3.0) - rspec-core (3.3.1) + rspec-core (3.3.2) rspec-support (~> 3.3.0) - rspec-expectations (3.3.0) + rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) - rspec-mocks (3.3.0) + rspec-mocks (3.3.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) - rspec-rails (3.3.2) + rspec-rails (3.3.3) actionpack (>= 3.0, < 4.3) activesupport (>= 3.0, < 4.3) railties (>= 3.0, < 4.3) @@ -573,16 +595,16 @@ GEM powerpack (~> 0.0.6) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) - ruby-progressbar (1.7.1) + ruby-progressbar (1.7.5) ruby-saml (1.0.0) nokogiri (>= 1.5.10) uuid (~> 2.3) - ruby2ruby (2.1.3) + ruby2ruby (2.1.4) ruby_parser (~> 3.1) sexp_processor (~> 4.0) - ruby_parser (3.5.0) + ruby_parser (3.7.1) sexp_processor (~> 4.1) - rubyntlm (0.5.0) + rubyntlm (0.5.2) rubypants (0.2.0) rugged (0.22.2) safe_yaml (1.0.4) @@ -606,15 +628,16 @@ GEM select2-rails (3.5.9.3) thor (~> 0.14) settingslogic (2.0.9) - sexp_processor (4.4.5) + sexp_processor (4.6.0) + shellany (0.0.1) shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (3.3.0) - celluloid (>= 0.16.0) - connection_pool (>= 2.0.0) - json - redis (>= 3.0.6) - redis-namespace (>= 1.3.1) + sidekiq (3.4.2) + celluloid (~> 0.16.0) + connection_pool (~> 2.2, >= 2.2.0) + json (~> 1.0) + redis (~> 3.2, >= 3.2.1) + redis-namespace (~> 1.5, >= 1.5.2) sidetiq (0.6.3) celluloid (>= 0.14.1) ice_cube (= 0.11.1) @@ -625,19 +648,20 @@ GEM json (~> 1.8) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - sinatra (1.4.4) + sinatra (1.4.6) rack (~> 1.4) rack-protection (~> 1.4) - tilt (~> 1.3, >= 1.3.4) + tilt (>= 1.3, < 3) six (0.2.0) slack-notifier (1.0.0) - slim (2.0.2) + slim (2.0.3) temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) slop (3.6.0) - spinach (0.8.7) - colorize (= 0.5.8) - gherkin-ruby (>= 0.3.1) + spinach (0.8.10) + colorize + gherkin-ruby (>= 0.3.2) + json spinach-rails (0.2.1) capybara (>= 2.0.0) railties (>= 3) @@ -668,31 +692,32 @@ GEM railties (>= 3.2.5, < 5) teaspoon-jasmine (2.2.0) teaspoon (>= 1.0.0) - temple (0.6.7) + temple (0.6.10) term-ansicolor (1.3.2) tins (~> 1.0) - terminal-table (1.4.5) - test_after_commit (0.2.2) - thin (1.6.1) - daemons (>= 1.0.9) - eventmachine (>= 1.0.0) - rack (>= 1.0.0) + terminal-table (1.5.2) + test_after_commit (0.2.7) + activerecord (>= 3.2) + thin (1.6.3) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0) + rack (~> 1.0) thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) timers (4.0.1) hitimes timfel-krb5-auth (0.8.3) - tinder (1.9.3) + tinder (1.9.4) eventmachine (~> 1.0) - faraday (~> 0.8) + faraday (~> 0.8.9) faraday_middleware (~> 0.9) hashie (>= 1.0, < 3) json (~> 1.8.0) mime-types (~> 1.19) multi_json (~> 1.7) twitter-stream (~> 0.1) - tins (1.5.4) + tins (1.6.0) trollop (2.1.2) turbolinks (2.5.3) coffee-rails @@ -700,41 +725,49 @@ GEM eventmachine (>= 0.12.8) http_parser.rb (~> 0.5.1) simple_oauth (~> 0.1.4) + twitter-text (1.12.0) + unf (~> 0.1.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.3.2) + uglifier (2.3.3) execjs (>= 0.3.0) json (>= 1.8.0) underscore-rails (1.4.4) unf (0.1.4) unf_ext unf_ext (0.0.7.1) - unicorn (4.6.3) + unicorn (4.8.3) kgio (~> 2.6) rack raindrops (~> 0.7) - unicorn-worker-killer (0.4.2) + unicorn-worker-killer (0.4.3) + get_process_mem (~> 0) unicorn (~> 4) uuid (2.3.8) macaddr (~> 1.0) version_sorter (2.0.0) - virtus (1.0.1) - axiom-types (~> 0.0.5) + virtus (1.0.5) + axiom-types (~> 0.1) coercible (~> 1.0) - descendants_tracker (~> 0.0.1) - equalizer (~> 0.0.7) + descendants_tracker (~> 0.0, >= 0.0.3) + equalizer (~> 0.0, >= 0.0.9) warden (1.2.3) rack (>= 1.0) webmock (1.21.0) addressable (>= 2.3.6) crack (>= 0.3.2) - websocket-driver (0.5.4) + websocket-driver (0.6.2) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) - wikicloth (0.8.1) + whenever (0.8.4) + activesupport (>= 2.3.4) + chronic (>= 0.6.3) + wikicloth (0.8.3) builder expression_parser - rinku + htmlentities + nokogiri + twitter-text xpath (2.0.0) nokogiri (~> 1.3) @@ -742,17 +775,19 @@ PLATFORMS ruby DEPENDENCIES - RedCloth - ace-rails-ap + RedCloth (~> 4.2.9) + ace-rails-ap (~> 2.0.1) + activerecord-deprecated_finders (~> 1.0.3) + activerecord-session_store (~> 0.1.0) acts-as-taggable-on (~> 3.4) - addressable - annotate (~> 2.6.0.beta2) + addressable (~> 2.3.8) + annotate (~> 2.6.0) asana (~> 0.0.6) asciidoctor (~> 1.5.2) - attr_encrypted (= 1.3.4) - awesome_print - better_errors - binding_of_caller + attr_encrypted (~> 1.3.4) + awesome_print (~> 1.2.0) + better_errors (~> 1.0.1) + binding_of_caller (~> 0.7.2) bootstrap-sass (~> 3.0) brakeman browser (~> 0.8.0) @@ -760,127 +795,136 @@ DEPENDENCIES cal-heatmap-rails (~> 0.0.1) capybara (~> 2.4.0) capybara-screenshot (~> 1.0.0) - carrierwave - charlock_holmes - coffee-rails - colored + carrierwave (~> 0.9.0) + charlock_holmes (~> 0.6.9.4) + coffee-rails (~> 4.1.0) + colored (~> 1.2) coveralls (~> 0.8.2) creole (~> 0.3.6) d3_rails (~> 3.5.5) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) - devise (= 3.2.4) - devise-async (= 0.9.0) - devise-two-factor + devise (~> 3.2.4) + devise-async (~> 0.9.0) + devise-two-factor (~> 1.0.1) diffy (~> 3.0.3) - doorkeeper (= 2.1.3) - dropzonejs-rails - email_reply_parser + doorkeeper (~> 2.1.3) + dropzonejs-rails (~> 0.7.1) + email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) - enumerize - factory_girl_rails + enumerize (~> 0.7.0) + factory_girl_rails (~> 4.3.0) ffaker (~> 2.0.0) fog (~> 1.25.0) font-awesome-rails (~> 4.2) foreman fuubar (~> 2.0.0) gemnasium-gitlab-service (~> 0.2) - github-markup + github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-grack (~> 2.0.2) gitlab-linguist (~> 3.0.1) + gitlab_ci_meta (~> 4.0) gitlab_emoji (~> 0.1) gitlab_git (~> 7.2.14) gitlab_meta (= 7.0) - gitlab_omniauth-ldap (= 1.2.1) + gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) gon (~> 5.0.0) grape (~> 0.6.1) grape-entity (~> 0.4.2) - haml-rails + growl + guard-rspec (~> 4.2.0) + haml-rails (~> 0.5.3) hipchat (~> 1.5.0) html-pipeline (~> 1.11.0) - httparty + httparty (~> 0.13.3) jquery-atwho-rails (~> 1.0.0) - jquery-rails (= 3.1.3) - jquery-scrollto-rails - jquery-turbolinks - jquery-ui-rails + jquery-rails (~> 3.1.3) + jquery-scrollto-rails (~> 1.4.3) + jquery-turbolinks (~> 2.0.1) + jquery-ui-rails (~> 4.2.1) kaminari (~> 0.15.1) - letter_opener + letter_opener (~> 1.1.2) mail_room (~> 0.4.0) - minitest (~> 5.3.0) - mousetrap-rails - mysql2 - newrelic_rpm - nprogress-rails - octokit (= 3.7.0) + minitest (~> 5.7.0) + mousetrap-rails (~> 1.4.6) + mysql2 (~> 0.3.16) + nested_form (~> 0.3.2) + newrelic_rpm (~> 3.9.4.245) + nprogress-rails (~> 0.1.2.3) + oauth2 (~> 1.0.0) + octokit (~> 3.7.0) omniauth (~> 1.2.2) - omniauth-bitbucket - omniauth-github - omniauth-gitlab - omniauth-google-oauth2 - omniauth-kerberos + omniauth-bitbucket (~> 0.0.2) + omniauth-github (~> 1.1.1) + omniauth-gitlab (~> 1.0.0) + omniauth-google-oauth2 (~> 0.2.5) + omniauth-kerberos (~> 0.2.0) omniauth-saml (~> 1.4.0) - omniauth-shibboleth - omniauth-twitter - org-ruby (= 0.9.12) - pg + omniauth-shibboleth (~> 1.1.1) + omniauth-twitter (~> 1.0.1) + org-ruby (~> 0.9.12) + paranoia (~> 2.0) + pg (~> 0.18.2) poltergeist (~> 1.6.0) pry-rails - quiet_assets (~> 1.0.1) + quiet_assets (~> 1.0.2) rack-attack (~> 4.3.0) - rack-cors - rack-mini-profiler + rack-cors (~> 0.2.9) + rack-mini-profiler (~> 0.9.0) rack-oauth2 (~> 1.0.5) - rails (= 4.1.11) + rails (= 4.1.12) raphael-rails (~> 2.1.2) + rb-fsevent + rb-inotify rdoc (~> 3.6) redcarpet (~> 3.3.2) - redis-rails - request_store + redis-rails (~> 4.0.0) + request_store (~> 1.2.0) rerun (~> 0.10.0) - rqrcode-rails3 + rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) - rubocop (= 0.28.0) + rubocop (~> 0.28.0) sanitize (~> 2.0) sass-rails (~> 4.0.5) - sdoc - seed-fu + sdoc (~> 0.3.20) + seed-fu (~> 2.3.5) select2-rails (~> 3.5.9) - settingslogic + settingslogic (~> 2.0.9) shoulda-matchers (~> 2.8.0) sidekiq (~> 3.3) - sidetiq (= 0.6.3) - simplecov - sinatra - six + sidetiq (~> 0.6.3) + simplecov (~> 0.10.0) + sinatra (~> 1.4.4) + six (~> 0.2.0) slack-notifier (~> 1.0.0) - slim - spinach-rails - spring (~> 1.3.1) - spring-commands-rspec (~> 1.0.0) + slim (~> 2.0.2) + spinach-rails (~> 0.2.1) + spring (~> 1.3.6) + spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.0.0) spring-commands-teaspoon (~> 0.0.2) sprockets (~> 2.12.3) - stamp - state_machine - task_list (= 1.0.2) + stamp (~> 0.5.0) + state_machine (~> 1.2.0) + task_list (~> 1.0.2) teaspoon (~> 1.0.0) - teaspoon-jasmine - test_after_commit - thin + teaspoon-jasmine (~> 2.2.0) + test_after_commit (~> 0.2.2) + thin (~> 1.6.1) tinder (~> 1.9.2) turbolinks (~> 2.5.0) - uglifier + uglifier (~> 2.3.2) underscore-rails (~> 1.4.4) - unf - unicorn (~> 4.6.3) - unicorn-worker-killer - version_sorter - virtus + unf (~> 0.1.4) + unicorn (~> 4.8.2) + unicorn-worker-killer (~> 0.4.2) + version_sorter (~> 2.0.0) + virtus (~> 1.0.1) webmock (~> 1.21.0) - wikicloth (= 0.8.1) + whenever (~> 0.8.4) + wikicloth (~> 0.8.1) BUNDLED WITH 1.10.6 diff --git a/Procfile b/Procfile index 18fd9eb3d92..08880b9c425 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q common -q default +worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/app/assets/images/ci/arch.jpg b/app/assets/images/ci/arch.jpg new file mode 100644 index 00000000000..0e05674e840 Binary files /dev/null and b/app/assets/images/ci/arch.jpg differ diff --git a/app/assets/images/ci/favicon.ico b/app/assets/images/ci/favicon.ico new file mode 100644 index 00000000000..9663d4d00b9 Binary files /dev/null and b/app/assets/images/ci/favicon.ico differ diff --git a/app/assets/images/ci/loader.gif b/app/assets/images/ci/loader.gif new file mode 100644 index 00000000000..2fcb8f2da0d Binary files /dev/null and b/app/assets/images/ci/loader.gif differ diff --git a/app/assets/images/ci/no_avatar.png b/app/assets/images/ci/no_avatar.png new file mode 100644 index 00000000000..752d26adba7 Binary files /dev/null and b/app/assets/images/ci/no_avatar.png differ diff --git a/app/assets/images/ci/rails.png b/app/assets/images/ci/rails.png new file mode 100644 index 00000000000..d5edc04e65f Binary files /dev/null and b/app/assets/images/ci/rails.png differ diff --git a/app/assets/images/ci/service_sample.png b/app/assets/images/ci/service_sample.png new file mode 100644 index 00000000000..65d29e3fd89 Binary files /dev/null and b/app/assets/images/ci/service_sample.png differ diff --git a/app/assets/javascripts/ci/Chart.min.js b/app/assets/javascripts/ci/Chart.min.js new file mode 100644 index 00000000000..ab635881087 --- /dev/null +++ b/app/assets/javascripts/ci/Chart.min.js @@ -0,0 +1,39 @@ +var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a= +Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);dc;)a=dc?c:!isNaN(parseFloat(b))&& +isFinite(b)&&a)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c? +b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)? +0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1== +a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);ea?-0.5*e*Math.pow(2,10* +(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)* +a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0, +scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce", +animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)", +scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a, +c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1, +onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0, +pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'", +scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]); +d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;fe&&(e=a[f].value),a[f].valuel&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE; +h=Number.MAX_VALUE;for(f=0;fe&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;gt?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0t?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]< +h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;ed?h:d;d+=10}r=q-d-t;m= +Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0 + event.preventDefault() + + descr = $(this).closest('.runner-description').first() + descr.addClass('hide') + form = descr.next('.runner-description-form') + descrInput = form.find('input.description') + originalValue = descrInput.val() + form.removeClass('hide') + form.find('.cancel').on 'click', (event) -> + event.preventDefault() + + form.addClass('hide') + descrInput.val(originalValue) + descr.removeClass('hide') + +$(document).on 'click', '.assign-all-runner', -> + $(this).replaceWith(' Assign in progress..') + +window.unbindEvents = -> + $(document).unbind('scroll') + $(document).off('scroll') + +document.addEventListener("page:fetch", unbindEvents) diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee new file mode 100644 index 00000000000..be4a3aa757a --- /dev/null +++ b/app/assets/javascripts/ci/build.coffee @@ -0,0 +1,41 @@ +class CiBuild + @interval: null + + constructor: (build_url, build_status) -> + clearInterval(CiBuild.interval) + + if build_status == "running" || build_status == "pending" + # + # Bind autoscroll button to follow build output + # + $("#autoscroll-button").bind "click", -> + state = $(this).data("state") + if "enabled" is state + $(this).data "state", "disabled" + $(this).text "enable autoscroll" + else + $(this).data "state", "enabled" + $(this).text "disable autoscroll" + + # + # Check for new build output if user still watching build page + # Only valid for runnig build when output changes during time + # + CiBuild.interval = setInterval => + if window.location.href is build_url + $.ajax + url: build_url + dataType: "json" + success: (build) => + if build.status == "running" + $('#build-trace code').html build.trace_html + $('#build-trace code').append '' + @checkAutoscroll() + else + Turbolinks.visit build_url + , 4000 + + checkAutoscroll: -> + $("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state") + +@CiBuild = CiBuild diff --git a/app/assets/javascripts/ci/pager.js.coffee b/app/assets/javascripts/ci/pager.js.coffee new file mode 100644 index 00000000000..b57e7c736e9 --- /dev/null +++ b/app/assets/javascripts/ci/pager.js.coffee @@ -0,0 +1,42 @@ +@CiPager = + init: (@url, @limit = 0, preload, @disable = false) -> + if preload + @offset = 0 + @getItems() + else + @offset = @limit + @initLoadMore() + + getItems: -> + $(".loading").show() + $.ajax + type: "GET" + url: @url + data: "limit=" + @limit + "&offset=" + @offset + complete: => + $(".loading").hide() + success: (data) => + Pager.append(data.count, data.html) + dataType: "json" + + append: (count, html) -> + if count > 1 + $(".content-list").append html + if count == @limit + @offset += count + else + @disable = true + + initLoadMore: -> + $(document).unbind('scroll') + $(document).endlessScroll + bottomPixels: 400 + fireDelay: 1000 + fireOnce: true + ceaseFire: -> + Pager.disable + + callback: (i) => + unless $(".loading").is(':visible') + $(".loading").show() + Pager.getItems() diff --git a/app/assets/javascripts/ci/projects.js.coffee b/app/assets/javascripts/ci/projects.js.coffee new file mode 100644 index 00000000000..7e028b4e115 --- /dev/null +++ b/app/assets/javascripts/ci/projects.js.coffee @@ -0,0 +1,6 @@ +$(document).on 'click', '.badge-codes-toggle', -> + $('.badge-codes-block').toggleClass("hide") + return false + +$(document).on 'click', '.sync-now', -> + $(this).find('i').addClass('fa-spin') diff --git a/app/assets/stylesheets/ci/application.scss b/app/assets/stylesheets/ci/application.scss new file mode 100644 index 00000000000..ce080c7cf8a --- /dev/null +++ b/app/assets/stylesheets/ci/application.scss @@ -0,0 +1,46 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the top of the + * compiled file, but it's generally better to create a new file per style scope. + * + *= require_self + */ + +@import "main/variables.scss"; +@import "main/mixins.scss"; +@import "main/fonts.scss"; +@import "main/layout.scss"; + +/** + * Twitter bootstrap + */ +@import 'bootstrap'; + +/** + * Font icons + * + */ +@import "font-awesome"; + +/** + * Generic css (forms, nav etc): + */ +@import "generic/*"; + +/** + * Page specific styles (issues, projects etc): + */ + +@import "sections/*"; + +/* + * NProgress + */ +$nprogress-color: #9BC; +@import 'nprogress'; +@import 'nprogress-bootstrap'; diff --git a/app/assets/stylesheets/ci/generic/avatar.scss b/app/assets/stylesheets/ci/generic/avatar.scss new file mode 100644 index 00000000000..fc0914cddea --- /dev/null +++ b/app/assets/stylesheets/ci/generic/avatar.scss @@ -0,0 +1,29 @@ +.avatar { + float: left; + margin-right: 12px; + width: 40px; + height: 40px; + padding: 0; + @include border-radius($avatar_radius); + + &.avatar-inline { + float: none; + margin-left: 4px; + margin-bottom: 2px; + + &.s16 { margin-right: 4px; } + &.s24 { margin-right: 4px; } + } + + &.avatar-tile { + @include border-radius(0px); + } + + &.s16 { width: 16px; height: 16px; margin-right: 6px; } + &.s24 { width: 24px; height: 24px; margin-right: 8px; } + &.s26 { width: 26px; height: 26px; margin-right: 8px; } + &.s32 { width: 32px; height: 32px; margin-right: 10px; } + &.s60 { width: 60px; height: 60px; margin-right: 12px; } + &.s90 { width: 90px; height: 90px; margin-right: 15px; } + &.s160 { width: 160px; height: 160px; margin-right: 20px; } +} diff --git a/app/assets/stylesheets/ci/generic/buttons.scss b/app/assets/stylesheets/ci/generic/buttons.scss new file mode 100644 index 00000000000..5605c097c03 --- /dev/null +++ b/app/assets/stylesheets/ci/generic/buttons.scss @@ -0,0 +1,7 @@ +.btn { + @extend .btn-default; + + &.btn-save { + @extend .btn-primary; + } +} diff --git a/app/assets/stylesheets/ci/generic/callout.scss b/app/assets/stylesheets/ci/generic/callout.scss new file mode 100644 index 00000000000..f1699d21c9b --- /dev/null +++ b/app/assets/stylesheets/ci/generic/callout.scss @@ -0,0 +1,45 @@ +/* + * Callouts from Bootstrap3 docs + * + * Not quite alerts, but custom and helpful notes for folks reading the docs. + * Requires a base and modifier class. + */ + +/* Common styles for all types */ +.bs-callout { + margin: 20px 0; + padding: 20px; + border-left: 3px solid #eee; + color: #666; + background: #f9f9f9; +} +.bs-callout h4 { + margin-top: 0; + margin-bottom: 5px; +} +.bs-callout p:last-child { + margin-bottom: 0; +} + +/* Variations */ +.bs-callout-danger { + background-color: #fdf7f7; + border-color: #eed3d7; + color: #b94a48; +} +.bs-callout-warning { + background-color: #faf8f0; + border-color: #faebcc; + color: #8a6d3b; +} +.bs-callout-info { + background-color: #f4f8fa; + border-color: #bce8f1; + color: #34789a; +} +.bs-callout-success { + background-color: #dff0d8; + border-color: #5cA64d; + color: #3c763d; +} + diff --git a/app/assets/stylesheets/ci/generic/common.scss b/app/assets/stylesheets/ci/generic/common.scss new file mode 100644 index 00000000000..58b7a93b0ad --- /dev/null +++ b/app/assets/stylesheets/ci/generic/common.scss @@ -0,0 +1,189 @@ +/** COLORS **/ +.cgray { color: gray } +.clgray { color: #BBB } +.cred { color: #D12F19 } +.cgreen { color: #4a2 } +.cblue { color: #29A } +.cblack { color: #111 } +.cdark { color: #444 } +.camber { color: #ffc000 } +.cwhite { color: #fff!important } +.bgred { background: #F2DEDE!important } + +/** COMMON CLASSES **/ +.prepend-top-10 { margin-top:10px } +.prepend-top-20 { margin-top:20px } +.prepend-left-10 { margin-left:10px } +.prepend-left-20 { margin-left:20px } +.append-right-10 { margin-right:10px } +.append-right-20 { margin-right:20px } +.append-bottom-10 { margin-bottom:10px } +.append-bottom-15 { margin-bottom:15px } +.append-bottom-20 { margin-bottom:20px } +.inline { display: inline-block } +.padded { padding:20px } +.ipadded { padding:20px!important } +.lborder { border-left:1px solid #eee } +.underlined_link { text-decoration: underline; } +.hint { font-style: italic; color: #999; } +.light { color: #888 } +.tiny { font-weight: normal } +.vtop { vertical-align: top !important; } + + +.dropdown-menu > li > a { + text-shadow: none; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background: #29b; +} + +.breadcrumb > li + li:before { + content: "/"; + padding: 0; + color: #666; +} + +.str-truncated { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; + white-space: nowrap; + max-width: 82%; +} + +.page-title { + color: #444; + line-height: 1.5; + margin-top: 0px; + margin-bottom: 15px; +} + +.slead { + margin-bottom: 18px; + font-size: 16px; + font-weight: normal; + line-height: 1.4; +} + +.help-callout { + li { + font-size: 15px; + line-height: 1.6; + } +} + +/** light list with border-bottom between li **/ +ul.bordered-list { + margin: 5px 0px; + padding: 0px; + li { + padding: 5px 0; + border-bottom: 1px solid #EEE; + overflow: hidden; + display: block; + margin: 0px; + &:last-child { border:none } + &.active { + background: #f9f9f9; + a { font-weight: bold; } + } + } + + &.top-list { + li:first-child { + padding-top: 0; + h4, h5 { + margin-top: 0; + } + } + } +} + +.underlined-title { + border-bottom: 1px solid #ccc; + padding: 0 0 3px 3px; +} + +// Nav tabs +.nav.nav-tabs { + li { + > a { + padding: 8px 20px; + margin-right: 7px; + line-height: 20px; + border-color: #EEE; + color: #888; + border-bottom: 1px solid #ddd; + .badge { + background-color: #eee; + color: #888; + text-shadow: 0 1px 1px #fff; + } + i[class^="fa-"] { + line-height: 14px; + } + } + &.active { + > a { + border-color: #CCC; + border-bottom: 1px solid #fff; + color: #333; + font-weight: bold; + } + } + } + + &.nav-small-tabs > li > a { + padding: 6px 9px; + } +} + +.nav-tabs > li > a, +.nav-pills > li > a { + color: #666; +} + +.nav-small > li > a { + padding: 3px 5px; + font-size: 12px; +} + + + +// Breadcrumb +ul.breadcrumb { + background: white; + border: none; + li { + display: inline; + text-shadow: 0 1px 0 white + } + + a { + font-size: 16px; + } +} + +/** + * fix to keep tooltips position in top navigation bar + * + */ +.navbar .nav > li { + position: relative; + white-space: nowrap; +} + +// alerts +.alert-disabled { + background-color: #e6e6e6; + border-color: #ebccd1; + color: #b0b0b0; +} + +.label { + margin-right: 5px; + font-weight: normal; +} diff --git a/app/assets/stylesheets/ci/generic/forms.scss b/app/assets/stylesheets/ci/generic/forms.scss new file mode 100644 index 00000000000..c8e4e8d6602 --- /dev/null +++ b/app/assets/stylesheets/ci/generic/forms.scss @@ -0,0 +1,28 @@ +input[type='text'].danger { + background: #F2DEDE!important; + border-color: #D66; + text-shadow: 0 1px 1px #fff +} + +fieldset { + margin-bottom: 25px; +} + +.form-actions { + padding: 17px 20px 18px; + margin-top: 18px; + margin-bottom: 18px; + background-color: whitesmoke; + border-top: 1px solid #e5e5e5; + padding-left: 17%; +} + +label { + &.control-label { + @extend .col-sm-2; + } + + &.inline-label { + margin: 0; + } +} diff --git a/app/assets/stylesheets/ci/generic/tables.scss b/app/assets/stylesheets/ci/generic/tables.scss new file mode 100644 index 00000000000..71a7d4abaee --- /dev/null +++ b/app/assets/stylesheets/ci/generic/tables.scss @@ -0,0 +1,20 @@ +table { + &.table { + tr { + td, th { + padding: 8px 10px; + line-height: 20px; + vertical-align: middle; + } + th { + font-weight: normal; + font-size: 15px; + border-bottom: 1px solid #CCC !important; + } + td { + border-color: #F1F1F1 !important; + border-bottom: 1px solid; + } + } + } +} diff --git a/app/assets/stylesheets/ci/generic/typography.scss b/app/assets/stylesheets/ci/generic/typography.scss new file mode 100644 index 00000000000..b9ed23b9d3a --- /dev/null +++ b/app/assets/stylesheets/ci/generic/typography.scss @@ -0,0 +1,63 @@ +h6 { + color: #888; + text-transform: uppercase; +} + +pre { + font-family: $monospace_font; + + &.dark { + background: #333; + color: #f5f5f5; + } +} + +/** + * Links + * + */ +a { + outline: none; + color: $link_color; + &:hover { + text-decoration: none; + color: $primary_color; + } + + &:focus { + text-decoration: underline; + } + + &.dark { + color: $style_color; + } + + &.lined { + text-decoration: underline; + &:hover { text-decoration: underline; } + } + + &.gray { + color: gray; + } + + &.supp_diff_link { + text-align: center; + padding: 20px 0; + background: #f1f1f1; + width: 100%; + float: left; + } + + &.neib { + margin-right: 15px; + } +} + +a:focus { + outline: none; +} + +.monospace { + font-family: $monospace_font; +} diff --git a/app/assets/stylesheets/ci/generic/xterm.scss b/app/assets/stylesheets/ci/generic/xterm.scss new file mode 100644 index 00000000000..460a6bb2024 --- /dev/null +++ b/app/assets/stylesheets/ci/generic/xterm.scss @@ -0,0 +1,904 @@ +// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg +// see also: https://gist.github.com/jasonm23/2868981 + +$black: #000000; +$red: #cd0000; +$green: #00cd00; +$yellow: #cdcd00; +$blue: #0000ee; // according to wikipedia, this is the xterm standard +//$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) +$magenta: #cd00cd; +$cyan: #00cdcd; +$white: #e5e5e5; +$l-black: #7f7f7f; +$l-red: #ff0000; +$l-green: #00ff00; +$l-yellow: #ffff00; +$l-blue: #5c5cff; +$l-magenta: #ff00ff; +$l-cyan: #00ffff; +$l-white: #ffffff; + +.term-bold { + font-weight: bold; +} +.term-italic { + font-style: italic; +} +.term-conceal { + visibility: hidden; +} +.term-underline { + text-decoration: underline; +} +.term-cross { + text-decoration: line-through; +} + +.term-fg-black { + color: $black; +} +.term-fg-red { + color: $red; +} +.term-fg-green { + color: $green; +} +.term-fg-yellow { + color: $yellow; +} +.term-fg-blue { + color: $blue; +} +.term-fg-magenta { + color: $magenta; +} +.term-fg-cyan { + color: $cyan; +} +.term-fg-white { + color: $white; +} +.term-fg-l-black { + color: $l-black; +} +.term-fg-l-red { + color: $l-red; +} +.term-fg-l-green { + color: $l-green; +} +.term-fg-l-yellow { + color: $l-yellow; +} +.term-fg-l-blue { + color: $l-blue; +} +.term-fg-l-magenta { + color: $l-magenta; +} +.term-fg-l-cyan { + color: $l-cyan; +} +.term-fg-l-white { + color: $l-white; +} + +.term-bg-black { + background-color: $black; +} +.term-bg-red { + background-color: $red; +} +.term-bg-green { + background-color: $green; +} +.term-bg-yellow { + background-color: $yellow; +} +.term-bg-blue { + background-color: $blue; +} +.term-bg-magenta { + background-color: $magenta; +} +.term-bg-cyan { + background-color: $cyan; +} +.term-bg-white { + background-color: $white; +} +.term-bg-l-black { + background-color: $l-black; +} +.term-bg-l-red { + background-color: $l-red; +} +.term-bg-l-green { + background-color: $l-green; +} +.term-bg-l-yellow { + background-color: $l-yellow; +} +.term-bg-l-blue { + background-color: $l-blue; +} +.term-bg-l-magenta { + background-color: $l-magenta; +} +.term-bg-l-cyan { + background-color: $l-cyan; +} +.term-bg-l-white { + background-color: $l-white; +} + + +.xterm-fg-0 { + color: #000000; +} +.xterm-fg-1 { + color: #800000; +} +.xterm-fg-2 { + color: #008000; +} +.xterm-fg-3 { + color: #808000; +} +.xterm-fg-4 { + color: #000080; +} +.xterm-fg-5 { + color: #800080; +} +.xterm-fg-6 { + color: #008080; +} +.xterm-fg-7 { + color: #c0c0c0; +} +.xterm-fg-8 { + color: #808080; +} +.xterm-fg-9 { + color: #ff0000; +} +.xterm-fg-10 { + color: #00ff00; +} +.xterm-fg-11 { + color: #ffff00; +} +.xterm-fg-12 { + color: #0000ff; +} +.xterm-fg-13 { + color: #ff00ff; +} +.xterm-fg-14 { + color: #00ffff; +} +.xterm-fg-15 { + color: #ffffff; +} +.xterm-fg-16 { + color: #000000; +} +.xterm-fg-17 { + color: #00005f; +} +.xterm-fg-18 { + color: #000087; +} +.xterm-fg-19 { + color: #0000af; +} +.xterm-fg-20 { + color: #0000d7; +} +.xterm-fg-21 { + color: #0000ff; +} +.xterm-fg-22 { + color: #005f00; +} +.xterm-fg-23 { + color: #005f5f; +} +.xterm-fg-24 { + color: #005f87; +} +.xterm-fg-25 { + color: #005faf; +} +.xterm-fg-26 { + color: #005fd7; +} +.xterm-fg-27 { + color: #005fff; +} +.xterm-fg-28 { + color: #008700; +} +.xterm-fg-29 { + color: #00875f; +} +.xterm-fg-30 { + color: #008787; +} +.xterm-fg-31 { + color: #0087af; +} +.xterm-fg-32 { + color: #0087d7; +} +.xterm-fg-33 { + color: #0087ff; +} +.xterm-fg-34 { + color: #00af00; +} +.xterm-fg-35 { + color: #00af5f; +} +.xterm-fg-36 { + color: #00af87; +} +.xterm-fg-37 { + color: #00afaf; +} +.xterm-fg-38 { + color: #00afd7; +} +.xterm-fg-39 { + color: #00afff; +} +.xterm-fg-40 { + color: #00d700; +} +.xterm-fg-41 { + color: #00d75f; +} +.xterm-fg-42 { + color: #00d787; +} +.xterm-fg-43 { + color: #00d7af; +} +.xterm-fg-44 { + color: #00d7d7; +} +.xterm-fg-45 { + color: #00d7ff; +} +.xterm-fg-46 { + color: #00ff00; +} +.xterm-fg-47 { + color: #00ff5f; +} +.xterm-fg-48 { + color: #00ff87; +} +.xterm-fg-49 { + color: #00ffaf; +} +.xterm-fg-50 { + color: #00ffd7; +} +.xterm-fg-51 { + color: #00ffff; +} +.xterm-fg-52 { + color: #5f0000; +} +.xterm-fg-53 { + color: #5f005f; +} +.xterm-fg-54 { + color: #5f0087; +} +.xterm-fg-55 { + color: #5f00af; +} +.xterm-fg-56 { + color: #5f00d7; +} +.xterm-fg-57 { + color: #5f00ff; +} +.xterm-fg-58 { + color: #5f5f00; +} +.xterm-fg-59 { + color: #5f5f5f; +} +.xterm-fg-60 { + color: #5f5f87; +} +.xterm-fg-61 { + color: #5f5faf; +} +.xterm-fg-62 { + color: #5f5fd7; +} +.xterm-fg-63 { + color: #5f5fff; +} +.xterm-fg-64 { + color: #5f8700; +} +.xterm-fg-65 { + color: #5f875f; +} +.xterm-fg-66 { + color: #5f8787; +} +.xterm-fg-67 { + color: #5f87af; +} +.xterm-fg-68 { + color: #5f87d7; +} +.xterm-fg-69 { + color: #5f87ff; +} +.xterm-fg-70 { + color: #5faf00; +} +.xterm-fg-71 { + color: #5faf5f; +} +.xterm-fg-72 { + color: #5faf87; +} +.xterm-fg-73 { + color: #5fafaf; +} +.xterm-fg-74 { + color: #5fafd7; +} +.xterm-fg-75 { + color: #5fafff; +} +.xterm-fg-76 { + color: #5fd700; +} +.xterm-fg-77 { + color: #5fd75f; +} +.xterm-fg-78 { + color: #5fd787; +} +.xterm-fg-79 { + color: #5fd7af; +} +.xterm-fg-80 { + color: #5fd7d7; +} +.xterm-fg-81 { + color: #5fd7ff; +} +.xterm-fg-82 { + color: #5fff00; +} +.xterm-fg-83 { + color: #5fff5f; +} +.xterm-fg-84 { + color: #5fff87; +} +.xterm-fg-85 { + color: #5fffaf; +} +.xterm-fg-86 { + color: #5fffd7; +} +.xterm-fg-87 { + color: #5fffff; +} +.xterm-fg-88 { + color: #870000; +} +.xterm-fg-89 { + color: #87005f; +} +.xterm-fg-90 { + color: #870087; +} +.xterm-fg-91 { + color: #8700af; +} +.xterm-fg-92 { + color: #8700d7; +} +.xterm-fg-93 { + color: #8700ff; +} +.xterm-fg-94 { + color: #875f00; +} +.xterm-fg-95 { + color: #875f5f; +} +.xterm-fg-96 { + color: #875f87; +} +.xterm-fg-97 { + color: #875faf; +} +.xterm-fg-98 { + color: #875fd7; +} +.xterm-fg-99 { + color: #875fff; +} +.xterm-fg-100 { + color: #878700; +} +.xterm-fg-101 { + color: #87875f; +} +.xterm-fg-102 { + color: #878787; +} +.xterm-fg-103 { + color: #8787af; +} +.xterm-fg-104 { + color: #8787d7; +} +.xterm-fg-105 { + color: #8787ff; +} +.xterm-fg-106 { + color: #87af00; +} +.xterm-fg-107 { + color: #87af5f; +} +.xterm-fg-108 { + color: #87af87; +} +.xterm-fg-109 { + color: #87afaf; +} +.xterm-fg-110 { + color: #87afd7; +} +.xterm-fg-111 { + color: #87afff; +} +.xterm-fg-112 { + color: #87d700; +} +.xterm-fg-113 { + color: #87d75f; +} +.xterm-fg-114 { + color: #87d787; +} +.xterm-fg-115 { + color: #87d7af; +} +.xterm-fg-116 { + color: #87d7d7; +} +.xterm-fg-117 { + color: #87d7ff; +} +.xterm-fg-118 { + color: #87ff00; +} +.xterm-fg-119 { + color: #87ff5f; +} +.xterm-fg-120 { + color: #87ff87; +} +.xterm-fg-121 { + color: #87ffaf; +} +.xterm-fg-122 { + color: #87ffd7; +} +.xterm-fg-123 { + color: #87ffff; +} +.xterm-fg-124 { + color: #af0000; +} +.xterm-fg-125 { + color: #af005f; +} +.xterm-fg-126 { + color: #af0087; +} +.xterm-fg-127 { + color: #af00af; +} +.xterm-fg-128 { + color: #af00d7; +} +.xterm-fg-129 { + color: #af00ff; +} +.xterm-fg-130 { + color: #af5f00; +} +.xterm-fg-131 { + color: #af5f5f; +} +.xterm-fg-132 { + color: #af5f87; +} +.xterm-fg-133 { + color: #af5faf; +} +.xterm-fg-134 { + color: #af5fd7; +} +.xterm-fg-135 { + color: #af5fff; +} +.xterm-fg-136 { + color: #af8700; +} +.xterm-fg-137 { + color: #af875f; +} +.xterm-fg-138 { + color: #af8787; +} +.xterm-fg-139 { + color: #af87af; +} +.xterm-fg-140 { + color: #af87d7; +} +.xterm-fg-141 { + color: #af87ff; +} +.xterm-fg-142 { + color: #afaf00; +} +.xterm-fg-143 { + color: #afaf5f; +} +.xterm-fg-144 { + color: #afaf87; +} +.xterm-fg-145 { + color: #afafaf; +} +.xterm-fg-146 { + color: #afafd7; +} +.xterm-fg-147 { + color: #afafff; +} +.xterm-fg-148 { + color: #afd700; +} +.xterm-fg-149 { + color: #afd75f; +} +.xterm-fg-150 { + color: #afd787; +} +.xterm-fg-151 { + color: #afd7af; +} +.xterm-fg-152 { + color: #afd7d7; +} +.xterm-fg-153 { + color: #afd7ff; +} +.xterm-fg-154 { + color: #afff00; +} +.xterm-fg-155 { + color: #afff5f; +} +.xterm-fg-156 { + color: #afff87; +} +.xterm-fg-157 { + color: #afffaf; +} +.xterm-fg-158 { + color: #afffd7; +} +.xterm-fg-159 { + color: #afffff; +} +.xterm-fg-160 { + color: #d70000; +} +.xterm-fg-161 { + color: #d7005f; +} +.xterm-fg-162 { + color: #d70087; +} +.xterm-fg-163 { + color: #d700af; +} +.xterm-fg-164 { + color: #d700d7; +} +.xterm-fg-165 { + color: #d700ff; +} +.xterm-fg-166 { + color: #d75f00; +} +.xterm-fg-167 { + color: #d75f5f; +} +.xterm-fg-168 { + color: #d75f87; +} +.xterm-fg-169 { + color: #d75faf; +} +.xterm-fg-170 { + color: #d75fd7; +} +.xterm-fg-171 { + color: #d75fff; +} +.xterm-fg-172 { + color: #d78700; +} +.xterm-fg-173 { + color: #d7875f; +} +.xterm-fg-174 { + color: #d78787; +} +.xterm-fg-175 { + color: #d787af; +} +.xterm-fg-176 { + color: #d787d7; +} +.xterm-fg-177 { + color: #d787ff; +} +.xterm-fg-178 { + color: #d7af00; +} +.xterm-fg-179 { + color: #d7af5f; +} +.xterm-fg-180 { + color: #d7af87; +} +.xterm-fg-181 { + color: #d7afaf; +} +.xterm-fg-182 { + color: #d7afd7; +} +.xterm-fg-183 { + color: #d7afff; +} +.xterm-fg-184 { + color: #d7d700; +} +.xterm-fg-185 { + color: #d7d75f; +} +.xterm-fg-186 { + color: #d7d787; +} +.xterm-fg-187 { + color: #d7d7af; +} +.xterm-fg-188 { + color: #d7d7d7; +} +.xterm-fg-189 { + color: #d7d7ff; +} +.xterm-fg-190 { + color: #d7ff00; +} +.xterm-fg-191 { + color: #d7ff5f; +} +.xterm-fg-192 { + color: #d7ff87; +} +.xterm-fg-193 { + color: #d7ffaf; +} +.xterm-fg-194 { + color: #d7ffd7; +} +.xterm-fg-195 { + color: #d7ffff; +} +.xterm-fg-196 { + color: #ff0000; +} +.xterm-fg-197 { + color: #ff005f; +} +.xterm-fg-198 { + color: #ff0087; +} +.xterm-fg-199 { + color: #ff00af; +} +.xterm-fg-200 { + color: #ff00d7; +} +.xterm-fg-201 { + color: #ff00ff; +} +.xterm-fg-202 { + color: #ff5f00; +} +.xterm-fg-203 { + color: #ff5f5f; +} +.xterm-fg-204 { + color: #ff5f87; +} +.xterm-fg-205 { + color: #ff5faf; +} +.xterm-fg-206 { + color: #ff5fd7; +} +.xterm-fg-207 { + color: #ff5fff; +} +.xterm-fg-208 { + color: #ff8700; +} +.xterm-fg-209 { + color: #ff875f; +} +.xterm-fg-210 { + color: #ff8787; +} +.xterm-fg-211 { + color: #ff87af; +} +.xterm-fg-212 { + color: #ff87d7; +} +.xterm-fg-213 { + color: #ff87ff; +} +.xterm-fg-214 { + color: #ffaf00; +} +.xterm-fg-215 { + color: #ffaf5f; +} +.xterm-fg-216 { + color: #ffaf87; +} +.xterm-fg-217 { + color: #ffafaf; +} +.xterm-fg-218 { + color: #ffafd7; +} +.xterm-fg-219 { + color: #ffafff; +} +.xterm-fg-220 { + color: #ffd700; +} +.xterm-fg-221 { + color: #ffd75f; +} +.xterm-fg-222 { + color: #ffd787; +} +.xterm-fg-223 { + color: #ffd7af; +} +.xterm-fg-224 { + color: #ffd7d7; +} +.xterm-fg-225 { + color: #ffd7ff; +} +.xterm-fg-226 { + color: #ffff00; +} +.xterm-fg-227 { + color: #ffff5f; +} +.xterm-fg-228 { + color: #ffff87; +} +.xterm-fg-229 { + color: #ffffaf; +} +.xterm-fg-230 { + color: #ffffd7; +} +.xterm-fg-231 { + color: #ffffff; +} +.xterm-fg-232 { + color: #080808; +} +.xterm-fg-233 { + color: #121212; +} +.xterm-fg-234 { + color: #1c1c1c; +} +.xterm-fg-235 { + color: #262626; +} +.xterm-fg-236 { + color: #303030; +} +.xterm-fg-237 { + color: #3a3a3a; +} +.xterm-fg-238 { + color: #444444; +} +.xterm-fg-239 { + color: #4e4e4e; +} +.xterm-fg-240 { + color: #585858; +} +.xterm-fg-241 { + color: #626262; +} +.xterm-fg-242 { + color: #6c6c6c; +} +.xterm-fg-243 { + color: #767676; +} +.xterm-fg-244 { + color: #808080; +} +.xterm-fg-245 { + color: #8a8a8a; +} +.xterm-fg-246 { + color: #949494; +} +.xterm-fg-247 { + color: #9e9e9e; +} +.xterm-fg-248 { + color: #a8a8a8; +} +.xterm-fg-249 { + color: #b2b2b2; +} +.xterm-fg-250 { + color: #bcbcbc; +} +.xterm-fg-251 { + color: #c6c6c6; +} +.xterm-fg-252 { + color: #d0d0d0; +} +.xterm-fg-253 { + color: #dadada; +} +.xterm-fg-254 { + color: #e4e4e4; +} +.xterm-fg-255 { + color: #eeeeee; +} diff --git a/app/assets/stylesheets/ci/main/fonts.scss b/app/assets/stylesheets/ci/main/fonts.scss new file mode 100644 index 00000000000..8cc9986415c --- /dev/null +++ b/app/assets/stylesheets/ci/main/fonts.scss @@ -0,0 +1,2 @@ +/** Typo **/ +$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; diff --git a/app/assets/stylesheets/ci/main/layout.scss b/app/assets/stylesheets/ci/main/layout.scss new file mode 100644 index 00000000000..fa54481fa05 --- /dev/null +++ b/app/assets/stylesheets/ci/main/layout.scss @@ -0,0 +1,18 @@ +html { + overflow-y: scroll; + + &.touch .tooltip { display: none !important; } +} + +body { + margin-bottom: 20px; +} + +.container { + padding-top: 0; + z-index: 5; +} + +.container .content { + margin: 0 0; +} diff --git a/app/assets/stylesheets/ci/main/mixins.scss b/app/assets/stylesheets/ci/main/mixins.scss new file mode 100644 index 00000000000..40040822331 --- /dev/null +++ b/app/assets/stylesheets/ci/main/mixins.scss @@ -0,0 +1,31 @@ +@mixin box-shadow($shadow) { + -webkit-box-shadow: $shadow; + -moz-box-shadow: $shadow; + -ms-box-shadow: $shadow; + -o-box-shadow: $shadow; + box-shadow: $shadow; +} + +@mixin border-radius($radius) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + -ms-border-radius: $radius; + -o-border-radius: $radius; + border-radius: $radius; +} + +@mixin linear-gradient($from, $to) { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); + background-image: -webkit-linear-gradient($from, $to); + background-image: -moz-linear-gradient($from, $to); + background-image: -ms-linear-gradient($from, $to); + background-image: -o-linear-gradient($from, $to); +} + +@mixin transition($transition) { + -webkit-transition: $transition; + -moz-transition: $transition; + -ms-transition: $transition; + -o-transition: $transition; + transition: $transition; +} diff --git a/app/assets/stylesheets/ci/main/variables.scss b/app/assets/stylesheets/ci/main/variables.scss new file mode 100644 index 00000000000..a8c672a8057 --- /dev/null +++ b/app/assets/stylesheets/ci/main/variables.scss @@ -0,0 +1,44 @@ +/** + * General Colors + */ +$primary_color: #2FA0BB; +$link_color: #3A89A3; +$style_color: #246; +$bg_style_color: #246; +$hover: #D9EDF7; + +/* + * Success colors (green) + */ +$border_success: #019875; +$bg_success: #019875; + +/* + * Danger colors (red) + */ +$border_danger: #d43f3a; +$bg_danger: #d9534f; + +/* + * Primary colors (blue) + */ +$border_primary: #246; +$bg_primary: #246; + +/* + * Warning colors (yellow) + */ +$bg_warning: #EB9532; +$border_warning: #EB9532; + +/** + * Twitter bootstrap variables + */ +$font-size-base: 13px !default; +$nav-pills-active-link-hover-bg: $bg_style_color; +$pagination-active-bg: $bg_style_color; + +/** + * Avatar variables + */ +$avatar_radius: 50%; diff --git a/app/assets/stylesheets/ci/sections/builds.scss b/app/assets/stylesheets/ci/sections/builds.scss new file mode 100644 index 00000000000..a9d39bb0cbd --- /dev/null +++ b/app/assets/stylesheets/ci/sections/builds.scss @@ -0,0 +1,54 @@ +pre.trace { + background: #111111; + color: #fff; + font-family: $monospace_font; + white-space: pre; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + overflow: auto; + overflow-y: hidden; + font-size: 12px; + + .fa-refresh { + font-size: 24px; + margin-left: 20px; + } +} + +.autoscroll-container { + position: fixed; + bottom: 10px; + right: 20px; + z-index: 100; +} + +.scroll-controls { + position: fixed; + bottom: 10px; + left: 20px; + z-index: 100; + + a { + display: block; + margin-bottom: 5px; + } +} + +.build-widget { + padding: 10px; + background: #f4f4f4; + margin-bottom: 20px; + border-radius: 4px; + + .title { + margin-top: 0; + color: #666; + line-height: 1.5; + } + .attr-name { + color: #777; + } +} diff --git a/app/assets/stylesheets/ci/sections/lint.scss b/app/assets/stylesheets/ci/sections/lint.scss new file mode 100644 index 00000000000..7191b5d47aa --- /dev/null +++ b/app/assets/stylesheets/ci/sections/lint.scss @@ -0,0 +1,8 @@ +.incorrect-syntax{ + font-size: 19px; + color: red; +} +.correct-syntax{ + font-size: 19px; + color: #47a447; +} \ No newline at end of file diff --git a/app/assets/stylesheets/ci/sections/login.scss b/app/assets/stylesheets/ci/sections/login.scss new file mode 100644 index 00000000000..47e453ec8d2 --- /dev/null +++ b/app/assets/stylesheets/ci/sections/login.scss @@ -0,0 +1,13 @@ +.login-block { + padding: 15px; + margin: 0 auto; + text-align: center; + + p { + font-size: 15px; + } + + .btn-login { + padding: 18px 32px; + } +} diff --git a/app/assets/stylesheets/ci/sections/navbar.scss b/app/assets/stylesheets/ci/sections/navbar.scss new file mode 100644 index 00000000000..efa70eb2956 --- /dev/null +++ b/app/assets/stylesheets/ci/sections/navbar.scss @@ -0,0 +1,54 @@ +.navbar-static-top { + margin-bottom: 20px; +} + +.navbar-ci { + background: $style_color; + + .navbar-brand { + color: #fff; + + &:hover { + color: #fff; + } + } + .brand, + .nav > li > a { + color: #fff; + + &:hover, &:focus, &:active { + background: none; + } + } + + .profile-holder { + position: relative; + + img { + position: absolute; + top: -8px; + width: 32px; + @include border-radius(32px); + } + + span { + margin-left: 42px; + } + } + + .btn-login { + padding: 7px 22px; + margin-top: 7px; + &:hover, &:active, &:focus { + background: #018865 !important; + } + } +} + +.turbolink-spinner { + position: absolute; + top: 11px; + left: 50%; + color: #FFF; + font-size: 20px; +} diff --git a/app/assets/stylesheets/ci/sections/projects.scss b/app/assets/stylesheets/ci/sections/projects.scss new file mode 100644 index 00000000000..84ee1399bff --- /dev/null +++ b/app/assets/stylesheets/ci/sections/projects.scss @@ -0,0 +1,61 @@ +.project-title { + margin: 0; + color: #444; + font-size: 20px; + line-height: 1.5; +} + +.builds { + @extend .table; + + .build { + &.alert{ + margin-bottom: 6px; + } + } +} + +.projects-table { + td { + vertical-align: middle !important; + } +} + +.commit-info { + font-size: 14px; + + .attr-name { + font-weight: 300; + color: #666; + margin-right: 5px; + } + + pre.commit-message { + font-size: 14px; + background: none; + padding: 0; + margin: 0; + border: none; + margin: 20px 0; + border-bottom: 1px solid #EEE; + padding-bottom: 20px; + border-radius: 0; + } +} + +.search{ + width: 300px; + + .search-input{ + height: 35px; + } + + form{ + margin-top: 0; + margin-bottom: 0; + } +} + +.loading{ + font-size: 20px; +} diff --git a/app/assets/stylesheets/ci/sections/runners.scss b/app/assets/stylesheets/ci/sections/runners.scss new file mode 100644 index 00000000000..a9111a7388f --- /dev/null +++ b/app/assets/stylesheets/ci/sections/runners.scss @@ -0,0 +1,34 @@ +.runner-state { + padding: 6px 12px; + margin-right: 10px; + color: #FFF; + + &.runner-state-shared { + background: #32b186; + } + &.runner-state-specific { + background: #3498db; + } +} + +.runner-status-online { + color: green; +} + +.runner-status-offline { + color: gray; +} + +.runner-status-paused { + color: red; +} + +.runner { + .btn { + padding: 1px 6px; + } + + h4 { + font-weight: normal; + } +} diff --git a/app/assets/stylesheets/ci/sections/setup.scss b/app/assets/stylesheets/ci/sections/setup.scss new file mode 100644 index 00000000000..242614616d1 --- /dev/null +++ b/app/assets/stylesheets/ci/sections/setup.scss @@ -0,0 +1,11 @@ +.welcome-block { + margin-top: 50px; + color: #555; + font-size: 16px; + line-height: 1.5; + + h1, h2, h3 { + font-weight: bold; + margin-bottom: 20px; + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 12d439b0b31..ac9484a4cd2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,13 @@ require 'gon' class ApplicationController < ActionController::Base + def self.railtie_helpers_paths + "app/helpers/gitlab" + end + include Gitlab::CurrentSettings - include GitlabRoutingHelper - include PageLayoutHelper + include Gitlab::GitlabRoutingHelper + include Gitlab::PageLayoutHelper PER_PAGE = 20 @@ -131,9 +135,6 @@ class ApplicationController < ActionController::Base def repository @repository ||= project.repository - rescue Grit::NoSuchPathError => e - log_exception(e) - nil end def authorize_project!(action) diff --git a/app/controllers/ci/admin/application_controller.rb b/app/controllers/ci/admin/application_controller.rb new file mode 100644 index 00000000000..430fae14c7d --- /dev/null +++ b/app/controllers/ci/admin/application_controller.rb @@ -0,0 +1,10 @@ +module Ci + module Admin + class ApplicationController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :authenticate_admin! + + layout "ci/admin" + end + end +end diff --git a/app/controllers/ci/admin/application_settings_controller.rb b/app/controllers/ci/admin/application_settings_controller.rb new file mode 100644 index 00000000000..71e253fac67 --- /dev/null +++ b/app/controllers/ci/admin/application_settings_controller.rb @@ -0,0 +1,31 @@ +module Ci + class Admin::ApplicationSettingsController < Ci::Admin::ApplicationController + before_action :set_application_setting + + def show + end + + def update + if @application_setting.update_attributes(application_setting_params) + redirect_to ci_admin_application_settings_path, + notice: 'Application settings saved successfully' + else + render :show + end + end + + private + + def set_application_setting + @application_setting = Ci::ApplicationSetting.current + @application_setting ||= Ci::ApplicationSetting.create_from_defaults + end + + def application_setting_params + params.require(:application_setting).permit( + :all_broken_builds, + :add_pusher, + ) + end + end +end diff --git a/app/controllers/ci/admin/builds_controller.rb b/app/controllers/ci/admin/builds_controller.rb new file mode 100644 index 00000000000..8fc776dd98e --- /dev/null +++ b/app/controllers/ci/admin/builds_controller.rb @@ -0,0 +1,12 @@ +module Ci + class Admin::BuildsController < Ci::Admin::ApplicationController + def index + @scope = params[:scope] + @builds = Ci::Build.order('created_at DESC').page(params[:page]).per(30) + + if ["pending", "running"].include? @scope + @builds = @builds.send(@scope) + end + end + end +end diff --git a/app/controllers/ci/admin/events_controller.rb b/app/controllers/ci/admin/events_controller.rb new file mode 100644 index 00000000000..5939efff980 --- /dev/null +++ b/app/controllers/ci/admin/events_controller.rb @@ -0,0 +1,9 @@ +module Ci + class Admin::EventsController < Ci::Admin::ApplicationController + EVENTS_PER_PAGE = 50 + + def index + @events = Ci::Event.admin.order('created_at DESC').page(params[:page]).per(EVENTS_PER_PAGE) + end + end +end diff --git a/app/controllers/ci/admin/projects_controller.rb b/app/controllers/ci/admin/projects_controller.rb new file mode 100644 index 00000000000..5bbd0ce7396 --- /dev/null +++ b/app/controllers/ci/admin/projects_controller.rb @@ -0,0 +1,19 @@ +module Ci + class Admin::ProjectsController < Ci::Admin::ApplicationController + def index + @projects = Ci::Project.ordered_by_last_commit_date.page(params[:page]).per(30) + end + + def destroy + project.destroy + + redirect_to ci_projects_url + end + + protected + + def project + @project ||= Ci::Project.find(params[:id]) + end + end +end diff --git a/app/controllers/ci/admin/runner_projects_controller.rb b/app/controllers/ci/admin/runner_projects_controller.rb new file mode 100644 index 00000000000..e7de6eb12ca --- /dev/null +++ b/app/controllers/ci/admin/runner_projects_controller.rb @@ -0,0 +1,34 @@ +module Ci + class Admin::RunnerProjectsController < Ci::Admin::ApplicationController + layout 'ci/project' + + def index + @runner_projects = project.runner_projects.all + @runner_project = project.runner_projects.new + end + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + if @runner.assign_to(project, current_user) + redirect_to ci_admin_runner_path(@runner) + else + redirect_to ci_admin_runner_path(@runner), alert: 'Failed adding runner to project' + end + end + + def destroy + rp = Ci::RunnerProject.find(params[:id]) + runner = rp.runner + rp.destroy + + redirect_to ci_admin_runner_path(runner) + end + + private + + def project + @project ||= Ci::Project.find(params[:project_id]) + end + end +end diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb new file mode 100644 index 00000000000..4f5f3776ddc --- /dev/null +++ b/app/controllers/ci/admin/runners_controller.rb @@ -0,0 +1,69 @@ +module Ci + class Admin::RunnersController < Ci::Admin::ApplicationController + before_filter :runner, except: :index + + def index + @runners = Ci::Runner.order('id DESC') + @runners = @runners.search(params[:search]) if params[:search].present? + @runners = @runners.page(params[:page]).per(30) + @active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count + end + + def show + @builds = @runner.builds.order('id DESC').first(30) + @projects = Ci::Project.all + @projects = @projects.search(params[:search]) if params[:search].present? + @projects = @projects.where("projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? + @projects = @projects.page(params[:page]).per(30) + end + + def update + @runner.update_attributes(runner_params) + + respond_to do |format| + format.js + format.html { redirect_to ci_admin_runner_path(@runner) } + end + end + + def destroy + @runner.destroy + + redirect_to ci_admin_runners_path + end + + def resume + if @runner.update_attributes(active: true) + redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' + end + end + + def pause + if @runner.update_attributes(active: false) + redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' + end + end + + def assign_all + Ci::Project.unassigned(@runner).all.each do |project| + @runner.assign_to(project, current_user) + end + + redirect_to ci_admin_runner_path(@runner), notice: "Runner was assigned to all projects" + end + + private + + def runner + @runner ||= Ci::Runner.find(params[:id]) + end + + def runner_params + params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active) + end + end +end diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb new file mode 100644 index 00000000000..726781cb30b --- /dev/null +++ b/app/controllers/ci/application_controller.rb @@ -0,0 +1,133 @@ +module Ci + class ApplicationController < ActionController::Base + def self.railtie_helpers_paths + "app/helpers/ci" + end + + include Ci::UserSessionsHelper + + rescue_from Ci::Network::UnauthorizedError, with: :invalid_token + before_filter :default_headers + before_filter :check_config + + protect_from_forgery + + helper_method :current_user + before_filter :reset_cache + + private + + def current_user + @current_user ||= session[:ci_current_user] + end + + def sign_in(user) + session[:ci_current_user] = user + end + + def sign_out + reset_session + end + + def authenticate_user! + unless current_user + redirect_to new_ci_user_sessions_path + return + end + end + + def authenticate_admin! + unless current_user && current_user.is_admin + redirect_to new_ci_user_sessions_path + return + end + end + + def authenticate_public_page! + unless project.public + unless current_user + redirect_to(new_ci_user_sessions_path(state: generate_oauth_state(request.fullpath))) and return + end + + unless current_user.can_access_project?(project.gitlab_id) + page_404 and return + end + end + end + + def authenticate_token! + unless project.valid_token?(params[:token]) + return head(403) + end + end + + def authorize_access_project! + unless current_user.can_access_project?(@project.gitlab_id) + return page_404 + end + end + + def authorize_project_developer! + unless current_user.has_developer_access?(@project.gitlab_id) + return page_404 + end + end + + def authorize_manage_project! + unless current_user.can_manage_project?(@project.gitlab_id) + return page_404 + end + end + + def page_404 + render file: "#{Rails.root}/public/404.html", status: 404, layout: false + end + + # Reset user cache every day for security purposes + def reset_cache + if current_user && current_user.sync_at < (Time.zone.now - 24.hours) + current_user.reset_cache + end + end + + def default_headers + headers['X-Frame-Options'] = 'DENY' + headers['X-XSS-Protection'] = '1; mode=block' + end + + # JSON for infinite scroll via Pager object + def pager_json(partial, count) + html = render_to_string( + partial, + layout: false, + formats: [:html] + ) + + render json: { + html: html, + count: count + } + end + + def check_config + redirect_to oauth2_ci_help_path unless valid_config? + end + + def valid_config? + server = GitlabCi.config.gitlab_server + + if server.blank? || server.url.blank? || server.app_id.blank? || server.app_secret.blank? + false + else + true + end + rescue Settingslogic::MissingSetting, NoMethodError + false + end + + def invalid_token + reset_session + redirect_to ci_root_path + end + end +end diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb new file mode 100644 index 00000000000..eeff3f1e0a0 --- /dev/null +++ b/app/controllers/ci/builds_controller.rb @@ -0,0 +1,77 @@ +module Ci + class BuildsController < Ci::ApplicationController + before_filter :authenticate_user!, except: [:status, :show] + before_filter :authenticate_public_page!, only: :show + before_filter :project + before_filter :authorize_access_project!, except: [:status, :show] + before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel] + before_filter :authorize_project_developer!, only: [:retry, :cancel] + before_filter :build, except: [:show] + + def show + if params[:id] =~ /\A\d+\Z/ + @build = build + else + # try to find commit by sha + commit = commit_by_sha + + if commit + # Redirect to commit page + redirect_to ci_project_ref_commit_path(@project, @build.commit.ref, @build.commit.sha) + return + end + end + + raise ActiveRecord::RecordNotFound unless @build + + @builds = @project.commits.find_by_sha(@build.sha).builds.order('id DESC') + @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20) + @commit = @build.commit + + respond_to do |format| + format.html + format.json do + render json: @build.to_json(methods: :trace_html) + end + end + end + + def retry + if @build.commands.blank? + return page_404 + end + + build = Ci::Build.retry(@build) + + if params[:return_to] + redirect_to URI.parse(params[:return_to]).path + else + redirect_to ci_project_build_path(project, build) + end + end + + def status + render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) + end + + def cancel + @build.cancel + + redirect_to ci_project_build_path(@project, @build) + end + + protected + + def project + @project = Ci::Project.find(params[:project_id]) + end + + def build + @build ||= project.builds.unscoped.find_by(id: params[:id]) + end + + def commit_by_sha + @project.commits.find_by(sha: params[:id]) + end + end +end diff --git a/app/controllers/ci/charts_controller.rb b/app/controllers/ci/charts_controller.rb new file mode 100644 index 00000000000..63326ef36cc --- /dev/null +++ b/app/controllers/ci/charts_controller.rb @@ -0,0 +1,24 @@ +module Ci + class ChartsController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + + layout 'ci/project' + + def show + @charts = {} + @charts[:week] = Ci::Charts::WeekChart.new(@project) + @charts[:month] = Ci::Charts::MonthChart.new(@project) + @charts[:year] = Ci::Charts::YearChart.new(@project) + @charts[:build_times] = Ci::Charts::BuildTime.new(@project) + end + + protected + + def project + @project = Ci::Project.find(params[:project_id]) + end + end +end diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb new file mode 100644 index 00000000000..9f74a2fd807 --- /dev/null +++ b/app/controllers/ci/commits_controller.rb @@ -0,0 +1,37 @@ +module Ci + class CommitsController < Ci::ApplicationController + before_filter :authenticate_user!, except: [:status, :show] + before_filter :authenticate_public_page!, only: :show + before_filter :project + before_filter :authorize_access_project!, except: [:status, :show, :cancel] + before_filter :authorize_project_developer!, only: [:cancel] + before_filter :commit, only: :show + + def show + @builds = @commit.builds + end + + def status + commit = Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id]) + render json: commit.to_json(only: [:id, :sha], methods: [:status, :coverage]) + rescue ActiveRecord::RecordNotFound + render json: { status: "not_found" } + end + + def cancel + commit.builds.running_or_pending.each(&:cancel) + + redirect_to ci_project_ref_commit_path(project, commit.ref, commit.sha) + end + + private + + def project + @project ||= Ci::Project.find(params[:project_id]) + end + + def commit + @commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id]) + end + end +end diff --git a/app/controllers/ci/events_controller.rb b/app/controllers/ci/events_controller.rb new file mode 100644 index 00000000000..c515caabe63 --- /dev/null +++ b/app/controllers/ci/events_controller.rb @@ -0,0 +1,21 @@ +module Ci + class EventsController < Ci::ApplicationController + EVENTS_PER_PAGE = 50 + + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_manage_project! + + layout 'ci/project' + + def index + @events = project.events.order("created_at DESC").page(params[:page]).per(EVENTS_PER_PAGE) + end + + private + + def project + @project ||= Ci::Project.find(params[:project_id]) + end + end +end diff --git a/app/controllers/ci/helps_controller.rb b/app/controllers/ci/helps_controller.rb new file mode 100644 index 00000000000..a1ee4111614 --- /dev/null +++ b/app/controllers/ci/helps_controller.rb @@ -0,0 +1,16 @@ +module Ci + class HelpsController < Ci::ApplicationController + skip_filter :check_config + + def show + end + + def oauth2 + if valid_config? + redirect_to ci_root_path + else + render layout: 'ci/empty' + end + end + end +end diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb new file mode 100644 index 00000000000..62c2ba86e86 --- /dev/null +++ b/app/controllers/ci/lints_controller.rb @@ -0,0 +1,26 @@ +module Ci + class LintsController < Ci::ApplicationController + before_filter :authenticate_user! + + def show + end + + def create + if params[:content].blank? + @status = false + @error = "Please provide content of .gitlab-ci.yml" + else + @config_processor = Ci::GitlabCiYamlProcessor.new params[:content] + @stages = @config_processor.stages + @builds = @config_processor.builds + @status = true + end + rescue Ci::GitlabCiYamlProcessor::ValidationError => e + @error = e.message + @status = false + rescue Exception => e + @error = "Undefined error" + @status = false + end + end +end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb new file mode 100644 index 00000000000..6ff7fc9f77a --- /dev/null +++ b/app/controllers/ci/projects_controller.rb @@ -0,0 +1,136 @@ +module Ci + class ProjectsController < Ci::ApplicationController + PROJECTS_BATCH = 100 + + before_filter :authenticate_user!, except: [:build, :badge, :index, :show] + before_filter :authenticate_public_page!, only: :show + before_filter :project, only: [:build, :integration, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_filter :authorize_access_project!, except: [:build, :gitlab, :badge, :index, :show, :new, :create] + before_filter :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_filter :authenticate_token!, only: [:build] + before_filter :no_cache, only: [:badge] + protect_from_forgery except: :build + + layout 'ci/project', except: [:index, :gitlab] + + def index + @projects = Ci::Project.ordered_by_last_commit_date.public_only.page(params[:page]) unless current_user + end + + def gitlab + @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i + @page = @offset == 0 ? 1 : (@offset / @limit + 1) + + current_user.reset_cache if params[:reset_cache] + + @gl_projects = current_user.gitlab_projects(params[:search], @page, @limit) + @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date + @total_count = @gl_projects.size + @gl_projects.reject! { |gl_project| @projects.map(&:gitlab_id).include?(gl_project.id) } + respond_to do |format| + format.json do + pager_json("ci/projects/gitlab", @total_count) + end + end + rescue Ci::Network::UnauthorizedError + raise + rescue + @error = 'Failed to fetch GitLab projects' + end + + def show + @ref = params[:ref] + + @commits = @project.commits.reverse_order + @commits = @commits.where(ref: @ref) if @ref + @commits = @commits.page(params[:page]).per(20) + end + + def integration + end + + def create + project_data = OpenStruct.new(JSON.parse(params["project"])) + + unless current_user.can_manage_project?(project_data.id) + return redirect_to ci_root_path, alert: 'You have to have at least master role to enable CI for this project' + end + + @project = Ci::CreateProjectService.new.execute(current_user, project_data, ci_project_url(":project_id")) + + if @project.persisted? + redirect_to ci_project_path(@project, show_guide: true), notice: 'Project was successfully created.' + else + redirect_to :back, alert: 'Cannot save project' + end + end + + def edit + end + + def update + if project.update_attributes(project_params) + Ci::EventService.new.change_project_settings(current_user, project) + + redirect_to :back, notice: 'Project was successfully updated.' + else + render action: "edit" + end + end + + def destroy + project.destroy + Ci::Network.new.disable_ci(project.gitlab_id, current_user.authenticate_options) + + Ci::EventService.new.remove_project(current_user, project) + + redirect_to ci_projects_url + end + + def build + @commit = Ci::CreateCommitService.new.execute(@project, params.dup) + + if @commit && @commit.valid? + head 201 + else + head 400 + end + end + + # Project status badge + # Image with build status for sha or ref + def badge + image = Ci::ImageForBuildService.new.execute(@project, params) + + send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" + end + + def toggle_shared_runners + project.toggle!(:shared_runners_enabled) + redirect_to :back + end + + def dumped_yaml + send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml' + end + + protected + + def project + @project ||= Ci::Project.find(params[:id]) + end + + def no_cache + response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" + end + + def project_params + params.require(:project).permit(:path, :timeout, :timeout_in_minutes, :default_ref, :always_build, + :polling_interval, :public, :ssh_url_to_repo, :allow_git_fetch, :email_recipients, + :email_add_pusher, :email_only_broken_builds, :coverage_regex, :shared_runners_enabled, :token, + { variables_attributes: [:id, :key, :value, :_destroy] }) + end + end +end diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb new file mode 100644 index 00000000000..3a52087cc6b --- /dev/null +++ b/app/controllers/ci/runner_projects_controller.rb @@ -0,0 +1,34 @@ +module Ci + class RunnerProjectsController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_manage_project! + + layout 'ci/project' + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + return head(403) unless current_user.authorized_runners.include?(@runner) + + if @runner.assign_to(project, current_user) + redirect_to ci_project_runners_path(project) + else + redirect_to ci_project_runners_path(project), alert: 'Failed adding runner to project' + end + end + + def destroy + runner_project = project.runner_projects.find(params[:id]) + runner_project.destroy + + redirect_to ci_project_runners_path(project) + end + + private + + def project + @project ||= Ci::Project.find(params[:project_id]) + end + end +end diff --git a/app/controllers/ci/runners_controller.rb b/app/controllers/ci/runners_controller.rb new file mode 100644 index 00000000000..01eebf7e6a7 --- /dev/null +++ b/app/controllers/ci/runners_controller.rb @@ -0,0 +1,71 @@ +module Ci + class RunnersController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + + layout 'ci/project' + + def index + @runners = @project.runners.order('id DESC') + @specific_runners = current_user.authorized_runners. + where.not(id: @runners).order('runners.id DESC').page(params[:page]).per(20) + @shared_runners = Ci::Runner.shared.active + @shared_runners_count = @shared_runners.count(:all) + end + + def edit + end + + def update + if @runner.update_attributes(runner_params) + redirect_to edit_ci_project_runner_path(@project, @runner), notice: 'Runner was successfully updated.' + else + redirect_to edit_ci_project_runner_path(@project, @runner), alert: 'Runner was not updated.' + end + end + + def destroy + if @runner.only_for?(@project) + @runner.destroy + end + + redirect_to ci_project_runners_path(@project) + end + + def resume + if @runner.update_attributes(active: true) + redirect_to ci_project_runners_path(@project, @runner), notice: 'Runner was successfully updated.' + else + redirect_to ci_project_runners_path(@project, @runner), alert: 'Runner was not updated.' + end + end + + def pause + if @runner.update_attributes(active: false) + redirect_to ci_project_runners_path(@project, @runner), notice: 'Runner was successfully updated.' + else + redirect_to ci_project_runners_path(@project, @runner), alert: 'Runner was not updated.' + end + end + + def show + end + + protected + + def project + @project = Ci::Project.find(params[:project_id]) + end + + def set_runner + @runner ||= @project.runners.find(params[:id]) + end + + def runner_params + params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) + end + end +end diff --git a/app/controllers/ci/services_controller.rb b/app/controllers/ci/services_controller.rb new file mode 100644 index 00000000000..e99f40f3a0a --- /dev/null +++ b/app/controllers/ci/services_controller.rb @@ -0,0 +1,59 @@ +module Ci + class ServicesController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + before_filter :service, only: [:edit, :update, :test] + + respond_to :html + + layout 'ci/project' + + def index + @project.build_missing_services + @services = @project.services.reload + end + + def edit + end + + def update + if @service.update_attributes(service_params) + redirect_to edit_ci_project_service_path(@project, @service.to_param), notice: 'Service was successfully updated.' + else + render 'edit' + end + end + + def test + last_build = @project.builds.last + + if @service.execute(last_build) + message = { notice: 'We successfully tested the service' } + else + message = { alert: 'We tried to test the service but error occurred' } + end + + redirect_to :back, message + end + + private + + def project + @project = Ci::Project.find(params[:project_id]) + end + + def service + @service ||= @project.services.find { |service| service.to_param == params[:id] } + end + + def service_params + params.require(:service).permit( + :type, :active, :webhook, :notify_only_broken_builds, + :email_recipients, :email_only_broken_builds, :email_add_pusher, + :hipchat_token, :hipchat_room, :hipchat_server + ) + end + end +end diff --git a/app/controllers/ci/triggers_controller.rb b/app/controllers/ci/triggers_controller.rb new file mode 100644 index 00000000000..6ba37cd843e --- /dev/null +++ b/app/controllers/ci/triggers_controller.rb @@ -0,0 +1,43 @@ +module Ci + class TriggersController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + + layout 'ci/project' + + def index + @triggers = @project.triggers + @trigger = Ci::Trigger.new + end + + def create + @trigger = @project.triggers.new + @trigger.save + + if @trigger.valid? + redirect_to ci_project_triggers_path(@project) + else + @triggers = @project.triggers.select(&:persisted?) + render :index + end + end + + def destroy + trigger.destroy + + redirect_to ci_project_triggers_path(@project) + end + + private + + def trigger + @trigger ||= @project.triggers.find(params[:id]) + end + + def project + @project = Ci::Project.find(params[:project_id]) + end + end +end diff --git a/app/controllers/ci/user_sessions_controller.rb b/app/controllers/ci/user_sessions_controller.rb new file mode 100644 index 00000000000..82134c1f7ba --- /dev/null +++ b/app/controllers/ci/user_sessions_controller.rb @@ -0,0 +1,65 @@ +module Ci + class UserSessionsController < Ci::ApplicationController + before_filter :authenticate_user!, except: [:new, :callback, :auth] + + def show + @user = current_user + end + + def new + end + + def auth + unless is_oauth_state_valid?(params[:state]) + redirect_to new_ci_user_sessions_path + return + end + + redirect_to client.auth_code.authorize_url({ + redirect_uri: callback_ci_user_sessions_url, + state: params[:state] + }) + end + + def callback + unless is_oauth_state_valid?(params[:state]) + redirect_to new_ci_user_sessions_path + return + end + + token = client.auth_code.get_token(params[:code], redirect_uri: callback_ci_user_sessions_url).token + + @user_session = Ci::UserSession.new + user = @user_session.authenticate(access_token: token) + + if user && sign_in(user) + return_to = get_ouath_state_return_to(params[:state]) + redirect_to(return_to || ci_root_path) + else + @error = 'Invalid credentials' + render :new + end + + end + + def destroy + sign_out + + redirect_to new_ci_user_sessions_path + end + + protected + + def client + @client ||= ::OAuth2::Client.new( + GitlabCi.config.gitlab_server.app_id, + GitlabCi.config.gitlab_server.app_secret, + { + site: GitlabCi.config.gitlab_server.url, + authorize_url: 'oauth/authorize', + token_url: 'oauth/token' + } + ) + end + end +end diff --git a/app/controllers/ci/variables_controller.rb b/app/controllers/ci/variables_controller.rb new file mode 100644 index 00000000000..6908e0877f0 --- /dev/null +++ b/app/controllers/ci/variables_controller.rb @@ -0,0 +1,33 @@ +module Ci + class VariablesController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + + layout 'ci/project' + + def show + end + + def update + if project.update_attributes(project_params) + Ci::EventService.new.change_project_settings(current_user, project) + + redirect_to ci_project_variables_path(project), notice: 'Variables were successfully updated.' + else + render action: 'show' + end + end + + private + + def project + @project ||= Ci::Project.find(params[:project_id]) + end + + def project_params + params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] }) + end + end +end diff --git a/app/controllers/ci/web_hooks_controller.rb b/app/controllers/ci/web_hooks_controller.rb new file mode 100644 index 00000000000..eea4842c91c --- /dev/null +++ b/app/controllers/ci/web_hooks_controller.rb @@ -0,0 +1,53 @@ +module Ci + class WebHooksController < Ci::ApplicationController + before_filter :authenticate_user! + before_filter :project + before_filter :authorize_access_project! + before_filter :authorize_manage_project! + + layout 'ci/project' + + def index + @web_hooks = @project.web_hooks + @web_hook = Ci::WebHook.new + end + + def create + @web_hook = @project.web_hooks.new(web_hook_params) + @web_hook.save + + if @web_hook.valid? + redirect_to ci_project_web_hooks_path(@project) + else + @web_hooks = @project.web_hooks.select(&:persisted?) + render :index + end + end + + def test + Ci::TestHookService.new.execute(hook, current_user) + + redirect_to :back + end + + def destroy + hook.destroy + + redirect_to ci_project_web_hooks_path(@project) + end + + private + + def hook + @web_hook ||= @project.web_hooks.find(params[:id]) + end + + def project + @project = Ci::Project.find(params[:project_id]) + end + + def web_hook_params + params.require(:web_hook).permit(:url) + end + end +end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index fc31118124b..4e007d2a4d0 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -1,6 +1,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::CurrentSettings - include PageLayoutHelper + include Gitlab::PageLayoutHelper before_action :verify_user_oauth_applications_enabled before_action :authenticate_user! diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 4193ac11399..08d94408fc8 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -1,5 +1,5 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController - include PageLayoutHelper + include Gitlab::PageLayoutHelper layout 'profile' diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index b181c47baec..b70e12365da 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -1,6 +1,6 @@ class Projects::NetworkController < Projects::ApplicationController include ExtractsPath - include ApplicationHelper + include Gitlab::ApplicationHelper before_action :require_non_empty_project before_action :assign_ref_vars diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 6080c849c8d..a9081a5ae16 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -1,6 +1,6 @@ class Projects::RefsController < Projects::ApplicationController include ExtractsPath - include TreeHelper + include Gitlab::TreeHelper before_action :require_non_empty_project before_action :assign_ref_vars diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 50512cb6dc3..870ff035b03 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -5,7 +5,7 @@ class Projects::WikisController < Projects::ApplicationController before_action :authorize_create_wiki!, only: [:edit, :create, :history] before_action :authorize_admin_wiki!, only: :destroy before_action :load_project_wiki - include WikiHelper + include Gitlab::WikiHelper def pages @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index eb0408a95e5..63d336b2bd5 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,5 @@ class SearchController < ApplicationController - include SearchHelper + include Gitlab::SearchHelper layout 'search' diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb deleted file mode 100644 index 14df8d4cbd7..00000000000 --- a/app/helpers/appearances_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module AppearancesHelper - def brand_item - nil - end - - def brand_title - 'GitLab Community Edition' - end - - def brand_image - nil - end - - def brand_text - nil - end - - def brand_header_logo - image_tag 'logo.svg' - end -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb deleted file mode 100644 index a803b66c502..00000000000 --- a/app/helpers/application_helper.rb +++ /dev/null @@ -1,315 +0,0 @@ -require 'digest/md5' -require 'uri' - -module ApplicationHelper - # Check if a particular controller is the current one - # - # args - One or more controller names to check - # - # Examples - # - # # On TreeController - # current_controller?(:tree) # => true - # current_controller?(:commits) # => false - # current_controller?(:commits, :tree) # => true - def current_controller?(*args) - args.any? { |v| v.to_s.downcase == controller.controller_name } - end - - # Check if a particular action is the current one - # - # args - One or more action names to check - # - # Examples - # - # # On Projects#new - # current_action?(:new) # => true - # current_action?(:create) # => false - # current_action?(:new, :create) # => true - def current_action?(*args) - args.any? { |v| v.to_s.downcase == action_name } - end - - def project_icon(project_id, options = {}) - project = - if project_id.is_a?(Project) - project = project_id - else - Project.find_with_namespace(project_id) - end - - if project.avatar_url - image_tag project.avatar_url, options - else # generated icon - project_identicon(project, options) - end - end - - def project_identicon(project, options = {}) - allowed_colors = { - red: 'FFEBEE', - purple: 'F3E5F5', - indigo: 'E8EAF6', - blue: 'E3F2FD', - teal: 'E0F2F1', - orange: 'FBE9E7', - gray: 'EEEEEE' - } - - options[:class] ||= '' - options[:class] << ' identicon' - bg_key = project.id % 7 - style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" - - content_tag(:div, class: options[:class], style: style) do - project.name[0, 1].upcase - end - end - - def avatar_icon(user_email = '', size = nil) - user = User.find_by(email: user_email) - - if user - user.avatar_url(size) || default_avatar - else - gravatar_icon(user_email, size) - end - end - - def gravatar_icon(user_email = '', size = nil) - GravatarService.new.execute(user_email, size) || - default_avatar - end - - def default_avatar - image_path('no_avatar.png') - end - - def last_commit(project) - if project.repo_exists? - time_ago_with_tooltip(project.repository.commit.committed_date) - else - 'Never' - end - rescue - 'Never' - end - - def grouped_options_refs - repository = @project.repository - - options = [ - ['Branches', repository.branch_names], - ['Tags', VersionSorter.rsort(repository.tag_names)] - ] - - # If reference is commit id - we should add it to branch/tag selectbox - if(@ref && !options.flatten.include?(@ref) && - @ref =~ /\A[0-9a-zA-Z]{6,52}\z/) - options << ['Commit', [@ref]] - end - - grouped_options_for_select(options, @ref || @project.default_branch) - end - - def emoji_autocomplete_source - # should be an array of strings - # so to_s can be called, because it is sufficient and to_json is too slow - Emoji.names.to_s - end - - # Define whenever show last push event - # with suggestion to create MR - def show_last_push_widget?(event) - # Skip if event is not about added or modified non-master branch - return false unless event && event.last_push_to_non_root? && !event.rm_ref? - - project = event.project - - # Skip if project repo is empty or MR disabled - return false unless project && !project.empty_repo? && project.merge_requests_enabled - - # Skip if user already created appropriate MR - return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? - - # Skip if user removed branch right after that - return false unless project.repository.branch_names.include?(event.branch_name) - - true - end - - def hexdigest(string) - Digest::SHA1.hexdigest string - end - - def simple_sanitize(str) - sanitize(str, tags: %w(a span)) - end - - def body_data_page - path = controller.controller_path.split('/') - namespace = path.first if path.second - - [namespace, controller.controller_name, controller.action_name].compact.join(':') - end - - # shortcut for gitlab config - def gitlab_config - Gitlab.config.gitlab - end - - # shortcut for gitlab extra config - def extra_config - Gitlab.config.extra - end - - def search_placeholder - if @project && @project.persisted? - 'Search in this project' - elsif @snippet || @snippets || @show_snippets - 'Search snippets' - elsif @group && @group.persisted? - 'Search in this group' - else - 'Search' - end - end - - def broadcast_message - BroadcastMessage.current - end - - # Render a `time` element with Javascript-based relative date and tooltip - # - # time - Time object - # placement - Tooltip placement String (default: "top") - # html_class - Custom class for `time` element (default: "time_ago") - # skip_js - When true, exclude the `script` tag (default: false) - # - # By default also includes a `script` element with Javascript necessary to - # initialize the `timeago` jQuery extension. If this method is called many - # times, for example rendering hundreds of commits, it's advisable to disable - # this behavior using the `skip_js` argument and re-initializing `timeago` - # manually once all of the elements have been rendered. - # - # A `js-timeago` class is always added to the element, even when a custom - # `html_class` argument is provided. - # - # Returns an HTML-safe String - def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) - element = content_tag :time, time.to_s, - class: "#{html_class} js-timeago", - datetime: time.getutc.iso8601, - title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), - data: { toggle: 'tooltip', placement: placement } - - element += javascript_tag "$('.js-timeago').timeago()" unless skip_js - - element - end - - def render_markup(file_name, file_content) - if gitlab_markdown?(file_name) - Haml::Helpers.preserve(markdown(file_content)) - elsif asciidoc?(file_name) - asciidoc(file_content) - elsif plain?(file_name) - content_tag :pre, class: 'plain-readme' do - file_content - end - else - GitHub::Markup.render(file_name, file_content). - force_encoding(file_content.encoding).html_safe - end - rescue RuntimeError - simple_format(file_content) - end - - def plain?(filename) - Gitlab::MarkupHelper.plain?(filename) - end - - def markup?(filename) - Gitlab::MarkupHelper.markup?(filename) - end - - def gitlab_markdown?(filename) - Gitlab::MarkupHelper.gitlab_markdown?(filename) - end - - def asciidoc?(filename) - Gitlab::MarkupHelper.asciidoc?(filename) - end - - def promo_host - 'about.gitlab.com' - end - - def promo_url - 'https://' + promo_host - end - - def page_filter_path(options = {}) - without = options.delete(:without) - - exist_opts = { - state: params[:state], - scope: params[:scope], - label_name: params[:label_name], - milestone_id: params[:milestone_id], - assignee_id: params[:assignee_id], - author_id: params[:author_id], - sort: params[:sort], - } - - options = exist_opts.merge(options) - - if without.present? - without.each do |key| - options.delete(key) - end - end - - path = request.path - path << "?#{options.to_param}" - path - end - - def outdated_browser? - browser.ie? && browser.version.to_i < 10 - end - - def path_to_key(key, admin = false) - if admin - admin_user_key_path(@user, key) - else - profile_key_path(key) - end - end - - def state_filters_text_for(entity, project) - titles = { - opened: "Open" - } - - entity_title = titles[entity] || entity.to_s.humanize - - count = - if project.nil? - nil - elsif current_controller?(:issues) - project.issues.send(entity).count - elsif current_controller?(:merge_requests) - project.merge_requests.send(entity).count - end - - html = content_tag :span, entity_title - - if count.present? - html += " " - html += content_tag :span, number_with_delimiter(count), class: 'badge' - end - - html.html_safe - end -end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb deleted file mode 100644 index 7d6b58ee21a..00000000000 --- a/app/helpers/application_settings_helper.rb +++ /dev/null @@ -1,59 +0,0 @@ -module ApplicationSettingsHelper - def gravatar_enabled? - current_application_settings.gravatar_enabled? - end - - def twitter_sharing_enabled? - current_application_settings.twitter_sharing_enabled? - end - - def signup_enabled? - current_application_settings.signup_enabled? - end - - def signin_enabled? - current_application_settings.signin_enabled? - end - - def extra_sign_in_text - current_application_settings.sign_in_text - end - - def user_oauth_applications? - current_application_settings.user_oauth_applications - end - - # Return a group of checkboxes that use Bootstrap's button plugin for a - # toggle button effect. - def restricted_level_checkboxes(help_block_id) - Gitlab::VisibilityLevel.options.map do |name, level| - checked = restricted_visibility_levels(true).include?(level) - css_class = 'btn' - css_class += ' active' if checked - checkbox_name = 'application_setting[restricted_visibility_levels][]' - - label_tag(checkbox_name, class: css_class) do - check_box_tag(checkbox_name, level, checked, - autocomplete: 'off', - 'aria-describedby' => help_block_id) + name - end - end - end - - # Return a group of checkboxes that use Bootstrap's button plugin for a - # toggle button effect. - def import_sources_checkboxes(help_block_id) - Gitlab::ImportSources.options.map do |name, source| - checked = current_application_settings.import_sources.include?(source) - css_class = 'btn' - css_class += ' active' if checked - checkbox_name = 'application_setting[import_sources][]' - - label_tag(checkbox_name, class: css_class) do - check_box_tag(checkbox_name, source, checked, - autocomplete: 'off', - 'aria-describedby' => help_block_id) + name - end - end - end -end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb deleted file mode 100644 index 0e7a37b4cc6..00000000000 --- a/app/helpers/auth_helper.rb +++ /dev/null @@ -1,50 +0,0 @@ -module AuthHelper - PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze - FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze - - def ldap_enabled? - Gitlab.config.ldap.enabled - end - - def provider_has_icon?(name) - PROVIDERS_WITH_ICONS.include?(name.to_s) - end - - def auth_providers - Gitlab::OAuth::Provider.providers - end - - def label_for_provider(name) - Gitlab::OAuth::Provider.label_for(name) - end - - def form_based_provider?(name) - FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s } - end - - def form_based_providers - auth_providers.select { |provider| form_based_provider?(provider) } - end - - def button_based_providers - auth_providers.reject { |provider| form_based_provider?(provider) } - end - - def provider_image_tag(provider, size = 64) - label = label_for_provider(provider) - - if provider_has_icon?(provider) - file_name = "#{provider.to_s.split('_').first}_#{size}.png" - - image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}") - else - label - end - end - - def auth_active?(provider) - current_user.identities.exists?(provider: provider.to_s) - end - - extend self -end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb deleted file mode 100644 index 77d99140c43..00000000000 --- a/app/helpers/blob_helper.rb +++ /dev/null @@ -1,74 +0,0 @@ -module BlobHelper - def highlight(blob_name, blob_content, nowrap: false, continue: false) - @formatter ||= Rouge::Formatters::HTMLGitlab.new( - nowrap: nowrap, - cssclass: 'code highlight', - lineanchors: true, - lineanchorsid: 'LC' - ) - - begin - @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new - result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe - rescue - @lexer = Rouge::Lexers::PlainText - result = @formatter.format(@lexer.lex(blob_content)).html_safe - end - - result - end - - def no_highlight_files - %w(credits changelog news copying copyright license authors) - end - - def edit_blob_link(project, ref, path, options = {}) - blob = - begin - project.repository.blob_at(ref, path) - rescue - nil - end - - if blob && blob.text? - text = 'Edit' - after = options[:after] || '' - from_mr = options[:from_merge_request_id] - link_opts = {} - link_opts[:from_merge_request_id] = from_mr if from_mr - cls = 'btn btn-small' - if allowed_tree_edit?(project, ref) - link_to(text, - namespace_project_edit_blob_path(project.namespace, project, - tree_join(ref, path), - link_opts), - class: cls - ) - else - content_tag :span, text, class: cls + ' disabled' - end + after.html_safe - else - '' - end - end - - def leave_edit_message - "Leave edit mode?\nAll unsaved changes will be lost." - end - - def editing_preview_title(filename) - if Gitlab::MarkupHelper.previewable?(filename) - 'Preview' - else - 'Preview changes' - end - end - - # Return an image icon depending on the file mode and extension - # - # mode - File unix mode - # mode - File name - def blob_icon(mode, name) - icon("#{file_type_icon_class('file', mode, name)} fw") - end -end diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb deleted file mode 100644 index d6eaa7d57bc..00000000000 --- a/app/helpers/branches_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module BranchesHelper - def can_remove_branch?(project, branch_name) - if project.protected_branch? branch_name - false - elsif branch_name == project.repository.root_ref - false - else - can?(current_user, :push_code, project) - end - end - - def can_push_branch?(project, branch_name) - return false unless project.repository.branch_names.include?(branch_name) - - ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) - end -end diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb deleted file mode 100644 index 6484dca6b55..00000000000 --- a/app/helpers/broadcast_messages_helper.rb +++ /dev/null @@ -1,16 +0,0 @@ -module BroadcastMessagesHelper - def broadcast_styling(broadcast_message) - styling = '' - - if broadcast_message.color.present? - styling << "background-color: #{broadcast_message.color}" - styling << '; ' if broadcast_message.font.present? - end - - if broadcast_message.font.present? - styling << "color: #{broadcast_message.font}" - end - - styling - end -end diff --git a/app/helpers/ci/application_helper.rb b/app/helpers/ci/application_helper.rb new file mode 100644 index 00000000000..3198fe55f91 --- /dev/null +++ b/app/helpers/ci/application_helper.rb @@ -0,0 +1,140 @@ +module Ci + module ApplicationHelper + def loader_html + image_tag 'ci/loader.gif', alt: 'Loading' + end + + # Navigation link helper + # + # Returns an `li` element with an 'active' class if the supplied + # controller(s) and/or action(s) are currently active. The content of the + # element is the value passed to the block. + # + # options - The options hash used to determine if the element is "active" (default: {}) + # :controller - One or more controller names to check (optional). + # :action - One or more action names to check (optional). + # :path - A shorthand path, such as 'dashboard#index', to check (optional). + # :html_options - Extra options to be passed to the list element (optional). + # block - An optional block that will become the contents of the returned + # `li` element. + # + # When both :controller and :action are specified, BOTH must match in order + # to be marked as active. When only one is given, either can match. + # + # Examples + # + # # Assuming we're on TreeController#show + # + # # Controller matches, but action doesn't + # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Controller matches + # nav_link(controller: [:tree, :refs]) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Shorthand path + # nav_link(path: 'tree#show') { "Hello" } + # # => '
  • Hello
  • ' + # + # # Supplying custom options for the list element + # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } + # # => '
  • Hello
  • ' + # + # Returns a list item element String + def nav_link(options = {}, &block) + if path = options.delete(:path) + if path.respond_to?(:each) + c = path.map { |p| p.split('#').first } + a = path.map { |p| p.split('#').last } + else + c, a, _ = path.split('#') + end + else + c = options.delete(:controller) + a = options.delete(:action) + end + + if c && a + # When given both options, make sure BOTH are active + klass = current_controller?(*c) && current_action?(*a) ? 'active' : '' + else + # Otherwise check EITHER option + klass = current_controller?(*c) || current_action?(*a) ? 'active' : '' + end + + # Add our custom class into the html_options, which may or may not exist + # and which may or may not already have a :class key + o = options.delete(:html_options) || {} + o[:class] ||= '' + o[:class] += ' ' + klass + o[:class].strip! + + if block_given? + content_tag(:li, capture(&block), o) + else + content_tag(:li, nil, o) + end + end + + # Check if a particular controller is the current one + # + # args - One or more controller names to check + # + # Examples + # + # # On TreeController + # current_controller?(:tree) # => true + # current_controller?(:commits) # => false + # current_controller?(:commits, :tree) # => true + def current_controller?(*args) + args.any? { |v| v.to_s.downcase == controller.controller_name } + end + + # Check if a particular action is the current one + # + # args - One or more action names to check + # + # Examples + # + # # On Projects#new + # current_action?(:new) # => true + # current_action?(:create) # => false + # current_action?(:new, :create) # => true + def current_action?(*args) + args.any? { |v| v.to_s.downcase == action_name } + end + + def date_from_to(from, to) + "#{from.to_s(:short)} - #{to.to_s(:short)}" + end + + def body_data_page + path = controller.controller_path.split('/') + namespace = path.first if path.second + + [namespace, controller.controller_name, controller.action_name].compact.join(":") + end + + def duration_in_words(finished_at, started_at) + if finished_at && started_at + interval_in_seconds = finished_at.to_i - started_at.to_i + elsif started_at + interval_in_seconds = Time.now.to_i - started_at.to_i + end + + time_interval_in_words(interval_in_seconds) + end + + def time_interval_in_words(interval_in_seconds) + minutes = interval_in_seconds / 60 + seconds = interval_in_seconds - minutes * 60 + + if minutes >= 1 + "#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}" + else + "#{pluralize(seconds, "second")}" + end + end + end +end diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb new file mode 100644 index 00000000000..cdabdad17d2 --- /dev/null +++ b/app/helpers/ci/builds_helper.rb @@ -0,0 +1,41 @@ +module Ci + module BuildsHelper + def build_ref_link build + gitlab_ref_link build.project, build.ref + end + + def build_compare_link build + gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha + end + + def build_commit_link build + gitlab_commit_link build.project, build.short_sha + end + + def build_url(build) + ci_project_build_url(build.project, build) + end + + def build_status_alert_class(build) + if build.success? + 'alert-success' + elsif build.failed? + 'alert-danger' + elsif build.canceled? + 'alert-disabled' + else + 'alert-warning' + end + end + + def build_icon_css_class(build) + if build.success? + 'fa-circle cgreen' + elsif build.failed? + 'fa-circle cred' + else + 'fa-circle light' + end + end + end +end diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb new file mode 100644 index 00000000000..0479bc10594 --- /dev/null +++ b/app/helpers/ci/commits_helper.rb @@ -0,0 +1,26 @@ +module Ci + module CommitsHelper + def commit_status_alert_class(commit) + return unless commit + + case commit.status + when 'success' + 'alert-success' + when 'failed', 'canceled' + 'alert-danger' + when 'skipped' + 'alert-disabled' + else + 'alert-warning' + end + end + + def commit_link(commit) + link_to(commit.short_sha, ci_project_ref_commit_path(commit.project, commit.ref, commit.sha)) + end + + def truncate_first_line(message, length = 50) + truncate(message.each_line.first.chomp, length: length) if message + end + end +end diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb new file mode 100644 index 00000000000..2b89a0ce93e --- /dev/null +++ b/app/helpers/ci/gitlab_helper.rb @@ -0,0 +1,36 @@ +module Ci + module GitlabHelper + def no_turbolink + { :"data-no-turbolink" => "data-no-turbolink" } + end + + def gitlab_ref_link project, ref + gitlab_url = project.gitlab_url.dup + gitlab_url << "/commits/#{ref}" + link_to ref, gitlab_url, no_turbolink + end + + def gitlab_compare_link project, before, after + gitlab_url = project.gitlab_url.dup + gitlab_url << "/compare/#{before}...#{after}" + + link_to "#{before}...#{after}", gitlab_url, no_turbolink + end + + def gitlab_commit_link project, sha + gitlab_url = project.gitlab_url.dup + gitlab_url << "/commit/#{sha}" + link_to Ci::Commit.truncate_sha(sha), gitlab_url, no_turbolink + end + + def yaml_web_editor_link(project) + commits = project.commits + + if commits.any? && commits.last.push_data[:ci_yaml_file] + "#{@project.gitlab_url}/edit/master/.gitlab-ci.yml" + else + "#{@project.gitlab_url}/new/master" + end + end + end +end diff --git a/app/helpers/ci/icons_helper.rb b/app/helpers/ci/icons_helper.rb new file mode 100644 index 00000000000..ecb6ef7be45 --- /dev/null +++ b/app/helpers/ci/icons_helper.rb @@ -0,0 +1,11 @@ +module Ci + module IconsHelper + def boolean_to_icon(value) + if value.to_s == "true" + content_tag :i, nil, class: 'fa-circle cgreen' + else + content_tag :i, nil, class: 'fa-power-off clgray' + end + end + end +end diff --git a/app/helpers/ci/projects_helper.rb b/app/helpers/ci/projects_helper.rb new file mode 100644 index 00000000000..fd991a4165a --- /dev/null +++ b/app/helpers/ci/projects_helper.rb @@ -0,0 +1,36 @@ +module Ci + module ProjectsHelper + def ref_tab_class ref = nil + 'active' if ref == @ref + end + + def success_ratio(success_builds, failed_builds) + failed_builds = failed_builds.count(:all) + success_builds = success_builds.count(:all) + + return 100 if failed_builds.zero? + + ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100 + ratio.to_i + end + + def markdown_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + "[![build status](#{url})](#{ci_project_url(project, ref: ref)})" + end + + def html_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + "" + end + + def project_uses_specific_runner?(project) + project.runners.any? + end + + def no_runners_for_project?(project) + project.runners.blank? && + Ci::Runner.shared.blank? + end + end +end diff --git a/app/helpers/ci/routes_helper.rb b/app/helpers/ci/routes_helper.rb new file mode 100644 index 00000000000..f22d5023db5 --- /dev/null +++ b/app/helpers/ci/routes_helper.rb @@ -0,0 +1,29 @@ +module Ci + module RoutesHelper + class Base + include Gitlab::Application.routes.url_helpers + + def default_url_options + { + host: Ci::Settings.gitlab_ci['host'], + protocol: Ci::Settings.gitlab_ci['https'] ? "https" : "http", + port: Ci::Settings.gitlab_ci['port'] + } + end + end + + def url_helpers + @url_helpers ||= Ci::Base.new + end + + def self.method_missing(method, *args, &block) + @url_helpers ||= Ci::Base.new + + if @url_helpers.respond_to?(method) + @url_helpers.send(method, *args, &block) + else + super method, *args, &block + end + end + end +end diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb new file mode 100644 index 00000000000..782208ddfe4 --- /dev/null +++ b/app/helpers/ci/runners_helper.rb @@ -0,0 +1,22 @@ +module Ci + module RunnersHelper + def runner_status_icon(runner) + unless runner.contacted_at + return content_tag :i, nil, + class: "fa-warning-sign", + title: "New runner. Has not connected yet" + end + + status = + if runner.active? + runner.contacted_at > 3.hour.ago ? :online : :offline + else + :paused + end + + content_tag :i, nil, + class: "fa-circle runner-status-#{status}", + title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" + end + end +end diff --git a/app/helpers/ci/triggers_helper.rb b/app/helpers/ci/triggers_helper.rb new file mode 100644 index 00000000000..caff54c3520 --- /dev/null +++ b/app/helpers/ci/triggers_helper.rb @@ -0,0 +1,7 @@ +module Ci + module TriggersHelper + def build_trigger_url(project_id, ref_name) + "#{Ci::Settings.gitlab_ci.url}/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" + end + end +end diff --git a/app/helpers/ci/user_helper.rb b/app/helpers/ci/user_helper.rb new file mode 100644 index 00000000000..c332d6ed9cf --- /dev/null +++ b/app/helpers/ci/user_helper.rb @@ -0,0 +1,15 @@ +module Ci + module UserHelper + def user_avatar_url(user = nil, size = nil, default = 'identicon') + size = 40 if size.nil? || size <= 0 + + if user.blank? || user.avatar_url.blank? + 'ci/no_avatar.png' + elsif /^(http(s?):\/\/(www|secure)\.gravatar\.com\/avatar\/(\w*))/ =~ user.avatar_url + Regexp.last_match[0] + "?s=#{size}&d=#{default}" + else + user.avatar_url + end + end + end +end diff --git a/app/helpers/ci/user_sessions_helper.rb b/app/helpers/ci/user_sessions_helper.rb new file mode 100644 index 00000000000..0296a74395c --- /dev/null +++ b/app/helpers/ci/user_sessions_helper.rb @@ -0,0 +1,32 @@ +module Ci + module UserSessionsHelper + def generate_oauth_salt + SecureRandom.hex(16) + end + + def generate_oauth_hmac(salt, return_to) + return unless return_to + digest = OpenSSL::Digest.new('sha256') + key = Gitlab::Application.secrets.db_key_base + salt + OpenSSL::HMAC.hexdigest(digest, key, return_to) + end + + def generate_oauth_state(return_to) + return unless return_to + salt = generate_oauth_salt + hmac = generate_oauth_hmac(salt, return_to) + "#{salt}:#{hmac}:#{return_to}" + end + + def get_ouath_state_return_to(state) + state.split(':', 3)[2] if state + end + + def is_oauth_state_valid?(state) + return true unless state + salt, hmac, return_to = state.split(':', 3) + return false unless return_to + hmac == generate_oauth_hmac(salt, return_to) + end + end +end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb deleted file mode 100644 index d13d80be293..00000000000 --- a/app/helpers/commits_helper.rb +++ /dev/null @@ -1,183 +0,0 @@ -# encoding: utf-8 -module CommitsHelper - # Returns a link to the commit author. If the author has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the author email as specified in the commit. - # - # options: - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_author_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :author)) - end - - # Just like #author_link but for the committer. - def commit_committer_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :committer)) - end - - def image_diff_class(diff) - if diff.deleted_file - "deleted" - elsif diff.new_file - "added" - else - nil - end - end - - def commit_to_html(commit, project, inline = true) - template = inline ? "inline_commit" : "commit" - escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? - end - - # Breadcrumb links for a Project and, if applicable, a tree path - def commits_breadcrumbs - return unless @project && @ref - - # Add the root project link and the arrow icon - crumbs = content_tag(:li) do - link_to( - @project.path, - namespace_project_commits_path(@project.namespace, @project, @ref) - ) - end - - if @path - parts = @path.split('/') - - parts.each_with_index do |part, i| - crumbs << content_tag(:li) do - # The text is just the individual part, but the link needs all the parts before it - link_to( - part, - namespace_project_commits_path( - @project.namespace, - @project, - tree_join(@ref, parts[0..i].join('/')) - ) - ) - end - end - end - - crumbs.html_safe - end - - # Return Project default branch, if it present in array - # Else - first branch in array (mb last actual branch) - def commit_default_branch(project, branches) - branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop - end - - # Returns the sorted alphabetically links to branches, separated by a comma - def commit_branches_links(project, branches) - branches.sort.map do |branch| - link_to( - namespace_project_tree_path(project.namespace, project, branch) - ) do - content_tag :span, class: 'label label-gray' do - icon('code-fork') + ' ' + branch - end - end - end.join(" ").html_safe - end - - # Returns the sorted links to tags, separated by a comma - def commit_tags_links(project, tags) - sorted = VersionSorter.rsort(tags) - sorted.map do |tag| - link_to( - namespace_project_commits_path(project.namespace, project, - project.repository.find_tag(tag).name) - ) do - content_tag :span, class: 'label label-gray' do - icon('tag') + ' ' + tag - end - end - end.join(" ").html_safe - end - - def link_to_browse_code(project, commit) - if current_controller?(:projects, :commits) - if @repo.blob_at(commit.id, @path) - return link_to( - "Browse File »", - namespace_project_blob_path(project.namespace, project, - tree_join(commit.id, @path)), - class: "pull-right" - ) - elsif @path.present? - return link_to( - "Browse Dir »", - namespace_project_tree_path(project.namespace, project, - tree_join(commit.id, @path)), - class: "pull-right" - ) - end - end - link_to( - "Browse Code »", - namespace_project_tree_path(project.namespace, project, commit), - class: "pull-right" - ) - end - - protected - - # Private: Returns a link to a person. If the person has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the person email as specified in the commit. - # - # options: - # source: one of :author or :committer - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_person_link(commit, options = {}) - user = commit.send(options[:source]) - - source_name = clean(commit.send "#{options[:source]}_name".to_sym) - source_email = clean(commit.send "#{options[:source]}_email".to_sym) - - person_name = user.try(:name) || source_name - person_email = user.try(:email) || source_email - - text = - if options[:avatar] - avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") - %Q{#{avatar} #{person_name}} - else - person_name - end - - options = { - class: "commit-#{options[:source]}-link has_tooltip", - data: { :'original-title' => sanitize(source_email) } - } - - if user.nil? - mail_to(source_email, text.html_safe, options) - else - link_to(text.html_safe, user_path(user), options) - end - end - - def view_file_btn(commit_sha, diff, project) - link_to( - namespace_project_blob_path(project.namespace, project, - tree_join(commit_sha, diff.new_path)), - class: 'btn btn-small view-file js-view-file' - ) do - raw('View file @') + content_tag(:span, commit_sha[0..6], - class: 'commit-short-id') - end - end - - def truncate_sha(sha) - Commit.truncate_sha(sha) - end - - def clean(string) - Sanitize.clean(string, remove_contents: true) - end -end diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb deleted file mode 100644 index f1dc906cab4..00000000000 --- a/app/helpers/compare_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module CompareHelper - def create_mr_button?(from = params[:from], to = params[:to], project = @project) - from.present? && - to.present? && - from != to && - project.merge_requests_enabled && - project.repository.branch_names.include?(from) && - project.repository.branch_names.include?(to) - end - - def create_mr_path(from = params[:from], to = params[:to], project = @project) - new_namespace_project_merge_request_path( - project.namespace, - project, - merge_request: { - source_branch: to, - target_branch: from - } - ) - end -end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb deleted file mode 100644 index c25b54eadc6..00000000000 --- a/app/helpers/dashboard_helper.rb +++ /dev/null @@ -1,9 +0,0 @@ -module DashboardHelper - def assigned_issues_dashboard_path - issues_dashboard_path(assignee_id: current_user.id) - end - - def assigned_mrs_dashboard_path - merge_requests_dashboard_path(assignee_id: current_user.id) - end -end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb deleted file mode 100644 index 1bd3ec5e0e0..00000000000 --- a/app/helpers/diff_helper.rb +++ /dev/null @@ -1,170 +0,0 @@ -module DiffHelper - def allowed_diff_size - if diff_hard_limit_enabled? - Commit::DIFF_HARD_LIMIT_FILES - else - Commit::DIFF_SAFE_FILES - end - end - - def allowed_diff_lines - if diff_hard_limit_enabled? - Commit::DIFF_HARD_LIMIT_LINES - else - Commit::DIFF_SAFE_LINES - end - end - - def safe_diff_files(diffs) - lines = 0 - safe_files = [] - diffs.first(allowed_diff_size).each do |diff| - lines += diff.diff.lines.count - break if lines > allowed_diff_lines - safe_files << Gitlab::Diff::File.new(diff) - end - safe_files - end - - def diff_hard_limit_enabled? - # Enabling hard limit allows user to see more diff information - if params[:force_show_diff].present? - true - else - false - end - end - - def generate_line_code(file_path, line) - Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) - end - - def parallel_diff(diff_file, index) - lines = [] - skip_next = false - - # Building array of lines - # - # [ - # left_type, left_line_number, left_line_content, left_line_code, - # right_line_type, right_line_number, right_line_content, right_line_code - # ] - # - diff_file.diff_lines.each do |line| - - full_line = line.text - type = line.type - line_code = generate_line_code(diff_file.file_path, line) - line_new = line.new_pos - line_old = line.old_pos - - next_line = diff_file.next_line(line.index) - - if next_line - next_line_code = generate_line_code(diff_file.file_path, next_line) - next_type = next_line.type - next_line = next_line.text - end - - if type == 'match' || type.nil? - # line in the right panel is the same as in the left one - line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code] - lines.push(line) - elsif type == 'old' - if next_type == 'new' - # Left side has text removed, right side has text added - line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code] - lines.push(line) - skip_next = true - elsif next_type == 'old' || next_type.nil? - # Left side has text removed, right side doesn't have any change - # No next line code, no new line number, no new line text - line = [type, line_old, full_line, line_code, next_type, nil, " ", nil] - lines.push(line) - end - elsif type == 'new' - if skip_next - # Change has been already included in previous line so no need to do it again - skip_next = false - next - else - # Change is only on the right side, left side has no change - line = [nil, nil, " ", line_code, type, line_new, full_line, line_code] - lines.push(line) - end - end - end - lines - end - - def unfold_bottom_class(bottom) - (bottom) ? 'js-unfold-bottom' : '' - end - - def unfold_class(unfold) - (unfold) ? 'unfold js-unfold' : '' - end - - def diff_line_content(line) - if line.blank? - "  " - else - line - end - end - - def line_comments - @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code) - end - - def organize_comments(type_left, type_right, line_code_left, line_code_right) - comments_left = comments_right = nil - - unless type_left.nil? && type_right == 'new' - comments_left = line_comments[line_code_left] - end - - unless type_left.nil? && type_right.nil? - comments_right = line_comments[line_code_right] - end - - [comments_left, comments_right] - end - - def inline_diff_btn - params_copy = params.dup - params_copy[:view] = 'inline' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do - 'Inline' - end - end - - def parallel_diff_btn - params_copy = params.dup - params_copy[:view] = 'parallel' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do - 'Side-by-side' - end - end - - def submodule_link(blob, ref, repository = @repository) - tree, commit = submodule_links(blob, ref, repository) - commit_id = if commit.nil? - blob.id[0..10] - else - link_to "#{blob.id[0..10]}", commit - end - - [ - content_tag(:span, link_to(truncate(blob.name, length: 40), tree)), - '@', - content_tag(:span, commit_id, class: 'monospace'), - ].join(' ').html_safe - end -end diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb deleted file mode 100644 index 45788ba95ac..00000000000 --- a/app/helpers/emails_helper.rb +++ /dev/null @@ -1,57 +0,0 @@ -module EmailsHelper - - # Google Actions - # https://developers.google.com/gmail/markup/reference/go-to-action - def email_action(url) - name = action_title(url) - if name - data = { - "@context" => "http://schema.org", - "@type" => "EmailMessage", - "action" => { - "@type" => "ViewAction", - "name" => name, - "url" => url, - } - } - - content_tag :script, type: 'application/ld+json' do - data.to_json.html_safe - end - end - end - - def action_title(url) - return unless url - ["merge_requests", "issues", "commit"].each do |action| - if url.split("/").include?(action) - return "View #{action.humanize.singularize}" - end - end - end - - def color_email_diff(diffcontent) - formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github') - lexer = Rouge::Lexers::Diff - raw formatter.format(lexer.lex(diffcontent)) - end - - def password_reset_token_valid_time - valid_hours = Devise.reset_password_within / 60 / 60 - if valid_hours >= 24 - unit = 'day' - valid_length = (valid_hours / 24).floor - else - unit = 'hour' - valid_length = valid_hours.floor - end - - pluralize(valid_length, unit) - end - - def reset_token_expire_message - link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email)) - msg = "This link is valid for #{password_reset_token_valid_time}. " - msg << "After it expires, you can #{link_tag}." - end -end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb deleted file mode 100644 index 8428281f8f6..00000000000 --- a/app/helpers/events_helper.rb +++ /dev/null @@ -1,203 +0,0 @@ -module EventsHelper - def link_to_author(event) - author = event.author - - if author - link_to author.name, user_path(author.username) - else - event.author_name - end - end - - def event_action_name(event) - target = if event.target_type - if event.note? - event.note_target_type - else - event.target_type.titleize.downcase - end - else - 'project' - end - - [event.action_name, target].join(" ") - end - - def event_filter_link(key, tooltip) - key = key.to_s - active = 'active' if @event_filter.active?(key) - link_opts = { - class: 'event_filter_link', - id: "#{key}_event_filter", - title: "Filter by #{tooltip.downcase}", - data: { toggle: 'tooltip', placement: 'top' } - } - - content_tag :li, class: "filter_icon #{active}" do - link_to request.path, link_opts do - icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip) - end - end - end - - def icon_for_event - { - EventFilter.push => 'upload', - EventFilter.merged => 'check-square-o', - EventFilter.comments => 'comments', - EventFilter.team => 'user', - } - end - - def event_feed_title(event) - words = [] - words << event.author_name - words << event_action_name(event) - - if event.push? - words << event.ref_type - words << event.ref_name - words << "at" - elsif event.commented? - if event.note_commit? - words << event.note_short_commit_id - else - words << "##{truncate event.note_target_iid}" - end - words << "at" - elsif event.target - words << "##{event.target_iid}:" - words << event.target.title if event.target.respond_to?(:title) - words << "at" - end - - words << event.project_name - - words.join(" ") - end - - def event_feed_url(event) - if event.issue? - namespace_project_issue_url(event.project.namespace, event.project, - event.issue) - elsif event.merge_request? - namespace_project_merge_request_url(event.project.namespace, - event.project, event.merge_request) - elsif event.note? && event.note_commit? - namespace_project_commit_url(event.project.namespace, event.project, - event.note_target) - elsif event.note? - if event.note_target - if event.note_commit? - namespace_project_commit_path(event.project.namespace, event.project, - event.note_commit_id, - anchor: dom_id(event.target)) - elsif event.note_project_snippet? - namespace_project_snippet_path(event.project.namespace, - event.project, event.note_target) - else - event_note_target_path(event) - end - end - elsif event.push? - if event.push_with_commits? && event.md_ref? - if event.commits_count > 1 - namespace_project_compare_url(event.project.namespace, event.project, - from: event.commit_from, to: - event.commit_to) - else - namespace_project_commit_url(event.project.namespace, event.project, - id: event.commit_to) - end - else - namespace_project_commits_url(event.project.namespace, event.project, - event.ref_name) - end - end - end - - def event_feed_summary(event) - if event.issue? - render "events/event_issue", issue: event.issue - elsif event.push? - render "events/event_push", event: event - elsif event.merge_request? - render "events/event_merge_request", merge_request: event.merge_request - elsif event.note? - render "events/event_note", note: event.note - end - end - - def event_note_target_path(event) - if event.note? && event.note_commit? - namespace_project_commit_path(event.project.namespace, event.project, - event.note_target) - else - polymorphic_path([event.project.namespace.becomes(Namespace), - event.project, event.note_target], - anchor: dom_id(event.target)) - end - end - - def event_note_title_html(event) - if event.note_target - if event.note_commit? - link_to( - namespace_project_commit_path(event.project.namespace, event.project, - event.note_commit_id, - anchor: dom_id(event.target)), - class: "commit_short_id" - ) do - "#{event.note_target_type} #{event.note_short_commit_id}" - end - elsif event.note_project_snippet? - link_to(namespace_project_snippet_path(event.project.namespace, - event.project, - event.note_target)) do - "#{event.note_target_type} ##{truncate event.note_target_id}" - end - else - link_to event_note_target_path(event) do - "#{event.note_target_type} ##{truncate event.note_target_iid}" - end - end - else - content_tag :strong do - "(deleted)" - end - end - end - - def event_note(text, options = {}) - text = first_line_in_markdown(text, 150, options) - sanitize(text, tags: %w(a img b pre code p span)) - end - - def event_commit_title(message) - escape_once(truncate(message.split("\n").first, length: 70)) - rescue - "--broken encoding" - end - - def event_to_atom(xml, event) - if event.proper? - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - event_summary = event_feed_summary(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link href: event_link - xml.title truncate(event_title, length: 80) - xml.updated event.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - - xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } - end - end - end -end diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb deleted file mode 100644 index 0d291f9a87e..00000000000 --- a/app/helpers/explore_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ExploreHelper - def explore_projects_filter_path(options={}) - exist_opts = { - sort: params[:sort], - scope: params[:scope], - group: params[:group], - tag: params[:tag], - visibility_level: params[:visibility_level], - } - - options = exist_opts.merge(options) - - path = explore_projects_path - path << "?#{options.to_param}" - path - end -end diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb deleted file mode 100644 index 838b85afdfe..00000000000 --- a/app/helpers/external_wiki_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ExternalWikiHelper - def get_project_wiki_path(project) - external_wiki_service = project.services. - select { |service| service.to_param == 'external_wiki' }.first - if external_wiki_service.present? && external_wiki_service.active? - external_wiki_service.properties['external_wiki_url'] - else - namespace_project_wiki_path(project.namespace, project, :home) - end - end -end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb deleted file mode 100644 index 09684955233..00000000000 --- a/app/helpers/git_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module GitHelper - def strip_gpg_signature(text) - text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") - end -end diff --git a/app/helpers/gitlab/appearances_helper.rb b/app/helpers/gitlab/appearances_helper.rb new file mode 100644 index 00000000000..54cafcd9e40 --- /dev/null +++ b/app/helpers/gitlab/appearances_helper.rb @@ -0,0 +1,23 @@ +module Gitlab + module AppearancesHelper + def brand_item + nil + end + + def brand_title + 'GitLab Community Edition' + end + + def brand_image + nil + end + + def brand_text + nil + end + + def brand_header_logo + image_tag 'logo.svg' + end + end +end diff --git a/app/helpers/gitlab/application_helper.rb b/app/helpers/gitlab/application_helper.rb new file mode 100644 index 00000000000..b019ffa5fe2 --- /dev/null +++ b/app/helpers/gitlab/application_helper.rb @@ -0,0 +1,317 @@ +require 'digest/md5' +require 'uri' + +module Gitlab + module ApplicationHelper + # Check if a particular controller is the current one + # + # args - One or more controller names to check + # + # Examples + # + # # On TreeController + # current_controller?(:tree) # => true + # current_controller?(:commits) # => false + # current_controller?(:commits, :tree) # => true + def current_controller?(*args) + args.any? { |v| v.to_s.downcase == controller.controller_name } + end + + # Check if a particular action is the current one + # + # args - One or more action names to check + # + # Examples + # + # # On Projects#new + # current_action?(:new) # => true + # current_action?(:create) # => false + # current_action?(:new, :create) # => true + def current_action?(*args) + args.any? { |v| v.to_s.downcase == action_name } + end + + def project_icon(project_id, options = {}) + project = + if project_id.is_a?(Project) + project = project_id + else + Project.find_with_namespace(project_id) + end + + if project.avatar_url + image_tag project.avatar_url, options + else # generated icon + project_identicon(project, options) + end + end + + def project_identicon(project, options = {}) + allowed_colors = { + red: 'FFEBEE', + purple: 'F3E5F5', + indigo: 'E8EAF6', + blue: 'E3F2FD', + teal: 'E0F2F1', + orange: 'FBE9E7', + gray: 'EEEEEE' + } + + options[:class] ||= '' + options[:class] << ' identicon' + bg_key = project.id % 7 + style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" + + content_tag(:div, class: options[:class], style: style) do + project.name[0, 1].upcase + end + end + + def avatar_icon(user_email = '', size = nil) + user = User.find_by(email: user_email) + + if user + user.avatar_url(size) || default_avatar + else + gravatar_icon(user_email, size) + end + end + + def gravatar_icon(user_email = '', size = nil) + GravatarService.new.execute(user_email, size) || + default_avatar + end + + def default_avatar + image_path('no_avatar.png') + end + + def last_commit(project) + if project.repo_exists? + time_ago_with_tooltip(project.repository.commit.committed_date) + else + 'Never' + end + rescue + 'Never' + end + + def grouped_options_refs + repository = @project.repository + + options = [ + ['Branches', repository.branch_names], + ['Tags', VersionSorter.rsort(repository.tag_names)] + ] + + # If reference is commit id - we should add it to branch/tag selectbox + if(@ref && !options.flatten.include?(@ref) && + @ref =~ /\A[0-9a-zA-Z]{6,52}\z/) + options << ['Commit', [@ref]] + end + + grouped_options_for_select(options, @ref || @project.default_branch) + end + + def emoji_autocomplete_source + # should be an array of strings + # so to_s can be called, because it is sufficient and to_json is too slow + Emoji.names.to_s + end + + # Define whenever show last push event + # with suggestion to create MR + def show_last_push_widget?(event) + # Skip if event is not about added or modified non-master branch + return false unless event && event.last_push_to_non_root? && !event.rm_ref? + + project = event.project + + # Skip if project repo is empty or MR disabled + return false unless project && !project.empty_repo? && project.merge_requests_enabled + + # Skip if user already created appropriate MR + return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? + + # Skip if user removed branch right after that + return false unless project.repository.branch_names.include?(event.branch_name) + + true + end + + def hexdigest(string) + Digest::SHA1.hexdigest string + end + + def simple_sanitize(str) + sanitize(str, tags: %w(a span)) + end + + def body_data_page + path = controller.controller_path.split('/') + namespace = path.first if path.second + + [namespace, controller.controller_name, controller.action_name].compact.join(':') + end + + # shortcut for gitlab config + def gitlab_config + Gitlab.config.gitlab + end + + # shortcut for gitlab extra config + def extra_config + Gitlab.config.extra + end + + def search_placeholder + if @project && @project.persisted? + 'Search in this project' + elsif @snippet || @snippets || @show_snippets + 'Search snippets' + elsif @group && @group.persisted? + 'Search in this group' + else + 'Search' + end + end + + def broadcast_message + BroadcastMessage.current + end + + # Render a `time` element with Javascript-based relative date and tooltip + # + # time - Time object + # placement - Tooltip placement String (default: "top") + # html_class - Custom class for `time` element (default: "time_ago") + # skip_js - When true, exclude the `script` tag (default: false) + # + # By default also includes a `script` element with Javascript necessary to + # initialize the `timeago` jQuery extension. If this method is called many + # times, for example rendering hundreds of commits, it's advisable to disable + # this behavior using the `skip_js` argument and re-initializing `timeago` + # manually once all of the elements have been rendered. + # + # A `js-timeago` class is always added to the element, even when a custom + # `html_class` argument is provided. + # + # Returns an HTML-safe String + def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) + element = content_tag :time, time.to_s, + class: "#{html_class} js-timeago", + datetime: time.getutc.iso8601, + title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), + data: { toggle: 'tooltip', placement: placement } + + element += javascript_tag "$('.js-timeago').timeago()" unless skip_js + + element + end + + def render_markup(file_name, file_content) + if gitlab_markdown?(file_name) + Haml::Helpers.preserve(markdown(file_content)) + elsif asciidoc?(file_name) + asciidoc(file_content) + elsif plain?(file_name) + content_tag :pre, class: 'plain-readme' do + file_content + end + else + GitHub::Markup.render(file_name, file_content). + force_encoding(file_content.encoding).html_safe + end + rescue RuntimeError + simple_format(file_content) + end + + def plain?(filename) + Gitlab::MarkupHelper.plain?(filename) + end + + def markup?(filename) + Gitlab::MarkupHelper.markup?(filename) + end + + def gitlab_markdown?(filename) + Gitlab::MarkupHelper.gitlab_markdown?(filename) + end + + def asciidoc?(filename) + Gitlab::MarkupHelper.asciidoc?(filename) + end + + def promo_host + 'about.gitlab.com' + end + + def promo_url + 'https://' + promo_host + end + + def page_filter_path(options = {}) + without = options.delete(:without) + + exist_opts = { + state: params[:state], + scope: params[:scope], + label_name: params[:label_name], + milestone_id: params[:milestone_id], + assignee_id: params[:assignee_id], + author_id: params[:author_id], + sort: params[:sort], + } + + options = exist_opts.merge(options) + + if without.present? + without.each do |key| + options.delete(key) + end + end + + path = request.path + path << "?#{options.to_param}" + path + end + + def outdated_browser? + browser.ie? && browser.version.to_i < 10 + end + + def path_to_key(key, admin = false) + if admin + admin_user_key_path(@user, key) + else + profile_key_path(key) + end + end + + def state_filters_text_for(entity, project) + titles = { + opened: "Open" + } + + entity_title = titles[entity] || entity.to_s.humanize + + count = + if project.nil? + nil + elsif current_controller?(:issues) + project.issues.send(entity).count + elsif current_controller?(:merge_requests) + project.merge_requests.send(entity).count + end + + html = content_tag :span, entity_title + + if count.present? + html += " " + html += content_tag :span, number_with_delimiter(count), class: 'badge' + end + + html.html_safe + end + end +end diff --git a/app/helpers/gitlab/application_settings_helper.rb b/app/helpers/gitlab/application_settings_helper.rb new file mode 100644 index 00000000000..7132d3dcdad --- /dev/null +++ b/app/helpers/gitlab/application_settings_helper.rb @@ -0,0 +1,61 @@ +module Gitlab + module ApplicationSettingsHelper + def gravatar_enabled? + current_application_settings.gravatar_enabled? + end + + def twitter_sharing_enabled? + current_application_settings.twitter_sharing_enabled? + end + + def signup_enabled? + current_application_settings.signup_enabled? + end + + def signin_enabled? + current_application_settings.signin_enabled? + end + + def extra_sign_in_text + current_application_settings.sign_in_text + end + + def user_oauth_applications? + current_application_settings.user_oauth_applications + end + + # Return a group of checkboxes that use Bootstrap's button plugin for a + # toggle button effect. + def restricted_level_checkboxes(help_block_id) + Gitlab::VisibilityLevel.options.map do |name, level| + checked = restricted_visibility_levels(true).include?(level) + css_class = 'btn' + css_class += ' active' if checked + checkbox_name = 'application_setting[restricted_visibility_levels][]' + + label_tag(checkbox_name, class: css_class) do + check_box_tag(checkbox_name, level, checked, + autocomplete: 'off', + 'aria-describedby' => help_block_id) + name + end + end + end + + # Return a group of checkboxes that use Bootstrap's button plugin for a + # toggle button effect. + def import_sources_checkboxes(help_block_id) + Gitlab::ImportSources.options.map do |name, source| + checked = current_application_settings.import_sources.include?(source) + css_class = 'btn' + css_class += ' active' if checked + checkbox_name = 'application_setting[import_sources][]' + + label_tag(checkbox_name, class: css_class) do + check_box_tag(checkbox_name, source, checked, + autocomplete: 'off', + 'aria-describedby' => help_block_id) + name + end + end + end + end +end diff --git a/app/helpers/gitlab/auth_helper.rb b/app/helpers/gitlab/auth_helper.rb new file mode 100644 index 00000000000..fbd52dbca3d --- /dev/null +++ b/app/helpers/gitlab/auth_helper.rb @@ -0,0 +1,52 @@ +module Gitlab + module AuthHelper + PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze + FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze + + def ldap_enabled? + Gitlab.config.ldap.enabled + end + + def provider_has_icon?(name) + PROVIDERS_WITH_ICONS.include?(name.to_s) + end + + def auth_providers + Gitlab::OAuth::Provider.providers + end + + def label_for_provider(name) + Gitlab::OAuth::Provider.label_for(name) + end + + def form_based_provider?(name) + FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s } + end + + def form_based_providers + auth_providers.select { |provider| form_based_provider?(provider) } + end + + def button_based_providers + auth_providers.reject { |provider| form_based_provider?(provider) } + end + + def provider_image_tag(provider, size = 64) + label = label_for_provider(provider) + + if provider_has_icon?(provider) + file_name = "#{provider.to_s.split('_').first}_#{size}.png" + + image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}") + else + label + end + end + + def auth_active?(provider) + current_user.identities.exists?(provider: provider.to_s) + end + + extend self + end +end diff --git a/app/helpers/gitlab/blob_helper.rb b/app/helpers/gitlab/blob_helper.rb new file mode 100644 index 00000000000..8b53ba8b54f --- /dev/null +++ b/app/helpers/gitlab/blob_helper.rb @@ -0,0 +1,76 @@ +module Gitlab + module BlobHelper + def highlight(blob_name, blob_content, nowrap: false, continue: false) + @formatter ||= Rouge::Formatters::HTMLGitlab.new( + nowrap: nowrap, + cssclass: 'code highlight', + lineanchors: true, + lineanchorsid: 'LC' + ) + + begin + @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new + result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe + rescue + @lexer = Rouge::Lexers::PlainText + result = @formatter.format(@lexer.lex(blob_content)).html_safe + end + + result + end + + def no_highlight_files + %w(credits changelog news copying copyright license authors) + end + + def edit_blob_link(project, ref, path, options = {}) + blob = + begin + project.repository.blob_at(ref, path) + rescue + nil + end + + if blob && blob.text? + text = 'Edit' + after = options[:after] || '' + from_mr = options[:from_merge_request_id] + link_opts = {} + link_opts[:from_merge_request_id] = from_mr if from_mr + cls = 'btn btn-small' + if allowed_tree_edit?(project, ref) + link_to(text, + namespace_project_edit_blob_path(project.namespace, project, + tree_join(ref, path), + link_opts), + class: cls + ) + else + content_tag :span, text, class: cls + ' disabled' + end + after.html_safe + else + '' + end + end + + def leave_edit_message + "Leave edit mode?\nAll unsaved changes will be lost." + end + + def editing_preview_title(filename) + if Gitlab::MarkupHelper.previewable?(filename) + 'Preview' + else + 'Preview changes' + end + end + + # Return an image icon depending on the file mode and extension + # + # mode - File unix mode + # mode - File name + def blob_icon(mode, name) + icon("#{file_type_icon_class('file', mode, name)} fw") + end + end +end diff --git a/app/helpers/gitlab/branches_helper.rb b/app/helpers/gitlab/branches_helper.rb new file mode 100644 index 00000000000..ecc56002e84 --- /dev/null +++ b/app/helpers/gitlab/branches_helper.rb @@ -0,0 +1,19 @@ +module Gitlab + module BranchesHelper + def can_remove_branch?(project, branch_name) + if project.protected_branch? branch_name + false + elsif branch_name == project.repository.root_ref + false + else + can?(current_user, :push_code, project) + end + end + + def can_push_branch?(project, branch_name) + return false unless project.repository.branch_names.include?(branch_name) + + ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) + end + end +end diff --git a/app/helpers/gitlab/broadcast_messages_helper.rb b/app/helpers/gitlab/broadcast_messages_helper.rb new file mode 100644 index 00000000000..93f0b0ec5ae --- /dev/null +++ b/app/helpers/gitlab/broadcast_messages_helper.rb @@ -0,0 +1,18 @@ +module Gitlab + module BroadcastMessagesHelper + def broadcast_styling(broadcast_message) + styling = '' + + if broadcast_message.color.present? + styling << "background-color: #{broadcast_message.color}" + styling << '; ' if broadcast_message.font.present? + end + + if broadcast_message.font.present? + styling << "color: #{broadcast_message.font}" + end + + styling + end + end +end diff --git a/app/helpers/gitlab/commits_helper.rb b/app/helpers/gitlab/commits_helper.rb new file mode 100644 index 00000000000..8a3de838b39 --- /dev/null +++ b/app/helpers/gitlab/commits_helper.rb @@ -0,0 +1,185 @@ +# encoding: utf-8 +module Gitlab + module CommitsHelper + # Returns a link to the commit author. If the author has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the author email as specified in the commit. + # + # options: + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def commit_author_link(commit, options = {}) + commit_person_link(commit, options.merge(source: :author)) + end + + # Just like #author_link but for the committer. + def commit_committer_link(commit, options = {}) + commit_person_link(commit, options.merge(source: :committer)) + end + + def image_diff_class(diff) + if diff.deleted_file + "deleted" + elsif diff.new_file + "added" + else + nil + end + end + + def commit_to_html(commit, project, inline = true) + template = inline ? "inline_commit" : "commit" + escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? + end + + # Breadcrumb links for a Project and, if applicable, a tree path + def commits_breadcrumbs + return unless @project && @ref + + # Add the root project link and the arrow icon + crumbs = content_tag(:li) do + link_to( + @project.path, + namespace_project_commits_path(@project.namespace, @project, @ref) + ) + end + + if @path + parts = @path.split('/') + + parts.each_with_index do |part, i| + crumbs << content_tag(:li) do + # The text is just the individual part, but the link needs all the parts before it + link_to( + part, + namespace_project_commits_path( + @project.namespace, + @project, + tree_join(@ref, parts[0..i].join('/')) + ) + ) + end + end + end + + crumbs.html_safe + end + + # Return Project default branch, if it present in array + # Else - first branch in array (mb last actual branch) + def commit_default_branch(project, branches) + branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop + end + + # Returns the sorted alphabetically links to branches, separated by a comma + def commit_branches_links(project, branches) + branches.sort.map do |branch| + link_to( + namespace_project_tree_path(project.namespace, project, branch) + ) do + content_tag :span, class: 'label label-gray' do + icon('code-fork') + ' ' + branch + end + end + end.join(" ").html_safe + end + + # Returns the sorted links to tags, separated by a comma + def commit_tags_links(project, tags) + sorted = VersionSorter.rsort(tags) + sorted.map do |tag| + link_to( + namespace_project_commits_path(project.namespace, project, + project.repository.find_tag(tag).name) + ) do + content_tag :span, class: 'label label-gray' do + icon('tag') + ' ' + tag + end + end + end.join(" ").html_safe + end + + def link_to_browse_code(project, commit) + if current_controller?(:projects, :commits) + if @repo.blob_at(commit.id, @path) + return link_to( + "Browse File »", + namespace_project_blob_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) + elsif @path.present? + return link_to( + "Browse Dir »", + namespace_project_tree_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) + end + end + link_to( + "Browse Code »", + namespace_project_tree_path(project.namespace, project, commit), + class: "pull-right" + ) + end + + protected + + # Private: Returns a link to a person. If the person has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the person email as specified in the commit. + # + # options: + # source: one of :author or :committer + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def commit_person_link(commit, options = {}) + user = commit.send(options[:source]) + + source_name = clean(commit.send "#{options[:source]}_name".to_sym) + source_email = clean(commit.send "#{options[:source]}_email".to_sym) + + person_name = user.try(:name) || source_name + person_email = user.try(:email) || source_email + + text = + if options[:avatar] + avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") + %Q{#{avatar} #{person_name}} + else + person_name + end + + options = { + class: "commit-#{options[:source]}-link has_tooltip", + data: { :'original-title' => sanitize(source_email) } + } + + if user.nil? + mail_to(source_email, text.html_safe, options) + else + link_to(text.html_safe, user_path(user), options) + end + end + + def view_file_btn(commit_sha, diff, project) + link_to( + namespace_project_blob_path(project.namespace, project, + tree_join(commit_sha, diff.new_path)), + class: 'btn btn-small view-file js-view-file' + ) do + raw('View file @') + content_tag(:span, commit_sha[0..6], + class: 'commit-short-id') + end + end + + def truncate_sha(sha) + Commit.truncate_sha(sha) + end + + def clean(string) + Sanitize.clean(string, remove_contents: true) + end + end +end diff --git a/app/helpers/gitlab/compare_helper.rb b/app/helpers/gitlab/compare_helper.rb new file mode 100644 index 00000000000..407d25d3102 --- /dev/null +++ b/app/helpers/gitlab/compare_helper.rb @@ -0,0 +1,23 @@ +module Gitlab + module CompareHelper + def create_mr_button?(from = params[:from], to = params[:to], project = @project) + from.present? && + to.present? && + from != to && + project.merge_requests_enabled && + project.repository.branch_names.include?(from) && + project.repository.branch_names.include?(to) + end + + def create_mr_path(from = params[:from], to = params[:to], project = @project) + new_namespace_project_merge_request_path( + project.namespace, + project, + merge_request: { + source_branch: to, + target_branch: from + } + ) + end + end +end diff --git a/app/helpers/gitlab/dashboard_helper.rb b/app/helpers/gitlab/dashboard_helper.rb new file mode 100644 index 00000000000..2211c93999e --- /dev/null +++ b/app/helpers/gitlab/dashboard_helper.rb @@ -0,0 +1,11 @@ +module Gitlab + module DashboardHelper + def assigned_issues_dashboard_path + issues_dashboard_path(assignee_id: current_user.id) + end + + def assigned_mrs_dashboard_path + merge_requests_dashboard_path(assignee_id: current_user.id) + end + end +end diff --git a/app/helpers/gitlab/diff_helper.rb b/app/helpers/gitlab/diff_helper.rb new file mode 100644 index 00000000000..02907eb80f3 --- /dev/null +++ b/app/helpers/gitlab/diff_helper.rb @@ -0,0 +1,172 @@ +module Gitlab + module DiffHelper + def allowed_diff_size + if diff_hard_limit_enabled? + Commit::DIFF_HARD_LIMIT_FILES + else + Commit::DIFF_SAFE_FILES + end + end + + def allowed_diff_lines + if diff_hard_limit_enabled? + Commit::DIFF_HARD_LIMIT_LINES + else + Commit::DIFF_SAFE_LINES + end + end + + def safe_diff_files(diffs) + lines = 0 + safe_files = [] + diffs.first(allowed_diff_size).each do |diff| + lines += diff.diff.lines.count + break if lines > allowed_diff_lines + safe_files << Gitlab::Diff::File.new(diff) + end + safe_files + end + + def diff_hard_limit_enabled? + # Enabling hard limit allows user to see more diff information + if params[:force_show_diff].present? + true + else + false + end + end + + def generate_line_code(file_path, line) + Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) + end + + def parallel_diff(diff_file, index) + lines = [] + skip_next = false + + # Building array of lines + # + # [ + # left_type, left_line_number, left_line_content, left_line_code, + # right_line_type, right_line_number, right_line_content, right_line_code + # ] + # + diff_file.diff_lines.each do |line| + + full_line = line.text + type = line.type + line_code = generate_line_code(diff_file.file_path, line) + line_new = line.new_pos + line_old = line.old_pos + + next_line = diff_file.next_line(line.index) + + if next_line + next_line_code = generate_line_code(diff_file.file_path, next_line) + next_type = next_line.type + next_line = next_line.text + end + + if type == 'match' || type.nil? + # line in the right panel is the same as in the left one + line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code] + lines.push(line) + elsif type == 'old' + if next_type == 'new' + # Left side has text removed, right side has text added + line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code] + lines.push(line) + skip_next = true + elsif next_type == 'old' || next_type.nil? + # Left side has text removed, right side doesn't have any change + # No next line code, no new line number, no new line text + line = [type, line_old, full_line, line_code, next_type, nil, " ", nil] + lines.push(line) + end + elsif type == 'new' + if skip_next + # Change has been already included in previous line so no need to do it again + skip_next = false + next + else + # Change is only on the right side, left side has no change + line = [nil, nil, " ", line_code, type, line_new, full_line, line_code] + lines.push(line) + end + end + end + lines + end + + def unfold_bottom_class(bottom) + (bottom) ? 'js-unfold-bottom' : '' + end + + def unfold_class(unfold) + (unfold) ? 'unfold js-unfold' : '' + end + + def diff_line_content(line) + if line.blank? + "  " + else + line + end + end + + def line_comments + @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code) + end + + def organize_comments(type_left, type_right, line_code_left, line_code_right) + comments_left = comments_right = nil + + unless type_left.nil? && type_right == 'new' + comments_left = line_comments[line_code_left] + end + + unless type_left.nil? && type_right.nil? + comments_right = line_comments[line_code_right] + end + + [comments_left, comments_right] + end + + def inline_diff_btn + params_copy = params.dup + params_copy[:view] = 'inline' + # Always use HTML to handle case where JSON diff rendered this button + params_copy.delete(:format) + + link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do + 'Inline' + end + end + + def parallel_diff_btn + params_copy = params.dup + params_copy[:view] = 'parallel' + # Always use HTML to handle case where JSON diff rendered this button + params_copy.delete(:format) + + link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do + 'Side-by-side' + end + end + + def submodule_link(blob, ref, repository = @repository) + tree, commit = submodule_links(blob, ref, repository) + commit_id = if commit.nil? + blob.id[0..10] + else + link_to "#{blob.id[0..10]}", commit + end + + [ + content_tag(:span, link_to(truncate(blob.name, length: 40), tree)), + '@', + content_tag(:span, commit_id, class: 'monospace'), + ].join(' ').html_safe + end + end +end diff --git a/app/helpers/gitlab/emails_helper.rb b/app/helpers/gitlab/emails_helper.rb new file mode 100644 index 00000000000..84f106dd536 --- /dev/null +++ b/app/helpers/gitlab/emails_helper.rb @@ -0,0 +1,59 @@ +module Gitlab + module EmailsHelper + + # Google Actions + # https://developers.google.com/gmail/markup/reference/go-to-action + def email_action(url) + name = action_title(url) + if name + data = { + "@context" => "http://schema.org", + "@type" => "EmailMessage", + "action" => { + "@type" => "ViewAction", + "name" => name, + "url" => url, + } + } + + content_tag :script, type: 'application/ld+json' do + data.to_json.html_safe + end + end + end + + def action_title(url) + return unless url + ["merge_requests", "issues", "commit"].each do |action| + if url.split("/").include?(action) + return "View #{action.humanize.singularize}" + end + end + end + + def color_email_diff(diffcontent) + formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github') + lexer = Rouge::Lexers::Diff + raw formatter.format(lexer.lex(diffcontent)) + end + + def password_reset_token_valid_time + valid_hours = Devise.reset_password_within / 60 / 60 + if valid_hours >= 24 + unit = 'day' + valid_length = (valid_hours / 24).floor + else + unit = 'hour' + valid_length = valid_hours.floor + end + + pluralize(valid_length, unit) + end + + def reset_token_expire_message + link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email)) + msg = "This link is valid for #{password_reset_token_valid_time}. " + msg << "After it expires, you can #{link_tag}." + end + end +end diff --git a/app/helpers/gitlab/events_helper.rb b/app/helpers/gitlab/events_helper.rb new file mode 100644 index 00000000000..65522dae533 --- /dev/null +++ b/app/helpers/gitlab/events_helper.rb @@ -0,0 +1,205 @@ +module Gitlab + module EventsHelper + def link_to_author(event) + author = event.author + + if author + link_to author.name, user_path(author.username) + else + event.author_name + end + end + + def event_action_name(event) + target = if event.target_type + if event.note? + event.note_target_type + else + event.target_type.titleize.downcase + end + else + 'project' + end + + [event.action_name, target].join(" ") + end + + def event_filter_link(key, tooltip) + key = key.to_s + active = 'active' if @event_filter.active?(key) + link_opts = { + class: 'event_filter_link', + id: "#{key}_event_filter", + title: "Filter by #{tooltip.downcase}", + data: { toggle: 'tooltip', placement: 'top' } + } + + content_tag :li, class: "filter_icon #{active}" do + link_to request.path, link_opts do + icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip) + end + end + end + + def icon_for_event + { + EventFilter.push => 'upload', + EventFilter.merged => 'check-square-o', + EventFilter.comments => 'comments', + EventFilter.team => 'user', + } + end + + def event_feed_title(event) + words = [] + words << event.author_name + words << event_action_name(event) + + if event.push? + words << event.ref_type + words << event.ref_name + words << "at" + elsif event.commented? + if event.note_commit? + words << event.note_short_commit_id + else + words << "##{truncate event.note_target_iid}" + end + words << "at" + elsif event.target + words << "##{event.target_iid}:" + words << event.target.title if event.target.respond_to?(:title) + words << "at" + end + + words << event.project_name + + words.join(" ") + end + + def event_feed_url(event) + if event.issue? + namespace_project_issue_url(event.project.namespace, event.project, + event.issue) + elsif event.merge_request? + namespace_project_merge_request_url(event.project.namespace, + event.project, event.merge_request) + elsif event.note? && event.note_commit? + namespace_project_commit_url(event.project.namespace, event.project, + event.note_target) + elsif event.note? + if event.note_target + if event.note_commit? + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)) + elsif event.note_project_snippet? + namespace_project_snippet_path(event.project.namespace, + event.project, event.note_target) + else + event_note_target_path(event) + end + end + elsif event.push? + if event.push_with_commits? && event.md_ref? + if event.commits_count > 1 + namespace_project_compare_url(event.project.namespace, event.project, + from: event.commit_from, to: + event.commit_to) + else + namespace_project_commit_url(event.project.namespace, event.project, + id: event.commit_to) + end + else + namespace_project_commits_url(event.project.namespace, event.project, + event.ref_name) + end + end + end + + def event_feed_summary(event) + if event.issue? + render "events/event_issue", issue: event.issue + elsif event.push? + render "events/event_push", event: event + elsif event.merge_request? + render "events/event_merge_request", merge_request: event.merge_request + elsif event.note? + render "events/event_note", note: event.note + end + end + + def event_note_target_path(event) + if event.note? && event.note_commit? + namespace_project_commit_path(event.project.namespace, event.project, + event.note_target) + else + polymorphic_path([event.project.namespace.becomes(Namespace), + event.project, event.note_target], + anchor: dom_id(event.target)) + end + end + + def event_note_title_html(event) + if event.note_target + if event.note_commit? + link_to( + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)), + class: "commit_short_id" + ) do + "#{event.note_target_type} #{event.note_short_commit_id}" + end + elsif event.note_project_snippet? + link_to(namespace_project_snippet_path(event.project.namespace, + event.project, + event.note_target)) do + "#{event.note_target_type} ##{truncate event.note_target_id}" + end + else + link_to event_note_target_path(event) do + "#{event.note_target_type} ##{truncate event.note_target_iid}" + end + end + else + content_tag :strong do + "(deleted)" + end + end + end + + def event_note(text, options = {}) + text = first_line_in_markdown(text, 150, options) + sanitize(text, tags: %w(a img b pre code p span)) + end + + def event_commit_title(message) + escape_once(truncate(message.split("\n").first, length: 70)) + rescue + "--broken encoding" + end + + def event_to_atom(xml, event) + if event.proper? + xml.entry do + event_link = event_feed_url(event) + event_title = event_feed_title(event) + event_summary = event_feed_summary(event) + + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" + xml.link href: event_link + xml.title truncate(event_title, length: 80) + xml.updated event.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) + xml.author do |author| + xml.name event.author_name + xml.email event.author_email + end + + xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } + end + end + end + end +end diff --git a/app/helpers/gitlab/explore_helper.rb b/app/helpers/gitlab/explore_helper.rb new file mode 100644 index 00000000000..b8e0f482b94 --- /dev/null +++ b/app/helpers/gitlab/explore_helper.rb @@ -0,0 +1,19 @@ +module Gitlab + module ExploreHelper + def explore_projects_filter_path(options={}) + exist_opts = { + sort: params[:sort], + scope: params[:scope], + group: params[:group], + tag: params[:tag], + visibility_level: params[:visibility_level], + } + + options = exist_opts.merge(options) + + path = explore_projects_path + path << "?#{options.to_param}" + path + end + end +end diff --git a/app/helpers/gitlab/external_wiki_helper.rb b/app/helpers/gitlab/external_wiki_helper.rb new file mode 100644 index 00000000000..710cdc727d0 --- /dev/null +++ b/app/helpers/gitlab/external_wiki_helper.rb @@ -0,0 +1,13 @@ +module Gitlab + module ExternalWikiHelper + def get_project_wiki_path(project) + external_wiki_service = project.services. + select { |service| service.to_param == 'external_wiki' }.first + if external_wiki_service.present? && external_wiki_service.active? + external_wiki_service.properties['external_wiki_url'] + else + namespace_project_wiki_path(project.namespace, project, :home) + end + end + end +end diff --git a/app/helpers/gitlab/git_helper.rb b/app/helpers/gitlab/git_helper.rb new file mode 100644 index 00000000000..867b30b8c74 --- /dev/null +++ b/app/helpers/gitlab/git_helper.rb @@ -0,0 +1,7 @@ +module Gitlab + module GitHelper + def strip_gpg_signature(text) + text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") + end + end +end diff --git a/app/helpers/gitlab/gitlab_markdown_helper.rb b/app/helpers/gitlab/gitlab_markdown_helper.rb new file mode 100644 index 00000000000..265cb4672fe --- /dev/null +++ b/app/helpers/gitlab/gitlab_markdown_helper.rb @@ -0,0 +1,195 @@ +require 'nokogiri' + +module Gitlab + module GitlabMarkdownHelper + include Gitlab::Markdown + include PreferencesHelper + + # Use this in places where you would normally use link_to(gfm(...), ...). + # + # It solves a problem occurring with nested links (i.e. + # "outer text gfm ref more outer text"). This will not be + # interpreted as intended. Browsers will parse something like + # "outer text gfm ref more outer text" (notice the last part is + # not linked any more). link_to_gfm corrects that. It wraps all parts to + # explicitly produce the correct linking behavior (i.e. + # "outer text gfm ref more outer text"). + def link_to_gfm(body, url, html_options = {}) + return "" if body.blank? + + escaped_body = if body =~ /\A\ at the beginning of a line", + "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___" + ].freeze + + # Returns a random markdown tip for use as a textarea placeholder + def random_markdown_tip + MARKDOWN_TIPS.sample + end + + private + + # Return +text+, truncated to +max_chars+ characters, excluding any HTML + # tags. + def truncate_visible(text, max_chars) + doc = Nokogiri::HTML.fragment(text) + content_length = 0 + truncated = false + + doc.traverse do |node| + if node.text? || node.content.empty? + if truncated + node.remove + next + end + + # Handle line breaks within a node + if node.content.strip.lines.length > 1 + node.content = "#{node.content.lines.first.chomp}..." + truncated = true + end + + num_remaining = max_chars - content_length + if node.content.length > num_remaining + node.content = node.content.truncate(num_remaining) + truncated = true + end + content_length += node.content.length + end + + truncated = truncate_if_block(node, truncated) + end + + doc.to_html + end + + # Used by #truncate_visible. If +node+ is the first block element, and the + # text hasn't already been truncated, then append "..." to the node contents + # and return true. Otherwise return false. + def truncate_if_block(node, truncated) + if node.element? && node.description.block? && !truncated + node.content = "#{node.content}..." if node.next_sibling + true + else + truncated + end + end + + # Returns the text necessary to reference `entity` across projects + # + # project - Project to reference + # entity - Object that responds to `to_reference` + # + # Examples: + # + # cross_project_reference(project, project.issues.first) + # # => 'namespace1/project1#123' + # + # cross_project_reference(project, project.merge_requests.first) + # # => 'namespace1/project1!345' + # + # Returns a String + def cross_project_reference(project, entity) + if entity.respond_to?(:to_reference) + "#{project.to_reference}#{entity.to_reference}" + else + '' + end + end + end +end diff --git a/app/helpers/gitlab/gitlab_routing_helper.rb b/app/helpers/gitlab/gitlab_routing_helper.rb new file mode 100644 index 00000000000..7f1e455d5de --- /dev/null +++ b/app/helpers/gitlab/gitlab_routing_helper.rb @@ -0,0 +1,69 @@ +# Shorter routing method for project and project items +# Since update to rails 4.1.9 we are now allowed to use `/` in project routing +# so we use nested routing for project resources which include project and +# project namespace. To avoid writing long methods every time we define shortcuts for +# some of routing. +# +# For example instead of this: +# +# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) +# +# We can simply use shortcut: +# +# merge_request_path(merge_request) +# +module Gitlab + module GitlabRoutingHelper + def project_path(project, *args) + namespace_project_path(project.namespace, project, *args) + end + + def activity_project_path(project, *args) + activity_namespace_project_path(project.namespace, project, *args) + end + + def edit_project_path(project, *args) + edit_namespace_project_path(project.namespace, project, *args) + end + + def issue_path(entity, *args) + namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_path(entity, *args) + namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) + end + + def milestone_path(entity, *args) + namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) + end + + def project_url(project, *args) + namespace_project_url(project.namespace, project, *args) + end + + def edit_project_url(project, *args) + edit_namespace_project_url(project.namespace, project, *args) + end + + def issue_url(entity, *args) + namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_url(entity, *args) + namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) + end + + def project_snippet_url(entity, *args) + namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) + end + + def toggle_subscription_path(entity, *args) + if entity.is_a?(Issue) + toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity) + else + toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity) + end + end + end +end diff --git a/app/helpers/gitlab/graph_helper.rb b/app/helpers/gitlab/graph_helper.rb new file mode 100644 index 00000000000..047f5c19095 --- /dev/null +++ b/app/helpers/gitlab/graph_helper.rb @@ -0,0 +1,18 @@ +module Gitlab + module GraphHelper + def get_refs(repo, commit) + refs = "" + refs << commit.ref_names(repo).join(' ') + + # append note count + refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 + + refs + end + + def parents_zip_spaces(parents, parent_spaces) + ids = parents.map { |p| p.id } + ids.zip(parent_spaces) + end + end +end diff --git a/app/helpers/gitlab/groups_helper.rb b/app/helpers/gitlab/groups_helper.rb new file mode 100644 index 00000000000..8172c617249 --- /dev/null +++ b/app/helpers/gitlab/groups_helper.rb @@ -0,0 +1,35 @@ +module Gitlab + module GroupsHelper + def remove_user_from_group_message(group, member) + if member.user + "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?" + else + "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?" + end + end + + def leave_group_message(group) + "Are you sure you want to leave \"#{group}\" group?" + end + + def should_user_see_group_roles?(user, group) + if user + user.is_admin? || group.members.exists?(user_id: user.id) + else + false + end + end + + def group_icon(group) + if group.is_a?(String) + group = Group.find_by(path: group) + end + + if group && group.avatar.present? + group.avatar.url + else + image_path('no_group_avatar.png') + end + end + end +end diff --git a/app/helpers/gitlab/icons_helper.rb b/app/helpers/gitlab/icons_helper.rb new file mode 100644 index 00000000000..e815d237bb1 --- /dev/null +++ b/app/helpers/gitlab/icons_helper.rb @@ -0,0 +1,87 @@ +module Gitlab + module IconsHelper + include FontAwesome::Rails::IconHelper + + # Creates an icon tag given icon name(s) and possible icon modifiers. + # + # Right now this method simply delegates directly to `fa_icon` from the + # font-awesome-rails gem, but should we ever use a different icon pack in the + # future we won't have to change hundreds of method calls. + def icon(names, options = {}) + fa_icon(names, options) + end + + def spinner(text = nil, visible = false) + css_class = 'loading' + css_class << ' hide' unless visible + + content_tag :div, class: css_class do + icon('spinner spin') + text + end + end + + def boolean_to_icon(value) + if value + icon('circle', class: 'cgreen') + else + icon('power-off', class: 'clgray') + end + end + + def public_icon + icon('globe fw') + end + + def internal_icon + icon('shield fw') + end + + def private_icon + icon('lock fw') + end + + def file_type_icon_class(type, mode, name) + if type == 'folder' + icon_class = 'folder' + elsif mode == '120000' + icon_class = 'share' + else + # Guess which icon to choose based on file extension. + # If you think a file extension is missing, feel free to add it on PR + + case File.extname(name).downcase + when '.pdf' + icon_class = 'file-pdf-o' + when '.jpg', '.jpeg', '.jif', '.jfif', + '.jp2', '.jpx', '.j2k', '.j2c', + '.png', '.gif', '.tif', '.tiff', + '.svg', '.ico', '.bmp' + icon_class = 'file-image-o' + when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip', + '.xz', '.rar', '.7z' + icon_class = 'file-archive-o' + when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac' + icon_class = 'file-audio-o' + when '.mp4', '.m4p', '.m4v', + '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', + '.mpg', '.mpeg', '.m2v', + '.avi', '.mkv', '.flv', '.ogv', '.mov', + '.3gp', '.3g2' + icon_class = 'file-video-o' + when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb' + icon_class = 'file-word-o' + when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm', + '.xlsb', '.xla', '.xlam', '.xll', '.xlw' + icon_class = 'file-excel-o' + when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm', + '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm' + icon_class = 'file-powerpoint-o' + else + icon_class = 'file-text-o' + end + end + + icon_class + end + end +end diff --git a/app/helpers/gitlab/issues_helper.rb b/app/helpers/gitlab/issues_helper.rb new file mode 100644 index 00000000000..67238926555 --- /dev/null +++ b/app/helpers/gitlab/issues_helper.rb @@ -0,0 +1,90 @@ +module Gitlab + module IssuesHelper + def issue_css_classes(issue) + classes = "issue" + classes << " closed" if issue.closed? + classes << " today" if issue.today? + classes + end + + # Returns an OpenStruct object suitable for use by options_from_collection_for_select + # to allow filtering issues by an unassigned User or Milestone + def unassigned_filter + # Milestone uses :title, Issue uses :name + OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned') + end + + def url_for_project_issues(project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.project_path + else + project.issues_tracker.project_url + end + end + + def url_for_new_issue(project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.new_issue_path + else + project.issues_tracker.new_issue_url + end + end + + def url_for_issue(issue_iid, project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.issue_path(issue_iid) + else + project.issues_tracker.issue_url(issue_iid) + end + end + + def bulk_update_milestone_options + options_for_select([['None (backlog)', -1]]) + + options_from_collection_for_select(project_active_milestones, 'id', + 'title', params[:milestone_id]) + end + + def milestone_options(object) + options_from_collection_for_select(object.project.milestones.active, + 'id', 'title', object.milestone_id) + end + + def issue_box_class(item) + if item.respond_to?(:expired?) && item.expired? + 'issue-box-expired' + elsif item.respond_to?(:merged?) && item.merged? + 'issue-box-merged' + elsif item.closed? + 'issue-box-closed' + else + 'issue-box-open' + end + end + + def issue_to_atom(xml, issue) + xml.entry do + xml.id namespace_project_issue_url(issue.project.namespace, + issue.project, issue) + xml.link href: namespace_project_issue_url(issue.project.namespace, + issue.project, issue) + xml.title truncate(issue.title, length: 80) + xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) + xml.author do |author| + xml.name issue.author_name + xml.email issue.author_email + end + xml.summary issue.title + end + end + + # Required for Gitlab::Markdown::IssueReferenceFilter + module_function :url_for_issue + end +end diff --git a/app/helpers/gitlab/labels_helper.rb b/app/helpers/gitlab/labels_helper.rb new file mode 100644 index 00000000000..aa16d71f42c --- /dev/null +++ b/app/helpers/gitlab/labels_helper.rb @@ -0,0 +1,103 @@ +module Gitlab + module LabelsHelper + include ActionView::Helpers::TagHelper + + # Link to a Label + # + # label - Label object to link to + # project - Project object which will be used as the context for the label's + # link. If omitted, defaults to `@project`, or the label's own + # project. + # block - An optional block that will be passed to `link_to`, forming the + # body of the link element. If omitted, defaults to + # `render_colored_label`. + # + # Examples: + # + # # Allow the generated link to use the label's own project + # link_to_label(label) + # + # # Force the generated link to use @project + # @project = Project.first + # link_to_label(label) + # + # # Force the generated link to use a provided project + # link_to_label(label, project: Project.last) + # + # # Customize link body with a block + # link_to_label(label) { "My Custom Label Text" } + # + # Returns a String + def link_to_label(label, project: nil, &block) + project ||= @project || label.project + link = namespace_project_issues_path(project.namespace, project, + label_name: label.name) + + if block_given? + link_to link, &block + else + link_to render_colored_label(label), link + end + end + + def project_label_names + @project.labels.pluck(:title) + end + + def render_colored_label(label) + label_color = label.color || Label::DEFAULT_COLOR + text_color = text_color_for_bg(label_color) + + # Intentionally not using content_tag here so that this method can be called + # by LabelReferenceFilter + span = %() + + escape_once(label.name) + '' + + span.html_safe + end + + def suggested_colors + [ + '#0033CC', + '#428BCA', + '#44AD8E', + '#A8D695', + '#5CB85C', + '#69D100', + '#004E00', + '#34495E', + '#7F8C8D', + '#A295D6', + '#5843AD', + '#8E44AD', + '#FFECDB', + '#AD4363', + '#D10069', + '#CC0033', + '#FF0000', + '#D9534F', + '#D1D100', + '#F0AD4E', + '#AD8D43' + ] + end + + def text_color_for_bg(bg_color) + r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex) + + if (r + g + b) > 500 + '#333333' + else + '#FFFFFF' + end + end + + def project_labels_options(project) + options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) + end + + # Required for Gitlab::Markdown::LabelReferenceFilter + module_function :render_colored_label, :text_color_for_bg, :escape_once + end +end diff --git a/app/helpers/gitlab/merge_requests_helper.rb b/app/helpers/gitlab/merge_requests_helper.rb new file mode 100644 index 00000000000..361f6b2fdac --- /dev/null +++ b/app/helpers/gitlab/merge_requests_helper.rb @@ -0,0 +1,76 @@ +module Gitlab + module MergeRequestsHelper + def new_mr_path_from_push_event(event) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project) + ) + end + + def new_mr_path_for_fork_from_push_event(event) + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, event.project.forked_from_project) + ) + end + + def new_mr_from_push_event(event, target_project) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_project.repository.root_ref + } + } + end + + def mr_css_classes(mr) + classes = "merge-request" + classes << " closed" if mr.closed? + classes << " merged" if mr.merged? + classes + end + + def ci_build_details_path(merge_request) + merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) + end + + def merge_path_description(merge_request, separator) + if merge_request.for_fork? + "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" + else + "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" + end + end + + def issues_sentence(issues) + issues.map { |i| "##{i.iid}" }.to_sentence + end + + def mr_change_branches_path(merge_request) + new_namespace_project_merge_request_path( + @project.namespace, @project, + merge_request: { + source_project_id: @merge_request.source_project_id, + target_project_id: @merge_request.target_project_id, + source_branch: @merge_request.source_branch, + target_branch: nil + } + ) + end + + def source_branch_with_namespace(merge_request) + if merge_request.for_fork? + namespace = link_to(merge_request.source_project_namespace, + project_path(merge_request.source_project)) + namespace + ":#{merge_request.source_branch}" + else + merge_request.source_branch + end + end + end +end diff --git a/app/helpers/gitlab/milestones_helper.rb b/app/helpers/gitlab/milestones_helper.rb new file mode 100644 index 00000000000..116967d4946 --- /dev/null +++ b/app/helpers/gitlab/milestones_helper.rb @@ -0,0 +1,38 @@ +module Gitlab + module MilestonesHelper + def milestones_filter_path(opts = {}) + if @project + namespace_project_milestones_path(@project.namespace, @project, opts) + elsif @group + group_milestones_path(@group, opts) + else + dashboard_milestones_path(opts) + end + end + + def milestone_progress_bar(milestone) + options = { + class: 'progress-bar progress-bar-success', + style: "width: #{milestone.percent_complete}%;" + } + + content_tag :div, class: 'progress' do + content_tag :div, nil, options + end + end + + def projects_milestones_options + milestones = + if @project + @project.milestones + else + Milestone.where(project_id: @projects) + end.active + + grouped_milestones = Milestones::GroupService.new(milestones).execute + grouped_milestones.unshift(Milestone::None) + + options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) + end + end +end diff --git a/app/helpers/gitlab/namespaces_helper.rb b/app/helpers/gitlab/namespaces_helper.rb new file mode 100644 index 00000000000..b1caaac3f63 --- /dev/null +++ b/app/helpers/gitlab/namespaces_helper.rb @@ -0,0 +1,38 @@ +module Gitlab + module NamespacesHelper + def namespaces_options(selected = :current_user, scope = :default) + groups = current_user.owned_groups + current_user.masters_groups + users = [current_user.namespace] + + group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] + users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] + + options = [] + options << group_opts + options << users_opts + + if selected == :current_user && current_user.namespace + selected = current_user.namespace.id + end + + grouped_options_for_select(options, selected) + end + + def namespace_select_tag(id, opts = {}) + css_class = "ajax-namespace-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end + + def namespace_icon(namespace, size = 40) + if namespace.kind_of?(Group) + group_icon(namespace) + else + avatar_icon(namespace.owner.email, size) + end + end + end +end diff --git a/app/helpers/gitlab/nav_helper.rb b/app/helpers/gitlab/nav_helper.rb new file mode 100644 index 00000000000..14106d70840 --- /dev/null +++ b/app/helpers/gitlab/nav_helper.rb @@ -0,0 +1,23 @@ +module Gitlab + module NavHelper + def nav_menu_collapsed? + cookies[:collapsed_nav] == 'true' + end + + def nav_sidebar_class + if nav_menu_collapsed? + "page-sidebar-collapsed" + else + "page-sidebar-expanded" + end + end + + def nav_header_class + if nav_menu_collapsed? + "header-collapsed" + else + "header-expanded" + end + end + end +end diff --git a/app/helpers/gitlab/notes_helper.rb b/app/helpers/gitlab/notes_helper.rb new file mode 100644 index 00000000000..15076148b02 --- /dev/null +++ b/app/helpers/gitlab/notes_helper.rb @@ -0,0 +1,78 @@ +module Gitlab + module NotesHelper + # Helps to distinguish e.g. commit notes in mr notes list + def note_for_main_target?(note) + (@noteable.class.name == note.noteable_type && !note.for_diff_line?) + end + + def note_target_fields(note) + hidden_field_tag(:target_type, note.noteable.class.name.underscore) + + hidden_field_tag(:target_id, note.noteable.id) + end + + def note_editable?(note) + note.editable? && can?(current_user, :admin_note, note) + end + + def link_to_commit_diff_line_note(note) + if note.for_commit_diff_line? + link_to( + "#{note.diff_file_name}:L#{note.diff_new_line}", + namespace_project_commit_path(@project.namespace, @project, + note.noteable, anchor: note.line_code) + ) + end + end + + def noteable_json(noteable) + { + id: noteable.id, + class: noteable.class.name, + resources: noteable.class.table_name, + project_id: noteable.project.id, + }.to_json + end + + def link_to_new_diff_note(line_code, line_type = nil) + discussion_id = Note.build_discussion_id( + @comments_target[:noteable_type], + @comments_target[:noteable_id] || @comments_target[:commit_id], + line_code + ) + + data = { + noteable_type: @comments_target[:noteable_type], + noteable_id: @comments_target[:noteable_id], + commit_id: @comments_target[:commit_id], + line_code: line_code, + discussion_id: discussion_id, + line_type: line_type + } + + button_tag(class: 'btn add-diff-note js-add-diff-note-button', + data: data, + title: 'Add a comment to this line') do + icon('comment-o') + end + end + + def link_to_reply_diff(note, line_type = nil) + return unless current_user + + data = { + noteable_type: note.noteable_type, + noteable_id: note.noteable_id, + commit_id: note.commit_id, + line_code: note.line_code, + discussion_id: note.discussion_id, + line_type: line_type + } + + button_tag class: 'btn reply-btn js-discussion-reply-button', + data: data, title: 'Add a reply' do + link_text = icon('comment') + link_text << ' Reply' + end + end + end +end diff --git a/app/helpers/gitlab/notifications_helper.rb b/app/helpers/gitlab/notifications_helper.rb new file mode 100644 index 00000000000..b6324044ab1 --- /dev/null +++ b/app/helpers/gitlab/notifications_helper.rb @@ -0,0 +1,17 @@ +module Gitlab + module NotificationsHelper + include IconsHelper + + def notification_icon(notification) + if notification.disabled? + icon('volume-off', class: 'ns-mute') + elsif notification.participating? + icon('volume-down', class: 'ns-part') + elsif notification.watch? + icon('volume-up', class: 'ns-watch') + else + icon('circle-o', class: 'ns-default') + end + end + end +end diff --git a/app/helpers/gitlab/page_layout_helper.rb b/app/helpers/gitlab/page_layout_helper.rb new file mode 100644 index 00000000000..d7a85186155 --- /dev/null +++ b/app/helpers/gitlab/page_layout_helper.rb @@ -0,0 +1,28 @@ +module Gitlab + module PageLayoutHelper + def page_title(*titles) + @page_title ||= [] + + @page_title.push(*titles.compact) if titles.any? + + @page_title.join(" | ") + end + + def header_title(title = nil, title_url = nil) + if title + @header_title = title + @header_title_url = title_url + else + @header_title_url ? link_to(@header_title, @header_title_url) : @header_title + end + end + + def sidebar(name = nil) + if name + @sidebar = name + else + @sidebar + end + end + end +end diff --git a/app/helpers/gitlab/preferences_helper.rb b/app/helpers/gitlab/preferences_helper.rb new file mode 100644 index 00000000000..3eac5d51acd --- /dev/null +++ b/app/helpers/gitlab/preferences_helper.rb @@ -0,0 +1,67 @@ +module Gitlab + # Helper methods for per-User preferences + module PreferencesHelper + COLOR_SCHEMES = { + 1 => 'white', + 2 => 'dark', + 3 => 'solarized-light', + 4 => 'solarized-dark', + 5 => 'monokai', + } + COLOR_SCHEMES.default = 'white' + + # Helper method to access the COLOR_SCHEMES + # + # The keys are the `color_scheme_ids` + # The values are the `name` of the scheme. + # + # The preview images are `name-scheme-preview.png` + # The stylesheets should use the css class `.name` + def color_schemes + COLOR_SCHEMES.freeze + end + + # Maps `dashboard` values to more user-friendly option text + DASHBOARD_CHOICES = { + projects: 'Your Projects (default)', + stars: 'Starred Projects' + }.with_indifferent_access.freeze + + # Returns an Array usable by a select field for more user-friendly option text + def dashboard_choices + defined = User.dashboards + + if defined.size != DASHBOARD_CHOICES.size + # Ensure that anyone adding new options updates this method too + raise RuntimeError, "`User` defines #{defined.size} dashboard choices," + + " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}." + else + defined.map do |key, _| + # Use `fetch` so `KeyError` gets raised when a key is missing + [DASHBOARD_CHOICES.fetch(key), key] + end + end + end + + def project_view_choices + [ + ['Readme (default)', :readme], + ['Activity view', :activity] + ] + end + + def user_application_theme + theme = Gitlab::Themes.by_id(current_user.try(:theme_id)) + theme.css_class + end + + def user_color_scheme_class + COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) + end + + def prefer_readme? + !current_user || + current_user.project_view == 'readme' + end + end +end diff --git a/app/helpers/gitlab/projects_helper.rb b/app/helpers/gitlab/projects_helper.rb new file mode 100644 index 00000000000..8a8cd6048df --- /dev/null +++ b/app/helpers/gitlab/projects_helper.rb @@ -0,0 +1,332 @@ +module Gitlab + module ProjectsHelper + def remove_from_project_team_message(project, member) + if member.user + "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?" + else + "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?" + end + end + + def link_to_project(project) + link_to [project.namespace.becomes(Namespace), project] do + title = content_tag(:span, project.name, class: 'project-name') + + if project.namespace + namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') + title = namespace + title + end + + title + end + end + + def link_to_member(project, author, opts = {}) + default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } + opts = default_opts.merge(opts) + + return "(deleted)" unless author + + author_html = "" + + # Build avatar image tag + author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + + # Build name span tag + author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] + + author_html = author_html.html_safe + + if opts[:name] + link_to(author_html, user_path(author), class: "author_link").html_safe + else + link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe + end + end + + def project_title(project) + if project.group + content_tag :span do + link_to( + simple_sanitize(project.group.name), group_path(project.group) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) + end + else + owner = project.namespace.owner + content_tag :span do + link_to( + simple_sanitize(owner.name), user_path(owner) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) + end + end + end + + def remove_project_message(project) + "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" + end + + def transfer_project_message(project) + "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" + end + + def project_nav_tabs + @nav_tabs ||= get_project_nav_tabs(@project, current_user) + end + + def project_nav_tab?(name) + project_nav_tabs.include? name + end + + def project_active_milestones + @project.milestones.active.order("due_date, title ASC") + end + + def project_for_deploy_key(deploy_key) + if deploy_key.projects.include?(@project) + @project + else + deploy_key.projects.find { |project| can?(current_user, :read_project, project) } + end + end + + def can_change_visibility_level?(project, current_user) + return false unless can?(current_user, :change_visibility_level, project) + + if project.forked? + project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE + else + true + end + end + + private + + def get_project_nav_tabs(project, current_user) + nav_tabs = [:home] + + if !project.empty_repo? && can?(current_user, :download_code, project) + nav_tabs << [:files, :commits, :network, :graphs] + end + + if project.repo_exists? && can?(current_user, :read_merge_request, project) + nav_tabs << :merge_requests + end + + if can?(current_user, :admin_project, project) + nav_tabs << :settings + end + + if can?(current_user, :read_issue, project) + nav_tabs << :issues + end + + if can?(current_user, :read_wiki, project) + nav_tabs << :wiki + end + + if can?(current_user, :read_project_snippet, project) + nav_tabs << :snippets + end + + if can?(current_user, :read_label, project) + nav_tabs << :labels + end + + if can?(current_user, :read_milestone, project) + nav_tabs << :milestones + end + + nav_tabs.flatten + end + + def git_user_name + if current_user + current_user.name + else + "Your name" + end + end + + def git_user_email + if current_user + current_user.email + else + "your@email.com" + end + end + + def repository_size(project = nil) + "#{(project || @project).repository_size} MB" + rescue + # In order to prevent 500 error + # when application cannot allocate memory + # to calculate repo size - just show 'Unknown' + 'unknown' + end + + def default_url_to_repo(project = nil) + project = project || @project + current_user ? project.url_to_repo : project.http_url_to_repo + end + + def default_clone_protocol + current_user ? "ssh" : "http" + end + + def project_last_activity(project) + if project.last_activity_at + time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') + else + "Never" + end + end + + def add_contribution_guide_path(project) + if project && !project.repository.contribution_guide + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "CONTRIBUTING.md", + commit_message: "Add contribution guide" + ) + end + end + + def add_changelog_path(project) + if project && !project.repository.changelog + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "CHANGELOG", + commit_message: "Add changelog" + ) + end + end + + def add_license_path(project) + if project && !project.repository.license + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "LICENSE", + commit_message: "Add license" + ) + end + end + + def contribution_guide_path(project) + if project && contribution_guide = project.repository.contribution_guide + namespace_project_blob_path( + project.namespace, + project, + tree_join(project.default_branch, + contribution_guide.name) + ) + end + end + + def readme_path(project) + filename_path(project, :readme) + end + + def changelog_path(project) + filename_path(project, :changelog) + end + + def license_path(project) + filename_path(project, :license) + end + + def version_path(project) + filename_path(project, :version) + end + + def hidden_pass_url(original_url) + result = URI(original_url) + result.password = '*****' unless result.password.nil? + result + rescue + original_url + end + + def project_wiki_path_with_version(proj, page, version, is_newest) + url_params = is_newest ? {} : { version_id: version } + namespace_project_wiki_path(proj.namespace, proj, page, url_params) + end + + def project_status_css_class(status) + case status + when "started" + "active" + when "failed" + "danger" + when "finished" + "success" + end + end + + def user_max_access_in_project(user, project) + level = project.team.max_member_access(user) + + if level + Gitlab::Access.options_with_owner.key(level) + end + end + + def leave_project_message(project) + "Are you sure you want to leave \"#{project.name}\" project?" + end + + def new_readme_path + ref = @repository.root_ref if @repository + ref ||= 'master' + + namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md') + end + + def last_push_event + if current_user + current_user.recent_push(@project.id) + end + end + + def readme_cache_key + sha = @project.commit.try(:sha) || 'nil' + [@project.id, sha, "readme"].join('-') + end + + def round_commit_count(project) + count = project.commit_count + + if count > 10000 + '10000+' + elsif count > 5000 + '5000+' + elsif count > 1000 + '1000+' + else + count + end + end + + private + + def filename_path(project, filename) + if project && blob = project.repository.send(filename) + namespace_project_blob_path( + project.namespace, + project, + tree_join(project.default_branch, + blob.name) + ) + end + end + end +end diff --git a/app/helpers/gitlab/search_helper.rb b/app/helpers/gitlab/search_helper.rb new file mode 100644 index 00000000000..f9caf8f2431 --- /dev/null +++ b/app/helpers/gitlab/search_helper.rb @@ -0,0 +1,114 @@ +module Gitlab + module SearchHelper + def search_autocomplete_opts(term) + return unless current_user + + resources_results = [ + groups_autocomplete(term), + projects_autocomplete(term) + ].flatten + + generic_results = project_autocomplete + default_autocomplete + help_autocomplete + generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") } + + [ + resources_results, + generic_results + ].flatten.uniq do |item| + item[:label] + end + end + + private + + # Autocomplete results for various settings pages + def default_autocomplete + [ + { label: "Profile settings", url: profile_path }, + { label: "SSH Keys", url: profile_keys_path }, + { label: "Dashboard", url: root_path }, + { label: "Admin Section", url: admin_root_path }, + ] + end + + # Autocomplete results for internal help pages + def help_autocomplete + [ + { label: "help: API Help", url: help_page_path("api", "README") }, + { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") }, + { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") }, + { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") }, + { label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") }, + { label: "help: SSH Keys Help", url: help_page_path("ssh", "README") }, + { label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") }, + { label: "help: Web Hooks Help", url: help_page_path("web_hooks", "web_hooks") }, + { label: "help: Workflow Help", url: help_page_path("workflow", "README") }, + ] + end + + # Autocomplete results for the current project, if it's defined + def project_autocomplete + if @project && @project.repository.exists? && @project.repository.root_ref + prefix = search_result_sanitize(@project.name_with_namespace) + ref = @ref || @project.repository.root_ref + + [ + { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, + { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, + { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, + { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, + { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, + { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, + ] + else + [] + end + end + + # Autocomplete results for the current user's groups + def groups_autocomplete(term, limit = 5) + current_user.authorized_groups.search(term).limit(limit).map do |group| + { + label: "group: #{search_result_sanitize(group.name)}", + url: group_path(group) + } + end + end + + # Autocomplete results for the current user's projects + def projects_autocomplete(term, limit = 5) + ProjectsFinder.new.execute(current_user).search_by_title(term). + sorted_by_stars.non_archived.limit(limit).map do |p| + { + label: "project: #{search_result_sanitize(p.name_with_namespace)}", + url: namespace_project_path(p.namespace, p) + } + end + end + + def search_result_sanitize(str) + Sanitize.clean(str) + end + + def search_filter_path(options={}) + exist_opts = { + search: params[:search], + project_id: params[:project_id], + group_id: params[:group_id], + scope: params[:scope] + } + + options = exist_opts.merge(options) + search_path(options) + end + + # Sanitize html generated after parsing markdown from issue description or comment + def search_md_sanitize(html) + sanitize(html, tags: %w(a p ol ul li pre code)) + end + end +end diff --git a/app/helpers/gitlab/selects_helper.rb b/app/helpers/gitlab/selects_helper.rb new file mode 100644 index 00000000000..d52d670a1cf --- /dev/null +++ b/app/helpers/gitlab/selects_helper.rb @@ -0,0 +1,47 @@ +module Gitlab + module SelectsHelper + def users_select_tag(id, opts = {}) + css_class = "ajax-users-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + placeholder = opts[:placeholder] || 'Search for a user' + + null_user = opts[:null_user] || false + any_user = opts[:any_user] || false + email_user = opts[:email_user] || false + first_user = opts[:first_user] && current_user ? current_user.username : false + current_user = opts[:current_user] || false + project = opts[:project] || @project + + html = { + class: css_class, + 'data-placeholder' => placeholder, + 'data-null-user' => null_user, + 'data-any-user' => any_user, + 'data-email-user' => email_user, + 'data-first-user' => first_user, + 'data-current-user' => current_user + } + + unless opts[:scope] == :all + if project + html['data-project-id'] = project.id + elsif @group + html['data-group-id'] = @group.id + end + end + + hidden_field_tag(id, value, html) + end + + def groups_select_tag(id, opts = {}) + css_class = "ajax-groups-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end + end +end diff --git a/app/helpers/gitlab/snippets_helper.rb b/app/helpers/gitlab/snippets_helper.rb new file mode 100644 index 00000000000..aaf4d43f852 --- /dev/null +++ b/app/helpers/gitlab/snippets_helper.rb @@ -0,0 +1,22 @@ +module Gitlab + module SnippetsHelper + def lifetime_select_options + options = [ + ['forever', nil], + ['1 day', "#{Date.current + 1.day}"], + ['1 week', "#{Date.current + 1.week}"], + ['1 month', "#{Date.current + 1.month}"] + ] + options_for_select(options) + end + + def reliable_snippet_path(snippet) + if snippet.project_id? + namespace_project_snippet_path(snippet.project.namespace, + snippet.project, snippet) + else + snippet_path(snippet) + end + end + end +end diff --git a/app/helpers/gitlab/sorting_helper.rb b/app/helpers/gitlab/sorting_helper.rb new file mode 100644 index 00000000000..29c63a0d129 --- /dev/null +++ b/app/helpers/gitlab/sorting_helper.rb @@ -0,0 +1,98 @@ +module Gitlab + module SortingHelper + def sort_options_hash + { + sort_value_name => sort_title_name, + sort_value_recently_updated => sort_title_recently_updated, + sort_value_oldest_updated => sort_title_oldest_updated, + sort_value_recently_created => sort_title_recently_created, + sort_value_oldest_created => sort_title_oldest_created, + sort_value_milestone_soon => sort_title_milestone_soon, + sort_value_milestone_later => sort_title_milestone_later, + sort_value_largest_repo => sort_title_largest_repo, + sort_value_recently_signin => sort_title_recently_signin, + sort_value_oldest_signin => sort_title_oldest_signin, + } + end + + def sort_title_oldest_updated + 'Oldest updated' + end + + def sort_title_recently_updated + 'Recently updated' + end + + def sort_title_oldest_created + 'Oldest created' + end + + def sort_title_recently_created + 'Recently created' + end + + def sort_title_milestone_soon + 'Milestone due soon' + end + + def sort_title_milestone_later + 'Milestone due later' + end + + def sort_title_name + 'Name' + end + + def sort_title_largest_repo + 'Largest repository' + end + + def sort_title_recently_signin + 'Recent sign in' + end + + def sort_title_oldest_signin + 'Oldest sign in' + end + + def sort_value_oldest_updated + 'updated_asc' + end + + def sort_value_recently_updated + 'updated_desc' + end + + def sort_value_oldest_created + 'created_asc' + end + + def sort_value_recently_created + 'created_desc' + end + + def sort_value_milestone_soon + 'milestone_due_asc' + end + + def sort_value_milestone_later + 'milestone_due_desc' + end + + def sort_value_name + 'name_asc' + end + + def sort_value_largest_repo + 'repository_size_desc' + end + + def sort_value_recently_signin + 'recent_sign_in' + end + + def sort_value_oldest_signin + 'oldest_sign_in' + end + end +end diff --git a/app/helpers/gitlab/submodule_helper.rb b/app/helpers/gitlab/submodule_helper.rb new file mode 100644 index 00000000000..c0fbebcb1d9 --- /dev/null +++ b/app/helpers/gitlab/submodule_helper.rb @@ -0,0 +1,76 @@ +module Gitlab + module SubmoduleHelper + include Gitlab::ShellAdapter + + # links to files listing for submodule if submodule is a project on this server + def submodule_links(submodule_item, ref = nil, repository = @repository) + url = repository.submodule_url_for(ref, submodule_item.path) + + return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/ + + namespace = $1 + project = $2 + project.chomp!('.git') + + if self_url?(url, namespace, project) + return namespace_project_path(namespace, project), + namespace_project_tree_path(namespace, project, + submodule_item.id) + elsif relative_self_url?(url) + relative_self_links(url, submodule_item.id) + elsif github_dot_com_url?(url) + standard_links('github.com', namespace, project, submodule_item.id) + elsif gitlab_dot_com_url?(url) + standard_links('gitlab.com', namespace, project, submodule_item.id) + else + return url, nil + end + end + + protected + + def github_dot_com_url?(url) + url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/ + end + + def gitlab_dot_com_url?(url) + url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ + end + + def self_url?(url, namespace, project) + return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', + project, '.git' ].join('') + url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) + end + + def relative_self_url?(url) + # (./)?(../repo.git) || (./)?(../../project/repo.git) ) + url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/ + end + + def standard_links(host, namespace, project, commit) + base = [ 'https://', host, '/', namespace, '/', project ].join('') + [base, [ base, '/tree/', commit ].join('')] + end + + def relative_self_links(url, commit) + # Map relative links to a namespace and project + # For example: + # ../bar.git -> same namespace, repo bar + # ../foo/bar.git -> namespace foo, repo bar + # ../../foo/bar/baz.git -> namespace bar, repo baz + components = url.split('/') + base = components.pop.gsub(/.git$/, '') + namespace = components.pop.gsub(/^\.\.$/, '') + + if namespace.empty? + namespace = @project.namespace.path + end + + [ + namespace_project_path(namespace, base), + namespace_project_tree_path(namespace, base, commit) + ] + end + end +end diff --git a/app/helpers/gitlab/tab_helper.rb b/app/helpers/gitlab/tab_helper.rb new file mode 100644 index 00000000000..01d36ff84fc --- /dev/null +++ b/app/helpers/gitlab/tab_helper.rb @@ -0,0 +1,133 @@ +module Gitlab + module TabHelper + # Navigation link helper + # + # Returns an `li` element with an 'active' class if the supplied + # controller(s) and/or action(s) are currently active. The content of the + # element is the value passed to the block. + # + # options - The options hash used to determine if the element is "active" (default: {}) + # :controller - One or more controller names to check (optional). + # :action - One or more action names to check (optional). + # :path - A shorthand path, such as 'dashboard#index', to check (optional). + # :html_options - Extra options to be passed to the list element (optional). + # block - An optional block that will become the contents of the returned + # `li` element. + # + # When both :controller and :action are specified, BOTH must match in order + # to be marked as active. When only one is given, either can match. + # + # Examples + # + # # Assuming we're on TreeController#show + # + # # Controller matches, but action doesn't + # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Controller matches + # nav_link(controller: [:tree, :refs]) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Several paths + # nav_link(path: ['tree#show', 'profile#show']) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Shorthand path + # nav_link(path: 'tree#show') { "Hello" } + # # => '
  • Hello
  • ' + # + # # Supplying custom options for the list element + # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } + # # => '
  • Hello
  • ' + # + # Returns a list item element String + def nav_link(options = {}, &block) + klass = active_nav_link?(options) ? 'active' : '' + + # Add our custom class into the html_options, which may or may not exist + # and which may or may not already have a :class key + o = options.delete(:html_options) || {} + o[:class] ||= '' + o[:class] += ' ' + klass + o[:class].strip! + + if block_given? + content_tag(:li, capture(&block), o) + else + content_tag(:li, nil, o) + end + end + + def active_nav_link?(options) + if path = options.delete(:path) + unless path.respond_to?(:each) + path = [path] + end + + path.any? do |single_path| + current_path?(single_path) + end + elsif page = options.delete(:page) + unless page.respond_to?(:each) + page = [page] + end + + page.any? do |single_page| + current_page?(single_page) + end + else + c = options.delete(:controller) + a = options.delete(:action) + + if c && a + # When given both options, make sure BOTH are true + current_controller?(*c) && current_action?(*a) + else + # Otherwise check EITHER option + current_controller?(*c) || current_action?(*a) + end + end + end + + def current_path?(path) + c, a, _ = path.split('#') + current_controller?(c) && current_action?(a) + end + + def project_tab_class + return "active" if current_page?(controller: "/projects", action: :edit, id: @project) + + if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name + "active" + end + end + + def branches_tab_class + if current_controller?(:protected_branches) || + current_controller?(:branches) || + current_page?(namespace_project_repository_path(@project.namespace, + @project)) + 'active' + end + end + + # Use nav_tab for save controller/action but different params + def nav_tab(key, value, &block) + o = {} + o[:class] = "" + + if value.nil? + o[:class] << " active" if params[key].blank? + else + o[:class] << " active" if params[key] == value + end + + if block_given? + content_tag(:li, capture(&block), o) + else + content_tag(:li, nil, o) + end + end + end +end diff --git a/app/helpers/gitlab/tags_helper.rb b/app/helpers/gitlab/tags_helper.rb new file mode 100644 index 00000000000..d694b2c90ce --- /dev/null +++ b/app/helpers/gitlab/tags_helper.rb @@ -0,0 +1,16 @@ +module Gitlab + module TagsHelper + def tag_path(tag) + "/tags/#{tag}" + end + + def tag_list(project) + html = '' + project.tag_list.each do |tag| + html << link_to(tag, tag_path(tag)) + end + + html.html_safe + end + end +end diff --git a/app/helpers/gitlab/tree_helper.rb b/app/helpers/gitlab/tree_helper.rb new file mode 100644 index 00000000000..dc48ff0e6e2 --- /dev/null +++ b/app/helpers/gitlab/tree_helper.rb @@ -0,0 +1,89 @@ +module Gitlab + module TreeHelper + # Sorts a repository's tree so that folders are before files and renders + # their corresponding partials + # + def render_tree(tree) + # Render Folders before Files/Submodules + folders, files, submodules = tree.trees, tree.blobs, tree.submodules + + tree = "" + + # Render folders if we have any + tree << render(partial: 'projects/tree/tree_item', collection: folders, + locals: { type: 'folder' }) if folders.present? + + # Render files if we have any + tree << render(partial: 'projects/tree/blob_item', collection: files, + locals: { type: 'file' }) if files.present? + + # Render submodules if we have any + tree << render(partial: 'projects/tree/submodule_item', + collection: submodules) if submodules.present? + + tree.html_safe + end + + def render_readme(readme) + render_markup(readme.name, readme.data) + end + + # Return an image icon depending on the file type and mode + # + # type - String type of the tree item; either 'folder' or 'file' + # mode - File unix mode + # name - File name + def tree_icon(type, mode, name) + icon("#{file_type_icon_class(type, mode, name)} fw") + end + + def tree_hex_class(content) + "file_#{hexdigest(content.name)}" + end + + # Simple shortcut to File.join + def tree_join(*args) + File.join(*args) + end + + def allowed_tree_edit?(project = nil, ref = nil) + project ||= @project + ref ||= @ref + return false unless project.repository.branch_names.include?(ref) + + ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) + end + + def tree_breadcrumbs(tree, max_links = 2) + if @path.present? + part_path = "" + parts = @path.split('/') + + yield('..', nil) if parts.count > max_links + + parts.each do |part| + part_path = File.join(part_path, part) unless part_path.empty? + part_path = part if part_path.empty? + + next unless parts.last(2).include?(part) if parts.count > max_links + yield(part, tree_join(@ref, part_path)) + end + end + end + + def up_dir_path + file = File.join(@path, "..") + tree_join(@ref, file) + end + + # returns the relative path of the first subdir that doesn't have only one directory descendant + def flatten_tree(tree) + subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) + if subtree.count == 1 && subtree.first.dir? + return tree_join(tree.name, flatten_tree(subtree.first)) + else + return tree.name + end + end + end +end diff --git a/app/helpers/gitlab/version_check_helper.rb b/app/helpers/gitlab/version_check_helper.rb new file mode 100644 index 00000000000..46a12cc8c60 --- /dev/null +++ b/app/helpers/gitlab/version_check_helper.rb @@ -0,0 +1,9 @@ +module Gitlab + module VersionCheckHelper + def version_status_badge + if Rails.env.production? + image_tag VersionCheck.new.url + end + end + end +end diff --git a/app/helpers/gitlab/visibility_level_helper.rb b/app/helpers/gitlab/visibility_level_helper.rb new file mode 100644 index 00000000000..feba901f7d7 --- /dev/null +++ b/app/helpers/gitlab/visibility_level_helper.rb @@ -0,0 +1,97 @@ +module Gitlab + module VisibilityLevelHelper + def visibility_level_color(level) + case level + when Gitlab::VisibilityLevel::PRIVATE + 'vs-private' + when Gitlab::VisibilityLevel::INTERNAL + 'vs-internal' + when Gitlab::VisibilityLevel::PUBLIC + 'vs-public' + end + end + + # Return the description for the +level+ argument. + # + # +level+ One of the Gitlab::VisibilityLevel constants + # +form_model+ Either a model object (Project, Snippet, etc.) or the name of + # a Project or Snippet class. + def visibility_level_description(level, form_model) + case form_model.is_a?(String) ? form_model : form_model.class.name + when 'PersonalSnippet', 'ProjectSnippet', 'Snippet' + snippet_visibility_level_description(level) + when 'Project' + project_visibility_level_description(level) + end + end + + def project_visibility_level_description(level) + capture_haml do + haml_tag :span do + case level + when Gitlab::VisibilityLevel::PRIVATE + haml_concat "Project access must be granted explicitly for each user." + when Gitlab::VisibilityLevel::INTERNAL + haml_concat "The project can be cloned by" + haml_concat "any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + haml_concat "The project can be cloned" + haml_concat "without any" + haml_concat "authentication." + end + end + end + end + + def snippet_visibility_level_description(level) + capture_haml do + haml_tag :span do + case level + when Gitlab::VisibilityLevel::PRIVATE + haml_concat "The snippet is visible only for me." + when Gitlab::VisibilityLevel::INTERNAL + haml_concat "The snippet is visible for any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + haml_concat "The snippet can be accessed" + haml_concat "without any" + haml_concat "authentication." + end + end + end + end + + def visibility_level_icon(level) + case level + when Gitlab::VisibilityLevel::PRIVATE + private_icon + when Gitlab::VisibilityLevel::INTERNAL + internal_icon + when Gitlab::VisibilityLevel::PUBLIC + public_icon + end + end + + def visibility_level_label(level) + Project.visibility_levels.key(level) + end + + def restricted_visibility_levels(show_all = false) + return [] if current_user.is_admin? && !show_all + current_application_settings.restricted_visibility_levels || [] + end + + def default_project_visibility + current_application_settings.default_project_visibility + end + + def default_snippet_visibility + current_application_settings.default_snippet_visibility + end + + def skip_level?(form_model, level) + form_model.is_a?(Project) && + form_model.forked? && + !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level) + end + end +end diff --git a/app/helpers/gitlab/wiki_helper.rb b/app/helpers/gitlab/wiki_helper.rb new file mode 100644 index 00000000000..02a1daf0019 --- /dev/null +++ b/app/helpers/gitlab/wiki_helper.rb @@ -0,0 +1,26 @@ +module Gitlab + module WikiHelper + # Rails v4.1.9+ escapes all model IDs, converting slashes into %2F. The + # only way around this is to implement our own path generators. + def namespace_project_wiki_path(namespace, project, wiki_page, *args) + slug = + case wiki_page + when Symbol + wiki_page + when String + wiki_page + else + wiki_page.slug + end + namespace_project_path(namespace, project) + "/wikis/#{slug}" + end + + def edit_namespace_project_wiki_path(namespace, project, wiki_page, *args) + namespace_project_wiki_path(namespace, project, wiki_page) + '/edit' + end + + def history_namespace_project_wiki_path(namespace, project, wiki_page, *args) + namespace_project_wiki_path(namespace, project, wiki_page) + '/history' + end + end +end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb deleted file mode 100644 index eb3f72a307d..00000000000 --- a/app/helpers/gitlab_markdown_helper.rb +++ /dev/null @@ -1,193 +0,0 @@ -require 'nokogiri' - -module GitlabMarkdownHelper - include Gitlab::Markdown - include PreferencesHelper - - # Use this in places where you would normally use link_to(gfm(...), ...). - # - # It solves a problem occurring with nested links (i.e. - # "outer text gfm ref more outer text"). This will not be - # interpreted as intended. Browsers will parse something like - # "outer text gfm ref more outer text" (notice the last part is - # not linked any more). link_to_gfm corrects that. It wraps all parts to - # explicitly produce the correct linking behavior (i.e. - # "outer text gfm ref more outer text"). - def link_to_gfm(body, url, html_options = {}) - return "" if body.blank? - - escaped_body = if body =~ /\A\ at the beginning of a line", - "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___" - ].freeze - - # Returns a random markdown tip for use as a textarea placeholder - def random_markdown_tip - MARKDOWN_TIPS.sample - end - - private - - # Return +text+, truncated to +max_chars+ characters, excluding any HTML - # tags. - def truncate_visible(text, max_chars) - doc = Nokogiri::HTML.fragment(text) - content_length = 0 - truncated = false - - doc.traverse do |node| - if node.text? || node.content.empty? - if truncated - node.remove - next - end - - # Handle line breaks within a node - if node.content.strip.lines.length > 1 - node.content = "#{node.content.lines.first.chomp}..." - truncated = true - end - - num_remaining = max_chars - content_length - if node.content.length > num_remaining - node.content = node.content.truncate(num_remaining) - truncated = true - end - content_length += node.content.length - end - - truncated = truncate_if_block(node, truncated) - end - - doc.to_html - end - - # Used by #truncate_visible. If +node+ is the first block element, and the - # text hasn't already been truncated, then append "..." to the node contents - # and return true. Otherwise return false. - def truncate_if_block(node, truncated) - if node.element? && node.description.block? && !truncated - node.content = "#{node.content}..." if node.next_sibling - true - else - truncated - end - end - - # Returns the text necessary to reference `entity` across projects - # - # project - Project to reference - # entity - Object that responds to `to_reference` - # - # Examples: - # - # cross_project_reference(project, project.issues.first) - # # => 'namespace1/project1#123' - # - # cross_project_reference(project, project.merge_requests.first) - # # => 'namespace1/project1!345' - # - # Returns a String - def cross_project_reference(project, entity) - if entity.respond_to?(:to_reference) - "#{project.to_reference}#{entity.to_reference}" - else - '' - end - end -end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb deleted file mode 100644 index d0fae255a04..00000000000 --- a/app/helpers/gitlab_routing_helper.rb +++ /dev/null @@ -1,67 +0,0 @@ -# Shorter routing method for project and project items -# Since update to rails 4.1.9 we are now allowed to use `/` in project routing -# so we use nested routing for project resources which include project and -# project namespace. To avoid writing long methods every time we define shortcuts for -# some of routing. -# -# For example instead of this: -# -# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) -# -# We can simply use shortcut: -# -# merge_request_path(merge_request) -# -module GitlabRoutingHelper - def project_path(project, *args) - namespace_project_path(project.namespace, project, *args) - end - - def activity_project_path(project, *args) - activity_namespace_project_path(project.namespace, project, *args) - end - - def edit_project_path(project, *args) - edit_namespace_project_path(project.namespace, project, *args) - end - - def issue_path(entity, *args) - namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) - end - - def merge_request_path(entity, *args) - namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) - end - - def milestone_path(entity, *args) - namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) - end - - def project_url(project, *args) - namespace_project_url(project.namespace, project, *args) - end - - def edit_project_url(project, *args) - edit_namespace_project_url(project.namespace, project, *args) - end - - def issue_url(entity, *args) - namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) - end - - def merge_request_url(entity, *args) - namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) - end - - def project_snippet_url(entity, *args) - namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) - end - - def toggle_subscription_path(entity, *args) - if entity.is_a?(Issue) - toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity) - else - toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity) - end - end -end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb deleted file mode 100644 index e1dda20de85..00000000000 --- a/app/helpers/graph_helper.rb +++ /dev/null @@ -1,16 +0,0 @@ -module GraphHelper - def get_refs(repo, commit) - refs = "" - refs << commit.ref_names(repo).join(' ') - - # append note count - refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 - - refs - end - - def parents_zip_spaces(parents, parent_spaces) - ids = parents.map { |p| p.id } - ids.zip(parent_spaces) - end -end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb deleted file mode 100644 index b067cb54a43..00000000000 --- a/app/helpers/groups_helper.rb +++ /dev/null @@ -1,33 +0,0 @@ -module GroupsHelper - def remove_user_from_group_message(group, member) - if member.user - "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?" - else - "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?" - end - end - - def leave_group_message(group) - "Are you sure you want to leave \"#{group}\" group?" - end - - def should_user_see_group_roles?(user, group) - if user - user.is_admin? || group.members.exists?(user_id: user.id) - else - false - end - end - - def group_icon(group) - if group.is_a?(String) - group = Group.find_by(path: group) - end - - if group && group.avatar.present? - group.avatar.url - else - image_path('no_group_avatar.png') - end - end -end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb deleted file mode 100644 index 1cf5b96481a..00000000000 --- a/app/helpers/icons_helper.rb +++ /dev/null @@ -1,85 +0,0 @@ -module IconsHelper - include FontAwesome::Rails::IconHelper - - # Creates an icon tag given icon name(s) and possible icon modifiers. - # - # Right now this method simply delegates directly to `fa_icon` from the - # font-awesome-rails gem, but should we ever use a different icon pack in the - # future we won't have to change hundreds of method calls. - def icon(names, options = {}) - fa_icon(names, options) - end - - def spinner(text = nil, visible = false) - css_class = 'loading' - css_class << ' hide' unless visible - - content_tag :div, class: css_class do - icon('spinner spin') + text - end - end - - def boolean_to_icon(value) - if value - icon('circle', class: 'cgreen') - else - icon('power-off', class: 'clgray') - end - end - - def public_icon - icon('globe fw') - end - - def internal_icon - icon('shield fw') - end - - def private_icon - icon('lock fw') - end - - def file_type_icon_class(type, mode, name) - if type == 'folder' - icon_class = 'folder' - elsif mode == '120000' - icon_class = 'share' - else - # Guess which icon to choose based on file extension. - # If you think a file extension is missing, feel free to add it on PR - - case File.extname(name).downcase - when '.pdf' - icon_class = 'file-pdf-o' - when '.jpg', '.jpeg', '.jif', '.jfif', - '.jp2', '.jpx', '.j2k', '.j2c', - '.png', '.gif', '.tif', '.tiff', - '.svg', '.ico', '.bmp' - icon_class = 'file-image-o' - when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip', - '.xz', '.rar', '.7z' - icon_class = 'file-archive-o' - when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac' - icon_class = 'file-audio-o' - when '.mp4', '.m4p', '.m4v', - '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', - '.mpg', '.mpeg', '.m2v', - '.avi', '.mkv', '.flv', '.ogv', '.mov', - '.3gp', '.3g2' - icon_class = 'file-video-o' - when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb' - icon_class = 'file-word-o' - when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm', - '.xlsb', '.xla', '.xlam', '.xll', '.xlw' - icon_class = 'file-excel-o' - when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm', - '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm' - icon_class = 'file-powerpoint-o' - else - icon_class = 'file-text-o' - end - end - - icon_class - end -end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb deleted file mode 100644 index 6ddb37cd0dc..00000000000 --- a/app/helpers/issues_helper.rb +++ /dev/null @@ -1,88 +0,0 @@ -module IssuesHelper - def issue_css_classes(issue) - classes = "issue" - classes << " closed" if issue.closed? - classes << " today" if issue.today? - classes - end - - # Returns an OpenStruct object suitable for use by options_from_collection_for_select - # to allow filtering issues by an unassigned User or Milestone - def unassigned_filter - # Milestone uses :title, Issue uses :name - OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned') - end - - def url_for_project_issues(project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.project_path - else - project.issues_tracker.project_url - end - end - - def url_for_new_issue(project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.new_issue_path - else - project.issues_tracker.new_issue_url - end - end - - def url_for_issue(issue_iid, project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.issue_path(issue_iid) - else - project.issues_tracker.issue_url(issue_iid) - end - end - - def bulk_update_milestone_options - options_for_select([['None (backlog)', -1]]) + - options_from_collection_for_select(project_active_milestones, 'id', - 'title', params[:milestone_id]) - end - - def milestone_options(object) - options_from_collection_for_select(object.project.milestones.active, - 'id', 'title', object.milestone_id) - end - - def issue_box_class(item) - if item.respond_to?(:expired?) && item.expired? - 'issue-box-expired' - elsif item.respond_to?(:merged?) && item.merged? - 'issue-box-merged' - elsif item.closed? - 'issue-box-closed' - else - 'issue-box-open' - end - end - - def issue_to_atom(xml, issue) - xml.entry do - xml.id namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.link href: namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.title truncate(issue.title, length: 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end - end - - # Required for Gitlab::Markdown::IssueReferenceFilter - module_function :url_for_issue -end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb deleted file mode 100644 index 8036303851b..00000000000 --- a/app/helpers/labels_helper.rb +++ /dev/null @@ -1,101 +0,0 @@ -module LabelsHelper - include ActionView::Helpers::TagHelper - - # Link to a Label - # - # label - Label object to link to - # project - Project object which will be used as the context for the label's - # link. If omitted, defaults to `@project`, or the label's own - # project. - # block - An optional block that will be passed to `link_to`, forming the - # body of the link element. If omitted, defaults to - # `render_colored_label`. - # - # Examples: - # - # # Allow the generated link to use the label's own project - # link_to_label(label) - # - # # Force the generated link to use @project - # @project = Project.first - # link_to_label(label) - # - # # Force the generated link to use a provided project - # link_to_label(label, project: Project.last) - # - # # Customize link body with a block - # link_to_label(label) { "My Custom Label Text" } - # - # Returns a String - def link_to_label(label, project: nil, &block) - project ||= @project || label.project - link = namespace_project_issues_path(project.namespace, project, - label_name: label.name) - - if block_given? - link_to link, &block - else - link_to render_colored_label(label), link - end - end - - def project_label_names - @project.labels.pluck(:title) - end - - def render_colored_label(label) - label_color = label.color || Label::DEFAULT_COLOR - text_color = text_color_for_bg(label_color) - - # Intentionally not using content_tag here so that this method can be called - # by LabelReferenceFilter - span = %() + - escape_once(label.name) + '' - - span.html_safe - end - - def suggested_colors - [ - '#0033CC', - '#428BCA', - '#44AD8E', - '#A8D695', - '#5CB85C', - '#69D100', - '#004E00', - '#34495E', - '#7F8C8D', - '#A295D6', - '#5843AD', - '#8E44AD', - '#FFECDB', - '#AD4363', - '#D10069', - '#CC0033', - '#FF0000', - '#D9534F', - '#D1D100', - '#F0AD4E', - '#AD8D43' - ] - end - - def text_color_for_bg(bg_color) - r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex) - - if (r + g + b) > 500 - '#333333' - else - '#FFFFFF' - end - end - - def project_labels_options(project) - options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) - end - - # Required for Gitlab::Markdown::LabelReferenceFilter - module_function :render_colored_label, :text_color_for_bg, :escape_once -end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb deleted file mode 100644 index f8169b4f288..00000000000 --- a/app/helpers/merge_requests_helper.rb +++ /dev/null @@ -1,74 +0,0 @@ -module MergeRequestsHelper - def new_mr_path_from_push_event(event) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project) - ) - end - - def new_mr_path_for_fork_from_push_event(event) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project) - ) - end - - def new_mr_from_push_event(event, target_project) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_project.repository.root_ref - } - } - end - - def mr_css_classes(mr) - classes = "merge-request" - classes << " closed" if mr.closed? - classes << " merged" if mr.merged? - classes - end - - def ci_build_details_path(merge_request) - merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) - end - - def merge_path_description(merge_request, separator) - if merge_request.for_fork? - "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" - else - "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" - end - end - - def issues_sentence(issues) - issues.map { |i| "##{i.iid}" }.to_sentence - end - - def mr_change_branches_path(merge_request) - new_namespace_project_merge_request_path( - @project.namespace, @project, - merge_request: { - source_project_id: @merge_request.source_project_id, - target_project_id: @merge_request.target_project_id, - source_branch: @merge_request.source_branch, - target_branch: nil - } - ) - end - - def source_branch_with_namespace(merge_request) - if merge_request.for_fork? - namespace = link_to(merge_request.source_project_namespace, - project_path(merge_request.source_project)) - namespace + ":#{merge_request.source_branch}" - else - merge_request.source_branch - end - end -end diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb deleted file mode 100644 index 132a893e532..00000000000 --- a/app/helpers/milestones_helper.rb +++ /dev/null @@ -1,36 +0,0 @@ -module MilestonesHelper - def milestones_filter_path(opts = {}) - if @project - namespace_project_milestones_path(@project.namespace, @project, opts) - elsif @group - group_milestones_path(@group, opts) - else - dashboard_milestones_path(opts) - end - end - - def milestone_progress_bar(milestone) - options = { - class: 'progress-bar progress-bar-success', - style: "width: #{milestone.percent_complete}%;" - } - - content_tag :div, class: 'progress' do - content_tag :div, nil, options - end - end - - def projects_milestones_options - milestones = - if @project - @project.milestones - else - Milestone.where(project_id: @projects) - end.active - - grouped_milestones = Milestones::GroupService.new(milestones).execute - grouped_milestones.unshift(Milestone::None) - - options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) - end -end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb deleted file mode 100644 index b3132a1f3ba..00000000000 --- a/app/helpers/namespaces_helper.rb +++ /dev/null @@ -1,36 +0,0 @@ -module NamespacesHelper - def namespaces_options(selected = :current_user, scope = :default) - groups = current_user.owned_groups + current_user.masters_groups - users = [current_user.namespace] - - group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] - users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] - - options = [] - options << group_opts - options << users_opts - - if selected == :current_user && current_user.namespace - selected = current_user.namespace.id - end - - grouped_options_for_select(options, selected) - end - - def namespace_select_tag(id, opts = {}) - css_class = "ajax-namespace-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end - - def namespace_icon(namespace, size = 40) - if namespace.kind_of?(Group) - group_icon(namespace) - else - avatar_icon(namespace.owner.email, size) - end - end -end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb deleted file mode 100644 index 9b1dd8b8e54..00000000000 --- a/app/helpers/nav_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module NavHelper - def nav_menu_collapsed? - cookies[:collapsed_nav] == 'true' - end - - def nav_sidebar_class - if nav_menu_collapsed? - "page-sidebar-collapsed" - else - "page-sidebar-expanded" - end - end - - def nav_header_class - if nav_menu_collapsed? - "header-collapsed" - else - "header-expanded" - end - end -end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb deleted file mode 100644 index 5f0c921413a..00000000000 --- a/app/helpers/notes_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module NotesHelper - # Helps to distinguish e.g. commit notes in mr notes list - def note_for_main_target?(note) - (@noteable.class.name == note.noteable_type && !note.for_diff_line?) - end - - def note_target_fields(note) - hidden_field_tag(:target_type, note.noteable.class.name.underscore) + - hidden_field_tag(:target_id, note.noteable.id) - end - - def note_editable?(note) - note.editable? && can?(current_user, :admin_note, note) - end - - def link_to_commit_diff_line_note(note) - if note.for_commit_diff_line? - link_to( - "#{note.diff_file_name}:L#{note.diff_new_line}", - namespace_project_commit_path(@project.namespace, @project, - note.noteable, anchor: note.line_code) - ) - end - end - - def noteable_json(noteable) - { - id: noteable.id, - class: noteable.class.name, - resources: noteable.class.table_name, - project_id: noteable.project.id, - }.to_json - end - - def link_to_new_diff_note(line_code, line_type = nil) - discussion_id = Note.build_discussion_id( - @comments_target[:noteable_type], - @comments_target[:noteable_id] || @comments_target[:commit_id], - line_code - ) - - data = { - noteable_type: @comments_target[:noteable_type], - noteable_id: @comments_target[:noteable_id], - commit_id: @comments_target[:commit_id], - line_code: line_code, - discussion_id: discussion_id, - line_type: line_type - } - - button_tag(class: 'btn add-diff-note js-add-diff-note-button', - data: data, - title: 'Add a comment to this line') do - icon('comment-o') - end - end - - def link_to_reply_diff(note, line_type = nil) - return unless current_user - - data = { - noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - line_code: note.line_code, - discussion_id: note.discussion_id, - line_type: line_type - } - - button_tag class: 'btn reply-btn js-discussion-reply-button', - data: data, title: 'Add a reply' do - link_text = icon('comment') - link_text << ' Reply' - end - end -end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb deleted file mode 100644 index 2f8e64c375f..00000000000 --- a/app/helpers/notifications_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -module NotificationsHelper - include IconsHelper - - def notification_icon(notification) - if notification.disabled? - icon('volume-off', class: 'ns-mute') - elsif notification.participating? - icon('volume-down', class: 'ns-part') - elsif notification.watch? - icon('volume-up', class: 'ns-watch') - else - icon('circle-o', class: 'ns-default') - end - end -end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb deleted file mode 100644 index 01b6a63552c..00000000000 --- a/app/helpers/page_layout_helper.rb +++ /dev/null @@ -1,26 +0,0 @@ -module PageLayoutHelper - def page_title(*titles) - @page_title ||= [] - - @page_title.push(*titles.compact) if titles.any? - - @page_title.join(" | ") - end - - def header_title(title = nil, title_url = nil) - if title - @header_title = title - @header_title_url = title_url - else - @header_title_url ? link_to(@header_title, @header_title_url) : @header_title - end - end - - def sidebar(name = nil) - if name - @sidebar = name - else - @sidebar - end - end -end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb deleted file mode 100644 index ea774e28ecf..00000000000 --- a/app/helpers/preferences_helper.rb +++ /dev/null @@ -1,65 +0,0 @@ -# Helper methods for per-User preferences -module PreferencesHelper - COLOR_SCHEMES = { - 1 => 'white', - 2 => 'dark', - 3 => 'solarized-light', - 4 => 'solarized-dark', - 5 => 'monokai', - } - COLOR_SCHEMES.default = 'white' - - # Helper method to access the COLOR_SCHEMES - # - # The keys are the `color_scheme_ids` - # The values are the `name` of the scheme. - # - # The preview images are `name-scheme-preview.png` - # The stylesheets should use the css class `.name` - def color_schemes - COLOR_SCHEMES.freeze - end - - # Maps `dashboard` values to more user-friendly option text - DASHBOARD_CHOICES = { - projects: 'Your Projects (default)', - stars: 'Starred Projects' - }.with_indifferent_access.freeze - - # Returns an Array usable by a select field for more user-friendly option text - def dashboard_choices - defined = User.dashboards - - if defined.size != DASHBOARD_CHOICES.size - # Ensure that anyone adding new options updates this method too - raise RuntimeError, "`User` defines #{defined.size} dashboard choices," + - " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}." - else - defined.map do |key, _| - # Use `fetch` so `KeyError` gets raised when a key is missing - [DASHBOARD_CHOICES.fetch(key), key] - end - end - end - - def project_view_choices - [ - ['Readme (default)', :readme], - ['Activity view', :activity] - ] - end - - def user_application_theme - theme = Gitlab::Themes.by_id(current_user.try(:theme_id)) - theme.css_class - end - - def user_color_scheme_class - COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) - end - - def prefer_readme? - !current_user || - current_user.project_view == 'readme' - end -end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb deleted file mode 100644 index ab9b068de05..00000000000 --- a/app/helpers/projects_helper.rb +++ /dev/null @@ -1,330 +0,0 @@ -module ProjectsHelper - def remove_from_project_team_message(project, member) - if member.user - "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?" - else - "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?" - end - end - - def link_to_project(project) - link_to [project.namespace.becomes(Namespace), project] do - title = content_tag(:span, project.name, class: 'project-name') - - if project.namespace - namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') - title = namespace + title - end - - title - end - end - - def link_to_member(project, author, opts = {}) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } - opts = default_opts.merge(opts) - - return "(deleted)" unless author - - author_html = "" - - # Build avatar image tag - author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] - - # Build name span tag - author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] - - author_html = author_html.html_safe - - if opts[:name] - link_to(author_html, user_path(author), class: "author_link").html_safe - else - link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe - end - end - - def project_title(project) - if project.group - content_tag :span do - link_to( - simple_sanitize(project.group.name), group_path(project.group) - ) + ' / ' + - link_to(simple_sanitize(project.name), - project_path(project)) - end - else - owner = project.namespace.owner - content_tag :span do - link_to( - simple_sanitize(owner.name), user_path(owner) - ) + ' / ' + - link_to(simple_sanitize(project.name), - project_path(project)) - end - end - end - - def remove_project_message(project) - "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" - end - - def transfer_project_message(project) - "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" - end - - def project_nav_tabs - @nav_tabs ||= get_project_nav_tabs(@project, current_user) - end - - def project_nav_tab?(name) - project_nav_tabs.include? name - end - - def project_active_milestones - @project.milestones.active.order("due_date, title ASC") - end - - def project_for_deploy_key(deploy_key) - if deploy_key.projects.include?(@project) - @project - else - deploy_key.projects.find { |project| can?(current_user, :read_project, project) } - end - end - - def can_change_visibility_level?(project, current_user) - return false unless can?(current_user, :change_visibility_level, project) - - if project.forked? - project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE - else - true - end - end - - private - - def get_project_nav_tabs(project, current_user) - nav_tabs = [:home] - - if !project.empty_repo? && can?(current_user, :download_code, project) - nav_tabs << [:files, :commits, :network, :graphs] - end - - if project.repo_exists? && can?(current_user, :read_merge_request, project) - nav_tabs << :merge_requests - end - - if can?(current_user, :admin_project, project) - nav_tabs << :settings - end - - if can?(current_user, :read_issue, project) - nav_tabs << :issues - end - - if can?(current_user, :read_wiki, project) - nav_tabs << :wiki - end - - if can?(current_user, :read_project_snippet, project) - nav_tabs << :snippets - end - - if can?(current_user, :read_label, project) - nav_tabs << :labels - end - - if can?(current_user, :read_milestone, project) - nav_tabs << :milestones - end - - nav_tabs.flatten - end - - def git_user_name - if current_user - current_user.name - else - "Your name" - end - end - - def git_user_email - if current_user - current_user.email - else - "your@email.com" - end - end - - def repository_size(project = nil) - "#{(project || @project).repository_size} MB" - rescue - # In order to prevent 500 error - # when application cannot allocate memory - # to calculate repo size - just show 'Unknown' - 'unknown' - end - - def default_url_to_repo(project = nil) - project = project || @project - current_user ? project.url_to_repo : project.http_url_to_repo - end - - def default_clone_protocol - current_user ? "ssh" : "http" - end - - def project_last_activity(project) - if project.last_activity_at - time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') - else - "Never" - end - end - - def add_contribution_guide_path(project) - if project && !project.repository.contribution_guide - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "CONTRIBUTING.md", - commit_message: "Add contribution guide" - ) - end - end - - def add_changelog_path(project) - if project && !project.repository.changelog - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "CHANGELOG", - commit_message: "Add changelog" - ) - end - end - - def add_license_path(project) - if project && !project.repository.license - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "LICENSE", - commit_message: "Add license" - ) - end - end - - def contribution_guide_path(project) - if project && contribution_guide = project.repository.contribution_guide - namespace_project_blob_path( - project.namespace, - project, - tree_join(project.default_branch, - contribution_guide.name) - ) - end - end - - def readme_path(project) - filename_path(project, :readme) - end - - def changelog_path(project) - filename_path(project, :changelog) - end - - def license_path(project) - filename_path(project, :license) - end - - def version_path(project) - filename_path(project, :version) - end - - def hidden_pass_url(original_url) - result = URI(original_url) - result.password = '*****' unless result.password.nil? - result - rescue - original_url - end - - def project_wiki_path_with_version(proj, page, version, is_newest) - url_params = is_newest ? {} : { version_id: version } - namespace_project_wiki_path(proj.namespace, proj, page, url_params) - end - - def project_status_css_class(status) - case status - when "started" - "active" - when "failed" - "danger" - when "finished" - "success" - end - end - - def user_max_access_in_project(user, project) - level = project.team.max_member_access(user) - - if level - Gitlab::Access.options_with_owner.key(level) - end - end - - def leave_project_message(project) - "Are you sure you want to leave \"#{project.name}\" project?" - end - - def new_readme_path - ref = @repository.root_ref if @repository - ref ||= 'master' - - namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md') - end - - def last_push_event - if current_user - current_user.recent_push(@project.id) - end - end - - def readme_cache_key - sha = @project.commit.try(:sha) || 'nil' - [@project.id, sha, "readme"].join('-') - end - - def round_commit_count(project) - count = project.commit_count - - if count > 10000 - '10000+' - elsif count > 5000 - '5000+' - elsif count > 1000 - '1000+' - else - count - end - end - - private - - def filename_path(project, filename) - if project && blob = project.repository.send(filename) - namespace_project_blob_path( - project.namespace, - project, - tree_join(project.default_branch, - blob.name) - ) - end - end -end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb deleted file mode 100644 index c31a556ff7b..00000000000 --- a/app/helpers/search_helper.rb +++ /dev/null @@ -1,112 +0,0 @@ -module SearchHelper - def search_autocomplete_opts(term) - return unless current_user - - resources_results = [ - groups_autocomplete(term), - projects_autocomplete(term) - ].flatten - - generic_results = project_autocomplete + default_autocomplete + help_autocomplete - generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") } - - [ - resources_results, - generic_results - ].flatten.uniq do |item| - item[:label] - end - end - - private - - # Autocomplete results for various settings pages - def default_autocomplete - [ - { label: "Profile settings", url: profile_path }, - { label: "SSH Keys", url: profile_keys_path }, - { label: "Dashboard", url: root_path }, - { label: "Admin Section", url: admin_root_path }, - ] - end - - # Autocomplete results for internal help pages - def help_autocomplete - [ - { label: "help: API Help", url: help_page_path("api", "README") }, - { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") }, - { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") }, - { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") }, - { label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") }, - { label: "help: SSH Keys Help", url: help_page_path("ssh", "README") }, - { label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") }, - { label: "help: Web Hooks Help", url: help_page_path("web_hooks", "web_hooks") }, - { label: "help: Workflow Help", url: help_page_path("workflow", "README") }, - ] - end - - # Autocomplete results for the current project, if it's defined - def project_autocomplete - if @project && @project.repository.exists? && @project.repository.root_ref - prefix = search_result_sanitize(@project.name_with_namespace) - ref = @ref || @project.repository.root_ref - - [ - { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, - { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, - { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, - { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, - { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, - { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, - ] - else - [] - end - end - - # Autocomplete results for the current user's groups - def groups_autocomplete(term, limit = 5) - current_user.authorized_groups.search(term).limit(limit).map do |group| - { - label: "group: #{search_result_sanitize(group.name)}", - url: group_path(group) - } - end - end - - # Autocomplete results for the current user's projects - def projects_autocomplete(term, limit = 5) - ProjectsFinder.new.execute(current_user).search_by_title(term). - sorted_by_stars.non_archived.limit(limit).map do |p| - { - label: "project: #{search_result_sanitize(p.name_with_namespace)}", - url: namespace_project_path(p.namespace, p) - } - end - end - - def search_result_sanitize(str) - Sanitize.clean(str) - end - - def search_filter_path(options={}) - exist_opts = { - search: params[:search], - project_id: params[:project_id], - group_id: params[:group_id], - scope: params[:scope] - } - - options = exist_opts.merge(options) - search_path(options) - end - - # Sanitize html generated after parsing markdown from issue description or comment - def search_md_sanitize(html) - sanitize(html, tags: %w(a p ol ul li pre code)) - end -end diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb deleted file mode 100644 index 12fce8db701..00000000000 --- a/app/helpers/selects_helper.rb +++ /dev/null @@ -1,45 +0,0 @@ -module SelectsHelper - def users_select_tag(id, opts = {}) - css_class = "ajax-users-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - placeholder = opts[:placeholder] || 'Search for a user' - - null_user = opts[:null_user] || false - any_user = opts[:any_user] || false - email_user = opts[:email_user] || false - first_user = opts[:first_user] && current_user ? current_user.username : false - current_user = opts[:current_user] || false - project = opts[:project] || @project - - html = { - class: css_class, - 'data-placeholder' => placeholder, - 'data-null-user' => null_user, - 'data-any-user' => any_user, - 'data-email-user' => email_user, - 'data-first-user' => first_user, - 'data-current-user' => current_user - } - - unless opts[:scope] == :all - if project - html['data-project-id'] = project.id - elsif @group - html['data-group-id'] = @group.id - end - end - - hidden_field_tag(id, value, html) - end - - def groups_select_tag(id, opts = {}) - css_class = "ajax-groups-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end -end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb deleted file mode 100644 index 906cb12cd48..00000000000 --- a/app/helpers/snippets_helper.rb +++ /dev/null @@ -1,20 +0,0 @@ -module SnippetsHelper - def lifetime_select_options - options = [ - ['forever', nil], - ['1 day', "#{Date.current + 1.day}"], - ['1 week', "#{Date.current + 1.week}"], - ['1 month', "#{Date.current + 1.month}"] - ] - options_for_select(options) - end - - def reliable_snippet_path(snippet) - if snippet.project_id? - namespace_project_snippet_path(snippet.project.namespace, - snippet.project, snippet) - else - snippet_path(snippet) - end - end -end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb deleted file mode 100644 index bb12d43f397..00000000000 --- a/app/helpers/sorting_helper.rb +++ /dev/null @@ -1,96 +0,0 @@ -module SortingHelper - def sort_options_hash - { - sort_value_name => sort_title_name, - sort_value_recently_updated => sort_title_recently_updated, - sort_value_oldest_updated => sort_title_oldest_updated, - sort_value_recently_created => sort_title_recently_created, - sort_value_oldest_created => sort_title_oldest_created, - sort_value_milestone_soon => sort_title_milestone_soon, - sort_value_milestone_later => sort_title_milestone_later, - sort_value_largest_repo => sort_title_largest_repo, - sort_value_recently_signin => sort_title_recently_signin, - sort_value_oldest_signin => sort_title_oldest_signin, - } - end - - def sort_title_oldest_updated - 'Oldest updated' - end - - def sort_title_recently_updated - 'Recently updated' - end - - def sort_title_oldest_created - 'Oldest created' - end - - def sort_title_recently_created - 'Recently created' - end - - def sort_title_milestone_soon - 'Milestone due soon' - end - - def sort_title_milestone_later - 'Milestone due later' - end - - def sort_title_name - 'Name' - end - - def sort_title_largest_repo - 'Largest repository' - end - - def sort_title_recently_signin - 'Recent sign in' - end - - def sort_title_oldest_signin - 'Oldest sign in' - end - - def sort_value_oldest_updated - 'updated_asc' - end - - def sort_value_recently_updated - 'updated_desc' - end - - def sort_value_oldest_created - 'created_asc' - end - - def sort_value_recently_created - 'created_desc' - end - - def sort_value_milestone_soon - 'milestone_due_asc' - end - - def sort_value_milestone_later - 'milestone_due_desc' - end - - def sort_value_name - 'name_asc' - end - - def sort_value_largest_repo - 'repository_size_desc' - end - - def sort_value_recently_signin - 'recent_sign_in' - end - - def sort_value_oldest_signin - 'oldest_sign_in' - end -end diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb deleted file mode 100644 index b3f50ceebe4..00000000000 --- a/app/helpers/submodule_helper.rb +++ /dev/null @@ -1,74 +0,0 @@ -module SubmoduleHelper - include Gitlab::ShellAdapter - - # links to files listing for submodule if submodule is a project on this server - def submodule_links(submodule_item, ref = nil, repository = @repository) - url = repository.submodule_url_for(ref, submodule_item.path) - - return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/ - - namespace = $1 - project = $2 - project.chomp!('.git') - - if self_url?(url, namespace, project) - return namespace_project_path(namespace, project), - namespace_project_tree_path(namespace, project, - submodule_item.id) - elsif relative_self_url?(url) - relative_self_links(url, submodule_item.id) - elsif github_dot_com_url?(url) - standard_links('github.com', namespace, project, submodule_item.id) - elsif gitlab_dot_com_url?(url) - standard_links('gitlab.com', namespace, project, submodule_item.id) - else - return url, nil - end - end - - protected - - def github_dot_com_url?(url) - url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/ - end - - def gitlab_dot_com_url?(url) - url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ - end - - def self_url?(url, namespace, project) - return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', - project, '.git' ].join('') - url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) - end - - def relative_self_url?(url) - # (./)?(../repo.git) || (./)?(../../project/repo.git) ) - url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/ - end - - def standard_links(host, namespace, project, commit) - base = [ 'https://', host, '/', namespace, '/', project ].join('') - [base, [ base, '/tree/', commit ].join('')] - end - - def relative_self_links(url, commit) - # Map relative links to a namespace and project - # For example: - # ../bar.git -> same namespace, repo bar - # ../foo/bar.git -> namespace foo, repo bar - # ../../foo/bar/baz.git -> namespace bar, repo baz - components = url.split('/') - base = components.pop.gsub(/.git$/, '') - namespace = components.pop.gsub(/^\.\.$/, '') - - if namespace.empty? - namespace = @project.namespace.path - end - - [ - namespace_project_path(namespace, base), - namespace_project_tree_path(namespace, base, commit) - ] - end -end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb deleted file mode 100644 index 0e7d8065ac7..00000000000 --- a/app/helpers/tab_helper.rb +++ /dev/null @@ -1,131 +0,0 @@ -module TabHelper - # Navigation link helper - # - # Returns an `li` element with an 'active' class if the supplied - # controller(s) and/or action(s) are currently active. The content of the - # element is the value passed to the block. - # - # options - The options hash used to determine if the element is "active" (default: {}) - # :controller - One or more controller names to check (optional). - # :action - One or more action names to check (optional). - # :path - A shorthand path, such as 'dashboard#index', to check (optional). - # :html_options - Extra options to be passed to the list element (optional). - # block - An optional block that will become the contents of the returned - # `li` element. - # - # When both :controller and :action are specified, BOTH must match in order - # to be marked as active. When only one is given, either can match. - # - # Examples - # - # # Assuming we're on TreeController#show - # - # # Controller matches, but action doesn't - # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Controller matches - # nav_link(controller: [:tree, :refs]) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Several paths - # nav_link(path: ['tree#show', 'profile#show']) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Shorthand path - # nav_link(path: 'tree#show') { "Hello" } - # # => '
  • Hello
  • ' - # - # # Supplying custom options for the list element - # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } - # # => '
  • Hello
  • ' - # - # Returns a list item element String - def nav_link(options = {}, &block) - klass = active_nav_link?(options) ? 'active' : '' - - # Add our custom class into the html_options, which may or may not exist - # and which may or may not already have a :class key - o = options.delete(:html_options) || {} - o[:class] ||= '' - o[:class] += ' ' + klass - o[:class].strip! - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end - - def active_nav_link?(options) - if path = options.delete(:path) - unless path.respond_to?(:each) - path = [path] - end - - path.any? do |single_path| - current_path?(single_path) - end - elsif page = options.delete(:page) - unless page.respond_to?(:each) - page = [page] - end - - page.any? do |single_page| - current_page?(single_page) - end - else - c = options.delete(:controller) - a = options.delete(:action) - - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end - end - - def current_path?(path) - c, a, _ = path.split('#') - current_controller?(c) && current_action?(a) - end - - def project_tab_class - return "active" if current_page?(controller: "/projects", action: :edit, id: @project) - - if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name - "active" - end - end - - def branches_tab_class - if current_controller?(:protected_branches) || - current_controller?(:branches) || - current_page?(namespace_project_repository_path(@project.namespace, - @project)) - 'active' - end - end - - # Use nav_tab for save controller/action but different params - def nav_tab(key, value, &block) - o = {} - o[:class] = "" - - if value.nil? - o[:class] << " active" if params[key].blank? - else - o[:class] << " active" if params[key] == value - end - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end -end diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb deleted file mode 100644 index fb85544df2d..00000000000 --- a/app/helpers/tags_helper.rb +++ /dev/null @@ -1,14 +0,0 @@ -module TagsHelper - def tag_path(tag) - "/tags/#{tag}" - end - - def tag_list(project) - html = '' - project.tag_list.each do |tag| - html << link_to(tag, tag_path(tag)) - end - - html.html_safe - end -end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb deleted file mode 100644 index 03a49e119b8..00000000000 --- a/app/helpers/tree_helper.rb +++ /dev/null @@ -1,88 +0,0 @@ -module TreeHelper - # Sorts a repository's tree so that folders are before files and renders - # their corresponding partials - # - # contents - A Grit::Tree object for the current tree - def render_tree(tree) - # Render Folders before Files/Submodules - folders, files, submodules = tree.trees, tree.blobs, tree.submodules - - tree = "" - - # Render folders if we have any - tree << render(partial: 'projects/tree/tree_item', collection: folders, - locals: { type: 'folder' }) if folders.present? - - # Render files if we have any - tree << render(partial: 'projects/tree/blob_item', collection: files, - locals: { type: 'file' }) if files.present? - - # Render submodules if we have any - tree << render(partial: 'projects/tree/submodule_item', - collection: submodules) if submodules.present? - - tree.html_safe - end - - def render_readme(readme) - render_markup(readme.name, readme.data) - end - - # Return an image icon depending on the file type and mode - # - # type - String type of the tree item; either 'folder' or 'file' - # mode - File unix mode - # name - File name - def tree_icon(type, mode, name) - icon("#{file_type_icon_class(type, mode, name)} fw") - end - - def tree_hex_class(content) - "file_#{hexdigest(content.name)}" - end - - # Simple shortcut to File.join - def tree_join(*args) - File.join(*args) - end - - def allowed_tree_edit?(project = nil, ref = nil) - project ||= @project - ref ||= @ref - return false unless project.repository.branch_names.include?(ref) - - ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) - end - - def tree_breadcrumbs(tree, max_links = 2) - if @path.present? - part_path = "" - parts = @path.split('/') - - yield('..', nil) if parts.count > max_links - - parts.each do |part| - part_path = File.join(part_path, part) unless part_path.empty? - part_path = part if part_path.empty? - - next unless parts.last(2).include?(part) if parts.count > max_links - yield(part, tree_join(@ref, part_path)) - end - end - end - - def up_dir_path - file = File.join(@path, "..") - tree_join(@ref, file) - end - - # returns the relative path of the first subdir that doesn't have only one directory descendant - def flatten_tree(tree) - subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) - if subtree.count == 1 && subtree.first.dir? - return tree_join(tree.name, flatten_tree(subtree.first)) - else - return tree.name - end - end -end diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb deleted file mode 100644 index f64d730b448..00000000000 --- a/app/helpers/version_check_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module VersionCheckHelper - def version_status_badge - if Rails.env.production? - image_tag VersionCheck.new.url - end - end -end diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb deleted file mode 100644 index b52cd23aba2..00000000000 --- a/app/helpers/visibility_level_helper.rb +++ /dev/null @@ -1,95 +0,0 @@ -module VisibilityLevelHelper - def visibility_level_color(level) - case level - when Gitlab::VisibilityLevel::PRIVATE - 'vs-private' - when Gitlab::VisibilityLevel::INTERNAL - 'vs-internal' - when Gitlab::VisibilityLevel::PUBLIC - 'vs-public' - end - end - - # Return the description for the +level+ argument. - # - # +level+ One of the Gitlab::VisibilityLevel constants - # +form_model+ Either a model object (Project, Snippet, etc.) or the name of - # a Project or Snippet class. - def visibility_level_description(level, form_model) - case form_model.is_a?(String) ? form_model : form_model.class.name - when 'PersonalSnippet', 'ProjectSnippet', 'Snippet' - snippet_visibility_level_description(level) - when 'Project' - project_visibility_level_description(level) - end - end - - def project_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "Project access must be granted explicitly for each user." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The project can be cloned by" - haml_concat "any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The project can be cloned" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def snippet_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "The snippet is visible only for me." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The snippet is visible for any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The snippet can be accessed" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def visibility_level_icon(level) - case level - when Gitlab::VisibilityLevel::PRIVATE - private_icon - when Gitlab::VisibilityLevel::INTERNAL - internal_icon - when Gitlab::VisibilityLevel::PUBLIC - public_icon - end - end - - def visibility_level_label(level) - Project.visibility_levels.key(level) - end - - def restricted_visibility_levels(show_all = false) - return [] if current_user.is_admin? && !show_all - current_application_settings.restricted_visibility_levels || [] - end - - def default_project_visibility - current_application_settings.default_project_visibility - end - - def default_snippet_visibility - current_application_settings.default_snippet_visibility - end - - def skip_level?(form_model, level) - form_model.is_a?(Project) && - form_model.forked? && - !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level) - end -end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb deleted file mode 100644 index f8a96516e61..00000000000 --- a/app/helpers/wiki_helper.rb +++ /dev/null @@ -1,24 +0,0 @@ -module WikiHelper - # Rails v4.1.9+ escapes all model IDs, converting slashes into %2F. The - # only way around this is to implement our own path generators. - def namespace_project_wiki_path(namespace, project, wiki_page, *args) - slug = - case wiki_page - when Symbol - wiki_page - when String - wiki_page - else - wiki_page.slug - end - namespace_project_path(namespace, project) + "/wikis/#{slug}" - end - - def edit_namespace_project_wiki_path(namespace, project, wiki_page, *args) - namespace_project_wiki_path(namespace, project, wiki_page) + '/edit' - end - - def history_namespace_project_wiki_path(namespace, project, wiki_page, *args) - namespace_project_wiki_path(namespace, project, wiki_page) + '/history' - end -end diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb index aedb0889185..2b650bc6eac 100644 --- a/app/mailers/base_mailer.rb +++ b/app/mailers/base_mailer.rb @@ -1,6 +1,6 @@ class BaseMailer < ActionMailer::Base - add_template_helper ApplicationHelper - add_template_helper GitlabMarkdownHelper + add_template_helper Gitlab::ApplicationHelper + add_template_helper Gitlab::GitlabMarkdownHelper attr_accessor :current_user helper_method :current_user, :can? diff --git a/app/mailers/ci/emails/builds.rb b/app/mailers/ci/emails/builds.rb new file mode 100644 index 00000000000..6fb4fba85e5 --- /dev/null +++ b/app/mailers/ci/emails/builds.rb @@ -0,0 +1,17 @@ +module Ci + module Emails + module Builds + def build_fail_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha)) + end + + def build_success_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha)) + end + end + end +end diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb new file mode 100644 index 00000000000..44e490e9b36 --- /dev/null +++ b/app/mailers/ci/notify.rb @@ -0,0 +1,47 @@ +module Ci + class Notify < ActionMailer::Base + include Ci::Emails::Builds + + add_template_helper Ci::ApplicationHelper + add_template_helper Ci::GitlabHelper + + default_url_options[:host] = GitlabCi.config.gitlab_ci.host + default_url_options[:protocol] = GitlabCi.config.gitlab_ci.protocol + default_url_options[:port] = GitlabCi.config.gitlab_ci.port if GitlabCi.config.gitlab_ci_on_non_standard_port? + default_url_options[:script_name] = GitlabCi.config.gitlab_ci.relative_url_root + + default from: GitlabCi.config.gitlab_ci.email_from + + # Just send email with 3 seconds delay + def self.delay + delay_for(2.seconds) + end + + private + + # Formats arguments into a String suitable for use as an email subject + # + # extra - Extra Strings to be inserted into the subject + # + # Examples + # + # >> subject('Lorem ipsum') + # => "GitLab-CI | Lorem ipsum" + # + # # Automatically inserts Project name when @project is set + # >> @project = Project.last + # => # + # >> subject('Lorem ipsum') + # => "GitLab-CI | Ruby on Rails | Lorem ipsum " + # + # # Accepts multiple arguments + # >> subject('Lorem ipsum', 'Dolor sit amet') + # => "GitLab-CI | Lorem ipsum | Dolor sit amet" + def subject(*extra) + subject = "GitLab-CI" + subject << (@project ? " | #{@project.name}" : "") + subject << " | " + extra.join(' | ') if extra.present? + subject + end + end +end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 5717c89e61d..38afb49c78c 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -8,8 +8,8 @@ class Notify < BaseMailer include Emails::Profile include Emails::Groups - add_template_helper MergeRequestsHelper - add_template_helper EmailsHelper + add_template_helper Gitlab::MergeRequestsHelper + add_template_helper Gitlab::EmailsHelper def test_email(recipient_email, subject, body) mail(to: recipient_email, diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb new file mode 100644 index 00000000000..0ea2452e392 --- /dev/null +++ b/app/models/ci/application_setting.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: application_settings +# +# id :integer not null, primary key +# all_broken_builds :boolean +# add_pusher :boolean +# created_at :datetime +# updated_at :datetime +# + +module Ci + class ApplicationSetting < ActiveRecord::Base + extend Ci::Model + + def self.current + Ci::ApplicationSetting.last + end + + def self.create_from_defaults + create( + all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'], + add_pusher: Ci::Settings.gitlab_ci['add_pusher'], + ) + end + end +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb new file mode 100644 index 00000000000..64e7a600672 --- /dev/null +++ b/app/models/ci/build.rb @@ -0,0 +1,285 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# deploy :boolean default(FALSE) +# trigger_request_id :integer +# + +module Ci + class Build < ActiveRecord::Base + extend Ci::Model + + LAZY_ATTRIBUTES = ['trace'] + + belongs_to :commit, class_name: 'Ci::Commit' + belongs_to :project, class_name: 'Ci::Project' + belongs_to :runner, class_name: 'Ci::Runner' + belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' + + serialize :options + + validates :commit, presence: true + validates :status, presence: true + validates :coverage, numericality: true, allow_blank: true + + scope :running, ->() { where(status: "running") } + scope :pending, ->() { where(status: "pending") } + scope :success, ->() { where(status: "success") } + scope :failed, ->() { where(status: "failed") } + scope :unstarted, ->() { where(runner_id: nil) } + scope :running_or_pending, ->() { where(status:[:running, :pending]) } + + acts_as_taggable + + # To prevent db load megabytes of data from trace + default_scope -> { select(Ci::Build.columns_without_lazy) } + + class << self + def columns_without_lazy + (column_names - LAZY_ATTRIBUTES).map do |column_name| + "#{table_name}.#{column_name}" + end + end + + def last_month + where('created_at > ?', Date.today - 1.month) + end + + def first_pending + pending.unstarted.order('created_at ASC').first + end + + def create_from(build) + new_build = build.dup + new_build.status = :pending + new_build.runner_id = nil + new_build.save + end + + def retry(build) + new_build = Ci::Build.new(status: :pending) + new_build.options = build.options + new_build.commands = build.commands + new_build.tag_list = build.tag_list + new_build.commit_id = build.commit_id + new_build.project_id = build.project_id + new_build.name = build.name + new_build.allow_failure = build.allow_failure + new_build.stage = build.stage + new_build.trigger_request = build.trigger_request + new_build.save + new_build + end + end + + state_machine :status, initial: :pending do + event :run do + transition pending: :running + end + + event :drop do + transition running: :failed + end + + event :success do + transition running: :success + end + + event :cancel do + transition [:pending, :running] => :canceled + end + + after_transition pending: :running do |build, transition| + build.update_attributes started_at: Time.now + end + + after_transition any => [:success, :failed, :canceled] do |build, transition| + build.update_attributes finished_at: Time.now + project = build.project + + if project.web_hooks? + Ci::WebHookService.new.build_end(build) + end + + if build.commit.success? + build.commit.create_next_builds(build.trigger_request) + end + + project.execute_services(build) + + if project.coverage_enabled? + build.update_coverage + end + end + + state :pending, value: 'pending' + state :running, value: 'running' + state :failed, value: 'failed' + state :success, value: 'success' + state :canceled, value: 'canceled' + end + + delegate :sha, :short_sha, :before_sha, :ref, + to: :commit, prefix: false + + def trace_html + html = Ci::Ansi2html::convert(trace) if trace.present? + html ||= '' + end + + def trace + if project && read_attribute(:trace).present? + read_attribute(:trace).gsub(project.token, 'xxxxxx') + end + end + + def started? + !pending? && !canceled? && started_at + end + + def active? + running? || pending? + end + + def complete? + canceled? || success? || failed? + end + + def ignored? + failed? && allow_failure? + end + + def timeout + project.timeout + end + + def variables + yaml_variables + project_variables + trigger_variables + end + + def duration + if started_at && finished_at + finished_at - started_at + elsif started_at + Time.now - started_at + end + end + + def project + commit.project + end + + def project_id + commit.project_id + end + + def project_name + project.name + end + + def repo_url + project.repo_url_with_auth + end + + def allow_git_fetch + project.allow_git_fetch + end + + def update_coverage + coverage = extract_coverage(trace, project.coverage_regex) + + if coverage.is_a? Numeric + update_attributes(coverage: coverage) + end + end + + def extract_coverage(text, regex) + begin + matches = text.gsub(Regexp.new(regex)).to_a.last + coverage = matches.gsub(/\d+(\.\d+)?/).first + + if coverage.present? + coverage.to_f + end + rescue => ex + # if bad regex or something goes wrong we dont want to interrupt transition + # so we just silentrly ignore error for now + end + end + + def trace + if File.exist?(path_to_trace) + File.read(path_to_trace) + else + # backward compatibility + read_attribute :trace + end + end + + def trace=(trace) + unless Dir.exists? dir_to_trace + FileUtils.mkdir_p dir_to_trace + end + + File.write(path_to_trace, trace) + end + + def dir_to_trace + File.join( + Ci::Settings.gitlab_ci.builds_path, + created_at.utc.strftime("%Y_%m"), + project.id.to_s + ) + end + + def path_to_trace + "#{dir_to_trace}/#{id}.log" + end + + private + + def yaml_variables + if commit.config_processor + commit.config_processor.variables.map do |key, value| + { key: key, value: value, public: true } + end + else + [] + end + end + + def project_variables + project.variables.map do |variable| + { key: variable.key, value: variable.value, public: false } + end + end + + def trigger_variables + if trigger_request && trigger_request.variables + trigger_request.variables.map do |key, value| + { key: key, value: value, public: false } + end + else + [] + end + end + end +end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb new file mode 100644 index 00000000000..23cd47dfe37 --- /dev/null +++ b/app/models/ci/commit.rb @@ -0,0 +1,267 @@ +# == Schema Information +# +# Table name: commits +# +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# + +module Ci + class Commit < ActiveRecord::Base + extend Ci::Model + + belongs_to :project, class_name: 'Ci::Project' + has_many :builds, dependent: :destroy, class_name: 'Ci::Build' + has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' + + serialize :push_data + + validates_presence_of :ref, :sha, :before_sha, :push_data + validate :valid_commit_sha + + def self.truncate_sha(sha) + sha[0...8] + end + + def to_param + sha + end + + def last_build + builds.order(:id).last + end + + def retry + builds_without_retry.each do |build| + Ci::Build.retry(build) + end + end + + def valid_commit_sha + if self.sha == Ci::Git::BLANK_SHA + self.errors.add(:sha, " cant be 00000000 (branch removal)") + end + end + + def new_branch? + before_sha == Ci::Git::BLANK_SHA + end + + def compare? + !new_branch? + end + + def git_author_name + commit_data[:author][:name] if commit_data && commit_data[:author] + end + + def git_author_email + commit_data[:author][:email] if commit_data && commit_data[:author] + end + + def git_commit_message + commit_data[:message] if commit_data && commit_data[:message] + end + + def short_before_sha + Ci::Commit.truncate_sha(before_sha) + end + + def short_sha + Ci::Commit.truncate_sha(sha) + end + + def commit_data + push_data[:commits].find do |commit| + commit[:id] == sha + end + rescue + nil + end + + def project_recipients + recipients = project.email_recipients.split(' ') + + if project.email_add_pusher? && push_data[:user_email].present? + recipients << push_data[:user_email] + end + + recipients.uniq + end + + def stage + return unless config_processor + stages = builds_without_retry.select(&:active?).map(&:stage) + config_processor.stages.find { |stage| stages.include? stage } + end + + def create_builds_for_stage(stage, trigger_request) + return if skip_ci? && trigger_request.blank? + return unless config_processor + + builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag) + builds_attrs.map do |build_attrs| + builds.create!({ + project: project, + name: build_attrs[:name], + commands: build_attrs[:script], + tag_list: build_attrs[:tags], + options: build_attrs[:options], + allow_failure: build_attrs[:allow_failure], + stage: build_attrs[:stage], + trigger_request: trigger_request, + }) + end + end + + def create_next_builds(trigger_request) + return if skip_ci? && trigger_request.blank? + return unless config_processor + + stages = builds.where(trigger_request: trigger_request).group_by(&:stage) + + config_processor.stages.any? do |stage| + !stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present? + end + end + + def create_builds(trigger_request = nil) + return if skip_ci? && trigger_request.blank? + return unless config_processor + + config_processor.stages.any? do |stage| + create_builds_for_stage(stage, trigger_request).present? + end + end + + def builds_without_retry + @builds_without_retry ||= + begin + grouped_builds = builds.group_by(&:name) + grouped_builds.map do |name, builds| + builds.sort_by(&:id).last + end + end + end + + def builds_without_retry_sorted + return builds_without_retry unless config_processor + + stages = config_processor.stages + builds_without_retry.sort_by do |build| + [stages.index(build.stage) || -1, build.name || ""] + end + end + + def retried_builds + @retried_builds ||= (builds.order(id: :desc) - builds_without_retry) + end + + def status + if skip_ci? + return 'skipped' + elsif yaml_errors.present? + return 'failed' + elsif builds.none? + return 'skipped' + elsif success? + 'success' + elsif pending? + 'pending' + elsif running? + 'running' + elsif canceled? + 'canceled' + else + 'failed' + end + end + + def pending? + builds_without_retry.all? do |build| + build.pending? + end + end + + def running? + builds_without_retry.any? do |build| + build.running? || build.pending? + end + end + + def success? + builds_without_retry.all? do |build| + build.success? || build.ignored? + end + end + + def failed? + status == 'failed' + end + + def canceled? + builds_without_retry.all? do |build| + build.canceled? + end + end + + def duration + @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i + end + + def finished_at + @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at) + end + + def coverage + if project.coverage_enabled? + coverage_array = builds_without_retry.map(&:coverage).compact + if coverage_array.size >= 1 + '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) + end + end + end + + def matrix? + builds_without_retry.size > 1 + end + + def config_processor + @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file] || project.generated_yaml_config) + rescue Ci::GitlabCiYamlProcessor::ValidationError => e + save_yaml_error(e.message) + nil + rescue Exception => e + logger.error e.message + "\n" + e.backtrace.join("\n") + save_yaml_error("Undefined yaml error") + nil + end + + def skip_ci? + return false if builds.any? + commits = push_data[:commits] + commits.present? && commits.last[:message] =~ /(\[ci skip\])/ + end + + def update_committed! + update!(committed_at: DateTime.now) + end + + private + + def save_yaml_error(error) + return if self.yaml_errors? + self.yaml_errors = error + save + end + end +end diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb new file mode 100644 index 00000000000..cac3a7a49c1 --- /dev/null +++ b/app/models/ci/event.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: events +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# is_admin :integer +# description :text +# created_at :datetime +# updated_at :datetime +# + +module Ci + class Event < ActiveRecord::Base + extend Ci::Model + + belongs_to :project, class_name: 'Ci::Project' + + validates :description, + presence: true, + length: { in: 5..200 } + + scope :admin, ->(){ where(is_admin: true) } + scope :project_wide, ->(){ where(is_admin: false) } + end +end diff --git a/app/models/ci/network.rb b/app/models/ci/network.rb new file mode 100644 index 00000000000..c307907e6b8 --- /dev/null +++ b/app/models/ci/network.rb @@ -0,0 +1,122 @@ +module Ci + class Network + class UnauthorizedError < StandardError; end + + include HTTParty + + API_PREFIX = '/api/v3/' + + def authenticate(api_opts) + opts = { + query: api_opts + } + + endpoint = File.join(url, API_PREFIX, 'user') + response = self.class.get(endpoint, default_opts.merge(opts)) + + build_response(response) + end + + def projects(api_opts, scope = :owned) + # Dont load archived projects + api_opts.merge!(archived: false) + + opts = { + query: api_opts + } + + query = if scope == :owned + 'projects/owned.json' + else + 'projects.json' + end + + endpoint = File.join(url, API_PREFIX, query) + response = self.class.get(endpoint, default_opts.merge(opts)) + + build_response(response) + end + + def project(api_opts, project_id) + opts = { + query: api_opts + } + + query = "projects/#{project_id}.json" + + endpoint = File.join(url, API_PREFIX, query) + response = self.class.get(endpoint, default_opts.merge(opts)) + + build_response(response) + end + + def project_hooks(api_opts, project_id) + opts = { + query: api_opts + } + + query = "projects/#{project_id}/hooks.json" + + endpoint = File.join(url, API_PREFIX, query) + response = self.class.get(endpoint, default_opts.merge(opts)) + + build_response(response) + end + + def enable_ci(project_id, data, api_opts) + opts = { + body: data.to_json, + query: api_opts + } + + query = "projects/#{project_id}/services/gitlab-ci.json" + endpoint = File.join(url, API_PREFIX, query) + response = self.class.put(endpoint, default_opts.merge(opts)) + + case response.code + when 200 + true + when 401 + raise UnauthorizedError + else + nil + end + end + + def disable_ci(project_id, api_opts) + opts = { + query: api_opts + } + + query = "projects/#{project_id}/services/gitlab-ci.json" + + endpoint = File.join(url, API_PREFIX, query) + response = self.class.delete(endpoint, default_opts.merge(opts)) + + build_response(response) + end + + private + + def url + GitlabCi.config.gitlab_server.url + end + + def default_opts + { + headers: { "Content-Type" => "application/json" }, + } + end + + def build_response(response) + case response.code + when 200 + response.parsed_response + when 401 + raise UnauthorizedError + else + nil + end + end + end +end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb new file mode 100644 index 00000000000..dceca7a275a --- /dev/null +++ b/app/models/ci/project.rb @@ -0,0 +1,221 @@ +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) not null +# timeout :integer default(3600), not null +# created_at :datetime +# updated_at :datetime +# token :string(255) +# default_ref :string(255) +# path :string(255) +# always_build :boolean default(FALSE), not null +# polling_interval :integer +# public :boolean default(FALSE), not null +# ssh_url_to_repo :string(255) +# gitlab_id :integer +# allow_git_fetch :boolean default(TRUE), not null +# email_recipients :string(255) default(""), not null +# email_add_pusher :boolean default(TRUE), not null +# email_only_broken_builds :boolean default(TRUE), not null +# skip_refs :string(255) +# coverage_regex :string(255) +# shared_runners_enabled :boolean default(FALSE) +# generated_yaml_config :text +# + +module Ci + class Project < ActiveRecord::Base + extend Ci::Model + + include Ci::ProjectStatus + + has_many :commits, ->() { order(:committed_at) }, dependent: :destroy, class_name: 'Ci::Commit' + has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' + has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' + has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' + has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook' + has_many :events, dependent: :destroy, class_name: 'Ci::Event' + has_many :variables, dependent: :destroy, class_name: 'Ci::Variable' + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' + + # Project services + has_many :services, dependent: :destroy, class_name: 'Ci::Service' + has_one :hip_chat_service, dependent: :destroy, class_name: 'Ci::HipChatService' + has_one :slack_service, dependent: :destroy, class_name: 'Ci::SlackService' + has_one :mail_service, dependent: :destroy, class_name: 'Ci::MailService' + + accepts_nested_attributes_for :variables, allow_destroy: true + + # + # Validations + # + validates_presence_of :name, :timeout, :token, :default_ref, + :path, :ssh_url_to_repo, :gitlab_id + + validates_uniqueness_of :gitlab_id + + validates :polling_interval, + presence: true, + if: ->(project) { project.always_build.present? } + + scope :public_only, ->() { where(public: true) } + + before_validation :set_default_values + + class << self + include Ci::CurrentSettings + + def base_build_script + <<-eos + git submodule update --init + ls -la + eos + end + + def parse(project) + params = { + name: project.name_with_namespace, + gitlab_id: project.id, + path: project.path_with_namespace, + default_ref: project.default_branch || 'master', + ssh_url_to_repo: project.ssh_url_to_repo, + email_add_pusher: current_application_settings.add_pusher, + email_only_broken_builds: current_application_settings.all_broken_builds, + } + + project = Ci::Project.new(params) + project.build_missing_services + project + end + + def from_gitlab(user, scope = :owned, options) + opts = user.authenticate_options + opts.merge! options + + projects = Ci::Network.new.projects(opts.compact, scope) + + if projects + projects.map { |pr| OpenStruct.new(pr) } + else + [] + end + end + + def already_added?(project) + where(gitlab_id: project.id).any? + end + + def unassigned(runner) + joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \ + "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}"). + where('#{Ci::RunnerProject.table_name}.project_id' => nil) + end + + def ordered_by_last_commit_date + last_commit_subquery = "(SELECT project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY project_id)" + joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.id = last_commit.project_id"). + order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") + end + + def search(query) + where("LOWER(#{Ci::Project.table_name}.name) LIKE :query", + query: "%#{query.try(:downcase)}%") + end + end + + def any_runners? + if runners.active.any? + return true + end + + shared_runners_enabled && Ci::Runner.shared.active.any? + end + + def set_default_values + self.token = SecureRandom.hex(15) if self.token.blank? + end + + def tracked_refs + @tracked_refs ||= default_ref.split(",").map{|ref| ref.strip} + end + + def valid_token? token + self.token && self.token == token + end + + def no_running_builds? + # Get running builds not later than 3 days ago to ignore hangs + builds.running.where("updated_at > ?", 3.days.ago).empty? + end + + def email_notification? + email_add_pusher || email_recipients.present? + end + + def web_hooks? + web_hooks.any? + end + + def services? + services.any? + end + + def timeout_in_minutes + timeout / 60 + end + + def timeout_in_minutes=(value) + self.timeout = value.to_i * 60 + end + + def coverage_enabled? + coverage_regex.present? + end + + # Build a clone-able repo url + # using http and basic auth + def repo_url_with_auth + auth = "gitlab-ci-token:#{token}@" + url = gitlab_url + ".git" + url.sub(/^https?:\/\//) do |prefix| + prefix + auth + end + end + + def available_services_names + %w(slack mail hip_chat) + end + + def build_missing_services + available_services_names.each do |service_name| + service = services.find { |service| service.to_param == service_name } + + # If service is available but missing in db + # we should create an instance. Ex `create_gitlab_ci_service` + service = self.send :"create_#{service_name}_service" if service.nil? + end + end + + def execute_services(data) + services.each do |service| + + # Call service hook only if it is active + begin + service.execute(data) if service.active && service.can_execute?(data) + rescue => e + logger.error(e) + end + end + end + + def gitlab_url + File.join(GitlabCi.config.gitlab_server.url, path) + end + + def setup_finished? + commits.any? + end + end +end diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb new file mode 100644 index 00000000000..6d5cafe81a2 --- /dev/null +++ b/app/models/ci/project_status.rb @@ -0,0 +1,47 @@ +module Ci + module ProjectStatus + def status + last_commit.status if last_commit + end + + def broken? + last_commit.failed? if last_commit + end + + def success? + last_commit.success? if last_commit + end + + def broken_or_success? + broken? || success? + end + + def last_commit + @last_commit ||= commits.last if commits.any? + end + + def last_commit_date + last_commit.try(:created_at) + end + + def human_status + status + end + + # only check for toggling build status within same ref. + def last_commit_changed_status? + ref = last_commit.ref + last_commits = commits.where(ref: ref).last(2) + + if last_commits.size < 2 + false + else + last_commits[0].status != last_commits[1].status + end + end + + def last_commit_for_ref(ref) + commits.where(ref: ref).last + end + end +end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb new file mode 100644 index 00000000000..79c81df5eb2 --- /dev/null +++ b/app/models/ci/runner.rb @@ -0,0 +1,80 @@ +# == Schema Information +# +# Table name: runners +# +# id :integer not null, primary key +# token :string(255) +# created_at :datetime +# updated_at :datetime +# description :string(255) +# contacted_at :datetime +# active :boolean default(TRUE), not null +# is_shared :boolean default(FALSE) +# name :string(255) +# version :string(255) +# revision :string(255) +# platform :string(255) +# architecture :string(255) +# + +module Ci + class Runner < ActiveRecord::Base + extend Ci::Model + + has_many :builds, class_name: 'Ci::Build' + has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' + has_many :projects, through: :runner_projects, class_name: 'Ci::Project' + + has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' + + before_validation :set_default_values + + scope :specific, ->() { where(is_shared: false) } + scope :shared, ->() { where(is_shared: true) } + scope :active, ->() { where(active: true) } + scope :paused, ->() { where(active: false) } + + acts_as_taggable + + def self.search(query) + where('LOWER(runners.token) LIKE :query OR LOWER(runners.description) like :query', + query: "%#{query.try(:downcase)}%") + end + + def set_default_values + self.token = SecureRandom.hex(15) if self.token.blank? + end + + def assign_to(project, current_user = nil) + self.is_shared = false if shared? + self.save + project.runner_projects.create!(runner_id: self.id) + end + + def display_name + return token unless !description.blank? + + description + end + + def shared? + is_shared + end + + def belongs_to_one_project? + runner_projects.count == 1 + end + + def specific? + !shared? + end + + def only_for?(project) + projects == [project] + end + + def short_sha + token[0...10] + end + end +end diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb new file mode 100644 index 00000000000..44453ee4b41 --- /dev/null +++ b/app/models/ci/runner_project.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: runner_projects +# +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +module Ci + class RunnerProject < ActiveRecord::Base + extend Ci::Model + + belongs_to :runner, class_name: 'Ci::Runner' + belongs_to :project, class_name: 'Ci::Project' + + validates_uniqueness_of :runner_id, scope: :project_id + end +end diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb new file mode 100644 index 00000000000..ed5e3f940b6 --- /dev/null +++ b/app/models/ci/service.rb @@ -0,0 +1,105 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +# To add new service you should build a class inherited from Service +# and implement a set of methods +module Ci + class Service < ActiveRecord::Base + extend Ci::Model + + serialize :properties, JSON + + default_value_for :active, false + + after_initialize :initialize_properties + + belongs_to :project, class_name: 'Ci::Project' + + validates :project_id, presence: true + + def activated? + active + end + + def category + :common + end + + def initialize_properties + self.properties = {} if properties.nil? + end + + def title + # implement inside child + end + + def description + # implement inside child + end + + def help + # implement inside child + end + + def to_param + # implement inside child + end + + def fields + # implement inside child + [] + end + + def can_test? + project.builds.any? + end + + def can_execute?(build) + true + end + + def execute(build) + # implement inside child + end + + # Provide convenient accessor methods + # for each serialized property. + def self.prop_accessor(*args) + args.each do |arg| + class_eval %{ + def #{arg} + (properties || {})['#{arg}'] + end + + def #{arg}=(value) + self.properties ||= {} + self.properties['#{arg}'] = value + end + } + end + end + + def self.boolean_accessor(*args) + self.prop_accessor(*args) + + args.each do |arg| + class_eval %{ + def #{arg}? + ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) + end + } + end + end + end +end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb new file mode 100644 index 00000000000..84eab91e8ba --- /dev/null +++ b/app/models/ci/trigger.rb @@ -0,0 +1,39 @@ +# == Schema Information +# +# Table name: triggers +# +# id :integer not null, primary key +# token :string(255) +# project_id :integer not null +# deleted_at :datetime +# created_at :datetime +# updated_at :datetime +# + +module Ci + class Trigger < ActiveRecord::Base + extend Ci::Model + + acts_as_paranoid + + belongs_to :project, class_name: 'Ci::Trigger' + has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' + + validates_presence_of :token + validates_uniqueness_of :token + + before_validation :set_default_values + + def set_default_values + self.token = SecureRandom.hex(15) if self.token.blank? + end + + def last_trigger_request + trigger_requests.last + end + + def short_token + token[0...10] + end + end +end diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb new file mode 100644 index 00000000000..29cd9553394 --- /dev/null +++ b/app/models/ci/trigger_request.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: trigger_requests +# +# id :integer not null, primary key +# trigger_id :integer not null +# variables :text +# created_at :datetime +# updated_at :datetime +# commit_id :integer +# + +module Ci + class TriggerRequest < ActiveRecord::Base + extend Ci::Model + + belongs_to :trigger, class_name: 'Ci::Trigger' + belongs_to :commit, class_name: 'Ci::Commit' + has_many :builds, class_name: 'Ci::Build' + + serialize :variables + end +end diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb new file mode 100644 index 00000000000..7456bd1a77b --- /dev/null +++ b/app/models/ci/user.rb @@ -0,0 +1,97 @@ +# User object is stored in session +module Ci + class User + DEVELOPER_ACCESS = 30 + + attr_reader :attributes + + def initialize(hash) + @attributes = hash + end + + def gitlab_projects(search = nil, page = 1, per_page = 100) + Rails.cache.fetch(cache_key(page, per_page, search)) do + Ci::Project.from_gitlab(self, :authorized, { page: page, per_page: per_page, search: search, ci_enabled_first: true }) + end + end + + def method_missing(meth, *args, &block) + if attributes.has_key?(meth.to_s) + attributes[meth.to_s] + else + super + end + end + + def avatar_url + attributes['avatar_url'] + end + + def cache_key(*args) + "#{self.id}:#{args.join(":")}:#{sync_at.to_s}" + end + + def sync_at + @sync_at ||= Time.now + end + + def reset_cache + @sync_at = Time.now + end + + def can_access_project?(project_gitlab_id) + !!project_info(project_gitlab_id) + end + + # Indicate if user has developer access or higher + def has_developer_access?(project_gitlab_id) + data = project_info(project_gitlab_id) + + return false unless data && data["permissions"] + + permissions = data["permissions"] + + if permissions["project_access"] && permissions["project_access"]["access_level"] >= DEVELOPER_ACCESS + return true + end + + if permissions["group_access"] && permissions["group_access"]["access_level"] >= DEVELOPER_ACCESS + return true + end + end + + def can_manage_project?(project_gitlab_id) + Rails.cache.fetch(cache_key('manage', project_gitlab_id, sync_at)) do + !!Ci::Network.new.project_hooks(authenticate_options, project_gitlab_id) + end + end + + def authorized_runners + Ci::Runner.specific.includes(:runner_projects). + where(runner_projects: { project_id: authorized_projects } ) + end + + def authorized_projects + Ci::Project.where(gitlab_id: gitlab_projects.map(&:id)).select do |project| + # This is slow: it makes request to GitLab for each project to verify manage permission + can_manage_project?(project.gitlab_id) + end + end + + def authenticate_options + if attributes['access_token'] + { access_token: attributes['access_token'] } + else + { private_token: attributes['private_token'] } + end + end + + private + + def project_info(project_gitlab_id) + Rails.cache.fetch(cache_key("project_info", project_gitlab_id, sync_at)) do + Ci::Network.new.project(authenticate_options, project_gitlab_id) + end + end + end +end diff --git a/app/models/ci/user_session.rb b/app/models/ci/user_session.rb new file mode 100644 index 00000000000..27c71e30591 --- /dev/null +++ b/app/models/ci/user_session.rb @@ -0,0 +1,23 @@ +module Ci + class UserSession + include ActiveModel::Conversion + include Ci::StaticModel + extend ActiveModel::Naming + + def authenticate(auth_opts) + network = Ci::Network.new + user = network.authenticate(auth_opts) + + if user + user["access_token"] = auth_opts[:access_token] + return Ci::User.new(user) + else + nil + end + + user + rescue + nil + end + end +end diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb new file mode 100644 index 00000000000..7a542802fa6 --- /dev/null +++ b/app/models/ci/variable.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: variables +# +# id :integer not null, primary key +# project_id :integer not null +# key :string(255) +# value :text +# encrypted_value :text +# encrypted_value_salt :string(255) +# encrypted_value_iv :string(255) +# + +module Ci + class Variable < ActiveRecord::Base + extend Ci::Model + + belongs_to :project, class_name: 'Ci::Project' + + validates_presence_of :key + validates_uniqueness_of :key, scope: :project_id + + attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base + end +end diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb new file mode 100644 index 00000000000..4b8c65a1a65 --- /dev/null +++ b/app/models/ci/web_hook.rb @@ -0,0 +1,44 @@ +# == Schema Information +# +# Table name: web_hooks +# +# id :integer not null, primary key +# url :string(255) not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +module Ci + class WebHook < ActiveRecord::Base + extend Ci::Model + + include HTTParty + + belongs_to :project, class_name: 'Ci::WebHook' + + # HTTParty timeout + default_timeout 10 + + validates :url, presence: true, + format: { with: URI::regexp(%w(http https)), message: "should be a valid url" } + + def execute(data) + parsed_url = URI.parse(url) + if parsed_url.userinfo.blank? + Ci::WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false) + else + post_url = url.gsub("#{parsed_url.userinfo}@", "") + auth = { + username: URI.decode(parsed_url.user), + password: URI.decode(parsed_url.password), + } + Ci::WebHook.post(post_url, + body: data.to_json, + headers: { "Content-Type" => "application/json" }, + verify: false, + basic_auth: auth) + end + end + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 69f9af91c51..f14cd884c89 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -316,7 +316,7 @@ class Project < ActiveRecord::Base end def web_url - Rails.application.routes.url_helpers.namespace_project_url(self.namespace, self) + Gitlab::Application.routes.url_helpers.namespace_project_url(self.namespace, self) end def web_url_without_protocol @@ -433,7 +433,7 @@ class Project < ActiveRecord::Base if avatar.present? [gitlab_config.url, avatar.url].join elsif avatar_in_git - Rails.application.routes.url_helpers.namespace_project_avatar_url(namespace, self) + Gitlab::Application.routes.url_helpers.namespace_project_avatar_url(namespace, self) end end diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb new file mode 100644 index 00000000000..3e9f99e7eaf --- /dev/null +++ b/app/models/project_services/ci/hip_chat_message.rb @@ -0,0 +1,78 @@ +module Ci + class HipChatMessage + attr_reader :build + + def initialize(build) + @build = build + end + + def to_s + lines = Array.new + lines.push("#{project.name} - ") + + if commit.matrix? + lines.push("Commit ##{commit.id}
    ") + else + first_build = commit.builds_without_retry.first + lines.push("Build '#{first_build.name}' ##{first_build.id}
    ") + end + + lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}
    ") + lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).") + lines.join('') + end + + def status_color(build_or_commit=nil) + build_or_commit ||= commit_status + case build_or_commit + when :success + 'green' + when :failed, :canceled + 'red' + else # :pending, :running or unknown + 'yellow' + end + end + + def notify? + [:failed, :canceled].include?(commit_status) + end + + + private + + def commit + build.commit + end + + def project + commit.project + end + + def build_status + build.status.to_sym + end + + def commit_status + commit.status.to_sym + end + + def humanized_status(build_or_commit=nil) + build_or_commit ||= commit_status + case build_or_commit + when :pending + "Pending" + when :running + "Running" + when :failed + "Failed" + when :success + "Successful" + when :canceled + "Canceled" + else + "Unknown" + end + end + end +end diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb new file mode 100644 index 00000000000..68acf71251e --- /dev/null +++ b/app/models/project_services/ci/hip_chat_service.rb @@ -0,0 +1,93 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +module Ci + class HipChatService < Service + prop_accessor :hipchat_token, :hipchat_room, :hipchat_server + boolean_accessor :notify_only_broken_builds + validates :hipchat_token, presence: true, if: :activated? + validates :hipchat_room, presence: true, if: :activated? + default_value_for :notify_only_broken_builds, true + + def title + "HipChat" + end + + def description + "Private group chat, video chat, instant messaging for teams" + end + + def help + end + + def to_param + 'hip_chat' + end + + def fields + [ + { type: 'text', name: 'hipchat_token', label: 'Token', placeholder: '' }, + { type: 'text', name: 'hipchat_room', label: 'Room', placeholder: '' }, + { type: 'text', name: 'hipchat_server', label: 'Server', placeholder: 'https://hipchat.example.com', help: 'Leave blank for default' }, + { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } + ] + end + + def can_execute?(build) + return if build.allow_failure? + + commit = build.commit + return unless commit + return unless commit.builds_without_retry.include? build + + case commit.status.to_sym + when :failed + true + when :success + true unless notify_only_broken_builds? + else + false + end + end + + def execute(build) + msg = Ci::HipChatMessage.new(build) + opts = default_options.merge( + token: hipchat_token, + room: hipchat_room, + server: server_url, + color: msg.status_color, + notify: msg.notify? + ) + Ci::HipChatNotifierWorker.perform_async(msg.to_s, opts) + end + + private + + def default_options + { + service_name: 'GitLab CI', + message_format: 'html' + } + end + + def server_url + if hipchat_server.blank? + 'https://api.hipchat.com' + else + hipchat_server + end + end + end +end diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb new file mode 100644 index 00000000000..3619a50fa96 --- /dev/null +++ b/app/models/project_services/ci/mail_service.rb @@ -0,0 +1,84 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +module Ci + class MailService < Service + delegate :email_recipients, :email_recipients=, + :email_add_pusher, :email_add_pusher=, + :email_only_broken_builds, :email_only_broken_builds=, to: :project, prefix: false + + before_save :update_project + + default_value_for :active, true + + def title + 'Mail' + end + + def description + 'Email notification' + end + + def to_param + 'mail' + end + + def fields + [ + { type: 'text', name: 'email_recipients', label: 'Recipients', help: 'Whitespace-separated list of recipient addresses' }, + { type: 'checkbox', name: 'email_add_pusher', label: 'Add pusher to recipients list' }, + { type: 'checkbox', name: 'email_only_broken_builds', label: 'Notify only broken builds' } + ] + end + + def can_execute?(build) + return if build.allow_failure? + + # it doesn't make sense to send emails for retried builds + commit = build.commit + return unless commit + return unless commit.builds_without_retry.include?(build) + + case build.status.to_sym + when :failed + true + when :success + true unless email_only_broken_builds + else + false + end + end + + def execute(build) + build.commit.project_recipients.each do |recipient| + case build.status.to_sym + when :success + mailer.build_success_email(build.id, recipient) + when :failed + mailer.build_fail_email(build.id, recipient) + end + end + end + + private + + def update_project + project.save! + end + + def mailer + Ci::Notify.delay + end + end +end diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb new file mode 100644 index 00000000000..7d884849bf3 --- /dev/null +++ b/app/models/project_services/ci/slack_message.rb @@ -0,0 +1,97 @@ +require 'slack-notifier' + +module Ci + class SlackMessage + def initialize(commit) + @commit = commit + end + + def pretext + '' + end + + def color + attachment_color + end + + def fallback + format(attachment_message) + end + + def attachments + fields = [] + + if commit.matrix? + commit.builds_without_retry.each do |build| + next if build.allow_failure? + next unless build.failed? + fields << { + title: build.name, + value: "Build <#{Ci::RoutesHelper.ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)." + } + end + end + + [{ + text: attachment_message, + color: attachment_color, + fields: fields + }] + end + + private + + attr_reader :commit + + def attachment_message + out = "<#{Ci::RoutesHelper.ci_project_url(project)}|#{project_name}>: " + if commit.matrix? + out << "Commit <#{Ci::RoutesHelper.ci_project_ref_commit_url(project, commit.ref, commit.sha)}|\##{commit.id}> " + else + build = commit.builds_without_retry.first + out << "Build <#{Ci::RoutesHelper.ci_project_build_url(project, build)}|\##{build.id}> " + end + out << "(<#{commit_sha_link}|#{commit.short_sha}>) " + out << "of <#{commit_ref_link}|#{commit.ref}> " + out << "by #{commit.git_author_name} " if commit.git_author_name + out << "#{commit_status} in " + out << "#{commit.duration} second(s)" + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def project + commit.project + end + + def project_name + project.name + end + + def commit_sha_link + "#{project.gitlab_url}/commit/#{commit.sha}" + end + + def commit_ref_link + "#{project.gitlab_url}/commits/#{commit.ref}" + end + + def attachment_color + if commit.success? + 'good' + else + 'danger' + end + end + + def commit_status + if commit.success? + 'succeeded' + else + 'failed' + end + end + end +end diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb new file mode 100644 index 00000000000..c9a7f865a25 --- /dev/null +++ b/app/models/project_services/ci/slack_service.rb @@ -0,0 +1,81 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +module Ci + class SlackService < Service + prop_accessor :webhook + boolean_accessor :notify_only_broken_builds + validates :webhook, presence: true, if: :activated? + + default_value_for :notify_only_broken_builds, true + + def title + 'Slack' + end + + def description + 'A team communication tool for the 21st century' + end + + def to_param + 'slack' + end + + def help + 'Visit https://www.slack.com/services/new/incoming-webhook. Then copy link and save project!' unless webhook.present? + end + + def fields + [ + { type: 'text', name: 'webhook', label: 'Webhook URL', placeholder: '' }, + { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } + ] + end + + def can_execute?(build) + return if build.allow_failure? + + commit = build.commit + return unless commit + return unless commit.builds_without_retry.include?(build) + + case commit.status.to_sym + when :failed + true + when :success + true unless notify_only_broken_builds? + else + false + end + end + + def execute(build) + message = Ci::SlackMessage.new(build.commit) + options = default_options.merge( + color: message.color, + fallback: message.fallback, + attachments: message.attachments + ) + Ci::SlackNotifierWorker.perform_async(webhook, message.pretext, options) + end + + private + + def default_options + { + username: 'GitLab CI' + } + end + end +end diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 0ebc0a3ba1a..9558292fea3 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -19,7 +19,7 @@ # class GitlabIssueTrackerService < IssueTrackerService - include Rails.application.routes.url_helpers + include Gitlab::Application.routes.url_helpers prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index bfa8fc7b860..35e30b1cb0b 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -19,7 +19,7 @@ # class JiraService < IssueTrackerService - include Rails.application.routes.url_helpers + include Gitlab::Application.routes.url_helpers prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb new file mode 100644 index 00000000000..0a1abf89a95 --- /dev/null +++ b/app/services/ci/create_commit_service.rb @@ -0,0 +1,50 @@ +module Ci + class CreateCommitService + def execute(project, params) + before_sha = params[:before] + sha = params[:checkout_sha] || params[:after] + origin_ref = params[:ref] + + unless origin_ref && sha.present? + return false + end + + ref = origin_ref.gsub(/\Arefs\/(tags|heads)\//, '') + + # Skip branch removal + if sha == Ci::Git::BLANK_SHA + return false + end + + commit = project.commits.find_by_sha_and_ref(sha, ref) + + # Create commit if not exists yet + unless commit + data = { + ref: ref, + sha: sha, + tag: origin_ref.start_with?('refs/tags/'), + before_sha: before_sha, + push_data: { + before: before_sha, + after: sha, + ref: ref, + user_name: params[:user_name], + user_email: params[:user_email], + repository: params[:repository], + commits: params[:commits], + total_commits_count: params[:total_commits_count], + ci_yaml_file: params[:ci_yaml_file] + } + } + + commit = project.commits.create(data) + end + + commit.update_committed! + commit.create_builds unless commit.builds.any? + + commit + end + end +end diff --git a/app/services/ci/create_project_service.rb b/app/services/ci/create_project_service.rb new file mode 100644 index 00000000000..049ac2e9181 --- /dev/null +++ b/app/services/ci/create_project_service.rb @@ -0,0 +1,35 @@ +module Ci + class CreateProjectService + include Gitlab::Application.routes.url_helpers + + def execute(current_user, params, project_route, forked_project = nil) + @project = Ci::Project.parse(params) + + Ci::Project.transaction do + @project.save! + + data = { + token: @project.token, + project_url: project_route.gsub(":project_id", @project.id.to_s), + } + + unless Ci::Network.new.enable_ci(@project.gitlab_id, data, current_user.authenticate_options) + raise ActiveRecord::Rollback + end + end + + if forked_project + # Copy settings + settings = forked_project.attributes.select do |attr_name, value| + ["public", "shared_runners_enabled", "allow_git_fetch"].include? attr_name + end + + @project.update(settings) + end + + Ci::EventService.new.create_project(current_user, @project) + + @project + end + end +end diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb new file mode 100644 index 00000000000..9bad09f2f54 --- /dev/null +++ b/app/services/ci/create_trigger_request_service.rb @@ -0,0 +1,17 @@ +module Ci + class CreateTriggerRequestService + def execute(project, trigger, ref, variables = nil) + commit = project.commits.where(ref: ref).last + return unless commit + + trigger_request = trigger.trigger_requests.create!( + commit: commit, + variables: variables + ) + + if commit.create_builds(trigger_request) + trigger_request + end + end + end +end diff --git a/app/services/ci/event_service.rb b/app/services/ci/event_service.rb new file mode 100644 index 00000000000..3f4e02dd26c --- /dev/null +++ b/app/services/ci/event_service.rb @@ -0,0 +1,31 @@ +module Ci + class EventService + def remove_project(user, project) + create( + description: "Project \"#{project.name}\" has been removed by #{user.username}", + user_id: user.id, + is_admin: true + ) + end + + def create_project(user, project) + create( + description: "Project \"#{project.name}\" has been created by #{user.username}", + user_id: user.id, + is_admin: true + ) + end + + def change_project_settings(user, project) + create( + project_id: project.id, + user_id: user.id, + description: "User \"#{user.username}\" updated projects settings" + ) + end + + def create(*args) + Ci::Event.create!(*args) + end + end +end diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb new file mode 100644 index 00000000000..b95835ba093 --- /dev/null +++ b/app/services/ci/image_for_build_service.rb @@ -0,0 +1,31 @@ +module Ci + class ImageForBuildService + def execute(project, params) + image_name = + if params[:sha] + commit = project.commits.find_by(sha: params[:sha]) + image_for_commit(commit) + elsif params[:ref] + commit = project.last_commit_for_ref(params[:ref]) + image_for_commit(commit) + else + 'build-unknown.svg' + end + + image_path = Rails.root.join('public/ci', image_name) + + OpenStruct.new( + path: image_path, + name: image_name + ) + end + + private + + def image_for_commit(commit) + return 'build-unknown.svg' unless commit + + 'build-' + commit.status + ".svg" + end + end +end diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb new file mode 100644 index 00000000000..7e0b58a5dc9 --- /dev/null +++ b/app/services/ci/register_build_service.rb @@ -0,0 +1,40 @@ +module Ci + # This class responsible for assigning + # proper pending build to runner on runner API request + class RegisterBuildService + def execute(current_runner) + builds = Ci::Build.pending.unstarted + + builds = + if current_runner.shared? + # don't run projects which have not enables shared runners + builds.includes(:project).where(projects: { shared_runners_enabled: true }) + else + # do run projects which are only assigned to this runner + builds.where(project_id: current_runner.projects) + end + + builds = builds.order('created_at ASC') + + build = builds.find do |build| + (build.tag_list - current_runner.tag_list).empty? + end + + + if build + # In case when 2 runners try to assign the same build, second runner will be declined + # with StateMachine::InvalidTransition in run! method. + build.with_lock do + build.runner_id = current_runner.id + build.save! + build.run! + end + end + + build + + rescue StateMachine::InvalidTransition + nil + end + end +end diff --git a/app/services/ci/test_hook_service.rb b/app/services/ci/test_hook_service.rb new file mode 100644 index 00000000000..3a17596aaeb --- /dev/null +++ b/app/services/ci/test_hook_service.rb @@ -0,0 +1,7 @@ +module Ci + class TestHookService + def execute(hook, current_user) + Ci::WebHookService.new.build_end(hook.project.commits.last.last_build) + end + end +end diff --git a/app/services/ci/web_hook_service.rb b/app/services/ci/web_hook_service.rb new file mode 100644 index 00000000000..87984b20fa1 --- /dev/null +++ b/app/services/ci/web_hook_service.rb @@ -0,0 +1,36 @@ +module Ci + class WebHookService + def build_end(build) + execute_hooks(build.project, build_data(build)) + end + + def execute_hooks(project, data) + project.web_hooks.each do |web_hook| + async_execute_hook(web_hook, data) + end + end + + def async_execute_hook(hook, data) + Sidekiq::Client.enqueue(Ci::WebHookWorker, hook.id, data) + end + + def build_data(build) + project = build.project + data = {} + data.merge!({ + build_id: build.id, + build_name: build.name, + build_status: build.status, + build_started_at: build.started_at, + build_finished_at: build.finished_at, + project_id: project.id, + project_name: project.name, + gitlab_url: project.gitlab_url, + ref: build.ref, + sha: build.sha, + before_sha: build.before_sha, + push_data: build.commit.push_data + }) + end + end +end diff --git a/app/views/ci/admin/application_settings/_form.html.haml b/app/views/ci/admin/application_settings/_form.html.haml new file mode 100644 index 00000000000..634c9daa477 --- /dev/null +++ b/app/views/ci/admin/application_settings/_form.html.haml @@ -0,0 +1,24 @@ += form_for @application_setting, url: ci_admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| + - if @application_setting.errors.any? + #error_explanation + .alert.alert-danger + - @application_setting.errors.full_messages.each do |msg| + %p= msg + + %fieldset + %legend Default Project Settings + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :all_broken_builds do + = f.check_box :all_broken_builds + Send emails only on broken builds + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :add_pusher do + = f.check_box :add_pusher + Add pusher to recipients list + + .form-actions + = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/ci/admin/application_settings/show.html.haml b/app/views/ci/admin/application_settings/show.html.haml new file mode 100644 index 00000000000..7ef0aa89ed6 --- /dev/null +++ b/app/views/ci/admin/application_settings/show.html.haml @@ -0,0 +1,3 @@ +%h3.page-title Settings +%hr += render 'form' diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml new file mode 100644 index 00000000000..1766ca39760 --- /dev/null +++ b/app/views/ci/admin/builds/_build.html.haml @@ -0,0 +1,32 @@ +- if build.commit && build.project + %tr.build.alert{class: build_status_alert_class(build)} + %td.build-link + = link_to ci_build_url(build) do + %strong #{build.id} + + %td.status + = build.status + + %td.commit-link + = commit_link(build.commit) + + %td.runner + - if build.runner + = link_to build.runner.id, ci_admin_runner_path(build.runner) + + %td.build-project + = truncate build.project.name, length: 30 + + %td.build-message + %span= truncate(build.commit.git_commit_message, length: 30) + + %td.build-branch + %span= truncate(build.ref, length: 25) + + %td.duration + - if build.duration + #{duration_in_words(build.finished_at, build.started_at)} + + %td.timestamp + - if build.finished_at + %span #{time_ago_in_words build.finished_at} ago diff --git a/app/views/ci/admin/builds/index.html.haml b/app/views/ci/admin/builds/index.html.haml new file mode 100644 index 00000000000..ab4ced54327 --- /dev/null +++ b/app/views/ci/admin/builds/index.html.haml @@ -0,0 +1,27 @@ +%ul.nav.nav-tabs.append-bottom-20 + %li{class: ("active" if @scope.nil?)} + = link_to 'All builds', ci_admin_builds_path + + %li{class: ("active" if @scope == "pending")} + = link_to "Pending", ci_admin_builds_path(scope: :pending) + + %li{class: ("active" if @scope == "running")} + = link_to "Running", ci_admin_builds_path(scope: :running) + + +%table.builds + %thead + %tr + %th Build + %th Status + %th Commit + %th Runner + %th Project + %th Message + %th Branch + %th Duration + %th Finished at + + = render @builds + += paginate @builds diff --git a/app/views/ci/admin/events/index.html.haml b/app/views/ci/admin/events/index.html.haml new file mode 100644 index 00000000000..f9ab0994304 --- /dev/null +++ b/app/views/ci/admin/events/index.html.haml @@ -0,0 +1,17 @@ +%table.table + %thead + %tr + %th User ID + %th Description + %th When + - @events.each do |event| + %tr + %td + = event.user_id + %td + = event.description + %td.light + = time_ago_in_words event.updated_at + ago + += paginate @events \ No newline at end of file diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml new file mode 100644 index 00000000000..e64bfe853d7 --- /dev/null +++ b/app/views/ci/admin/projects/_project.html.haml @@ -0,0 +1,28 @@ +- last_commit = project.last_commit +%tr.alert{class: commit_status_alert_class(last_commit) } + %td + = project.id + %td + = link_to project do + %strong= project.name + %td + - if last_commit + #{last_commit.status} (#{commit_link(last_commit)}) + - if project.last_commit_date + = time_ago_in_words project.last_commit_date + ago + - else + No builds yet + %td + - if project.public + %i.fa-globe + Public + - else + %i.fa-lock + Private + %td + = project.commits.count + %td + = link_to [:ci, :admin, project], method: :delete, class: 'btn btn-danger btn-sm' do + %i.fa-remove + Remove diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml new file mode 100644 index 00000000000..73956575a89 --- /dev/null +++ b/app/views/ci/admin/projects/index.html.haml @@ -0,0 +1,14 @@ +%table.table + %thead + %tr + %th ID + %th Name + %th Last build + %th Access + %th Builds + %th + + = render @projects + += paginate @projects + diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml new file mode 100644 index 00000000000..f049b4f4c4e --- /dev/null +++ b/app/views/ci/admin/runner_projects/index.html.haml @@ -0,0 +1,57 @@ +%p.lead + To register new runner visit #{link_to 'this page ', ci_runners_path} + +.row + .col-md-8 + %h5 Activated: + %table.table + %tr + %th Runner ID + %th Runner Description + %th Last build + %th Builds Stats + %th Registered + %th + + - @runner_projects.each do |runner_project| + - runner = runner_project.runner + - builds = runner.builds.where(project_id: @project.id) + %tr + %td + %span.badge.badge-info= runner.id + %td + = runner.display_name + %td + - last_build = builds.last + - if last_build + = link_to last_build.short_sha, [last_build.project, last_build] + - else + unknown + %td + %span.badge.badge-success + #{builds.success.count} + %span / + %span.badge.badge-important + #{builds.failed.count} + %td + #{time_ago_in_words(runner_project.created_at)} ago + %td + = link_to 'Disable', [:ci, @project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm right' + .col-md-4 + %h5 Available + %table.table + %tr + %th ID + %th Token + %th + + - (Ci::Runner.all - @project.runners).each do |runner| + %tr + %td + = runner.id + %td + = runner.token + %td + = form_for [:ci, @project, @runner_project] do |f| + = f.hidden_field :runner_id, value: runner.id + = f.submit 'Add', class: 'btn btn-sm' diff --git a/app/views/ci/admin/runners/_runner.html.haml b/app/views/ci/admin/runners/_runner.html.haml new file mode 100644 index 00000000000..701782d26bb --- /dev/null +++ b/app/views/ci/admin/runners/_runner.html.haml @@ -0,0 +1,48 @@ +%tr{id: dom_id(runner)} + %td + - if runner.shared? + %span.label.label-success shared + - else + %span.label.label-info specific + - unless runner.active? + %span.label.label-danger paused + + %td + = link_to ci_admin_runner_path(runner) do + = runner.short_sha + %td + .runner-description + = runner.description + %span (#{link_to 'edit', '#', class: 'edit-runner-link'}) + .runner-description-form.hide + = form_for [:ci, :admin, runner], remote: true, html: { class: 'form-inline' } do |f| + .form-group + = f.text_field :description, class: 'form-control' + = f.submit 'Save', class: 'btn' + %span (#{link_to 'cancel', '#', class: 'cancel'}) + %td + - if runner.shared? + \- + - else + = runner.projects.count(:all) + %td + #{runner.builds.count(:all)} + %td + - runner.tag_list.each do |tag| + %span.label.label-primary + = tag + %td + - if runner.contacted_at + #{time_ago_in_words(runner.contacted_at)} ago + - else + Never + %td + .pull-right + = link_to 'Edit', ci_admin_runner_path(runner), class: 'btn btn-sm' +   + - if runner.active? + = link_to 'Pause', [:pause, :ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' + - else + = link_to 'Resume', [:resume, :ci, :admin, runner], method: :get, class: 'btn btn-success btn-sm' + = link_to 'Remove', [:ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml new file mode 100644 index 00000000000..f1ab3399dcc --- /dev/null +++ b/app/views/ci/admin/runners/index.html.haml @@ -0,0 +1,51 @@ +%p.lead + %span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. + %code #{GitlabCi::REGISTRATION_TOKEN} + +.bs-callout + %p + A 'runner' is a process which runs a build. + You can setup as many runners as you need. + %br + Runners can be placed on separate users, servers, and even on your local machine. + %br + + %div + %span Each runner can be in one of the following states: + %ul + %li + %span.label.label-success shared + \- run builds from all unassigned projects + %li + %span.label.label-info specific + \- run builds from assigned projects + %li + %span.label.label-danger paused + \- runner will not receive any new build + +.append-bottom-20.clearfix + .pull-left + = form_tag ci_admin_runners_path, class: 'form-inline', method: :get do + .form-group + = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token' + = submit_tag 'Search', class: 'btn' + + .pull-right.light + Runners with last contact less than a minute ago: #{@active_runners_cnt} + +%br + +%table.table + %thead + %tr + %th Type + %th Runner token + %th Description + %th Projects + %th Builds + %th Tags + %th Last contact + %th + + = render @runners += paginate @runners diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml new file mode 100644 index 00000000000..0270da53349 --- /dev/null +++ b/app/views/ci/admin/runners/show.html.haml @@ -0,0 +1,118 @@ += content_for :title do + %h3.project-title + Runner ##{@runner.id} + .pull-right + - if @runner.shared? + %span.runner-state.runner-state-shared + Shared + - else + %span.runner-state.runner-state-specific + Specific + + + +- if @runner.shared? + .bs-callout.bs-callout-success + %h4 This runner will process build from ALL UNASSIGNED projects + %p + If you want runners to build only specific projects, enable them in the table below. + Keep in mind that this is a one way transition. +- else + .bs-callout.bs-callout-info + %h4 This runner will process build only from ASSIGNED projects + %p You can't make this a shared runner. +%hr += form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| + .form-group + = label_tag :token, class: 'control-label' do + Token + .col-sm-10 + = f.text_field :token, class: 'form-control', readonly: true + .form-group + = label_tag :description, class: 'control-label' do + Description + .col-sm-10 + = f.text_field :description, class: 'form-control' + .form-group + = label_tag :tag_list, class: 'control-label' do + Tags + .col-sm-10 + = f.text_field :tag_list, class: 'form-control' + .help-block You can setup builds to only use runners with specific tags + .form-actions + = f.submit 'Save', class: 'btn btn-save' + +.row + .col-md-6 + %h4 Restrict projects for this runner + - if @runner.projects.any? + %table.table + %thead + %tr + %th Assigned projects + %th + - @runner.runner_projects.each do |runner_project| + - project = runner_project.project + %tr.alert-info + %td + %strong + = project.name + %td + .pull-right + = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + + %table.table + %thead + %tr + %th Project + %th + .pull-right + = link_to 'Assign to all', assign_all_ci_admin_runner_path(@runner), + class: 'btn btn-sm assign-all-runner', + title: 'Assign runner to all projects', + method: :put + + %tr + %td + = form_tag ci_admin_runner_path(@runner), class: 'form-inline', method: :get do + .form-group + = search_field_tag :search, params[:search], class: 'form-control' + = submit_tag 'Search', class: 'btn' + + %td + - @projects.each do |project| + %tr + %td + = project.name + %td + .pull-right + = form_for [:ci, :admin, project, project.runner_projects.new] do |f| + = f.hidden_field :runner_id, value: @runner.id + = f.submit 'Enable', class: 'btn btn-xs' + = paginate @projects + + .col-md-6 + %h4 Recent builds served by this runner + %table.builds.runner-builds + %thead + %tr + %th Status + %th Project + %th Commit + %th Finished at + + - @builds.each do |build| + %tr.build.alert{class: build_status_alert_class(build)} + %td.status + = build.status + + %td.status + = build.project.name + + %td.build-link + = link_to ci_project_build_path(build.project, build) do + %strong #{build.short_sha} + + %td.timestamp + - if build.finished_at + %span #{time_ago_in_words build.finished_at} ago diff --git a/app/views/ci/admin/runners/update.js.haml b/app/views/ci/admin/runners/update.js.haml new file mode 100644 index 00000000000..2b7d3067e20 --- /dev/null +++ b/app/views/ci/admin/runners/update.js.haml @@ -0,0 +1,2 @@ +:plain + $("#runner_#{@runner.id}").replaceWith("#{escape_javascript(render(@runner))}") diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml new file mode 100644 index 00000000000..ff9fdbbcb4e --- /dev/null +++ b/app/views/ci/builds/_build.html.haml @@ -0,0 +1,45 @@ +%tr.build.alert{class: build_status_alert_class(build)} + %td.status + = build.status + + %td.build-link + = link_to ci_project_build_path(build.project, build) do + %strong Build ##{build.id} + + %td + = build.stage + + %td + = build.name + .pull-right + - if build.tags.any? + - build.tag_list.each do |tag| + %span.label.label-primary + = tag + - if build.trigger_request + %span.label.label-info triggered + - if build.allow_failure + %span.label.label-danger allowed to fail + + %td.duration + - if build.duration + #{duration_in_words(build.finished_at, build.started_at)} + + %td.timestamp + - if build.finished_at + %span #{time_ago_in_words build.finished_at} ago + + - if build.project.coverage_enabled? + %td.coverage + - if build.coverage + #{build.coverage}% + + %td + - if defined?(controls) && current_user && current_user.has_developer_access?(@project.gitlab_id) + .pull-right + - if build.active? + = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do + %i.fa-remove.cred + - elsif build.commands.present? + = link_to retry_ci_project_build_path(build.project, build, return_to: request.original_url), method: :post, title: 'Retry build' do + %i.fa-repeat diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml new file mode 100644 index 00000000000..fed30847e73 --- /dev/null +++ b/app/views/ci/builds/show.html.haml @@ -0,0 +1,176 @@ +%h4.page-title + = link_to @project.name, @project + @ + = @commit.short_sha + +%p + = link_to ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) do + ← Back to project commit +%hr +#up-build-trace +- if @commit.matrix? + %ul.nav.nav-tabs.append-bottom-10 + - @commit.builds_without_retry_sorted.each do |build| + %li{class: ('active' if build == @build) } + = link_to ci_build_url(build) do + %i{class: build_icon_css_class(build)} + %span + Build ##{build.id} + - if build.name + · + = build.name + + - unless @commit.builds_without_retry.include?(@build) + %li.active + %a + Build ##{@build.id} + · + %i.fa-warning-sign + This build was retried. + +.row + .col-md-9 + .build-head.alert{class: build_status_alert_class(@build)} + %h4 + - if @build.commit.tag? + Build for tag + %code #{@build.ref} + - else + Build for commit + %code #{@build.short_sha} + from + + = link_to ci_project_path(@build.project, ref: @build.ref) do + %span.label.label-primary= "#{@build.ref}" + + - if @build.duration + .pull-right + %span + %i.fa-time + #{duration_in_words(@build.finished_at, @build.started_at)} + + .clearfix + = @build.status + .pull-right + = @build.updated_at.stamp('19:00 Aug 27') + + + + .clearfix + - if @build.active? + .autoscroll-container + %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll + .clearfix + .scroll-controls + = link_to '#up-build-trace', class: 'btn' do + %i.fa-angle-up + = link_to '#down-build-trace', class: 'btn' do + %i.fa-angle-down + + %pre.trace#build-trace + %code.bash + = preserve do + = raw @build.trace_html + %div#down-build-trace + + .col-md-3 + - if @build.coverage + .build-widget + %h4.title + Test coverage + %h1 #{@build.coverage}% + + + .build-widget + %h4.title + Build + - if current_user && current_user.has_developer_access?(@project.gitlab_id) + .pull-right + - if @build.active? + = link_to "Cancel", cancel_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-danger' + - elsif @build.commands.present? + = link_to "Retry", retry_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-primary', method: :post + + - if @build.duration + %p + %span.attr-name Duration: + #{duration_in_words(@build.finished_at, @build.started_at)} + %p + %span.attr-name Created: + #{time_ago_in_words(@build.created_at)} ago + - if @build.finished_at + %p + %span.attr-name Finished: + #{time_ago_in_words(@build.finished_at)} ago + %p + %span.attr-name Runner: + - if @build.runner && current_user && current_user.is_admin + \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)} + - elsif @build.runner + \##{@build.runner.id} + + - if @build.trigger_request + .build-widget + %h4.title + Trigger + + %p + %span.attr-name Token: + #{@build.trigger_request.trigger.short_token} + + - if @build.trigger_request.variables + %p + %span.attr-name Variables: + + %code + - @build.trigger_request.variables.each do |key, value| + #{key}=#{value} + + .build-widget + %h4.title + Commit + .pull-right + %small #{build_commit_link @build} + + - if @build.commit.compare? + %p + %span.attr-name Compare: + #{build_compare_link @build} + %p + %span.attr-name Branch: + #{build_ref_link @build} + %p + %span.attr-name Author: + #{@build.commit.git_author_name} + %p + %span.attr-name Message: + #{@build.commit.git_commit_message} + + - if @build.tags.any? + .build-widget + %h4.title + Tags + - @build.tag_list.each do |tag| + %span.label.label-primary + = tag + + - if @builds.present? + .build-widget + %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}: + %table.builds + - @builds.each_with_index do |build, i| + %tr.build.alert{class: build_status_alert_class(build)} + %td + = link_to ci_build_url(build) do + %span ##{build.id} + %td + - if build.name + = build.name + %td.status= build.status + + + = paginate @builds + + +:javascript + new CiBuild("#{ci_build_url(@build)}", "#{@build.status}") diff --git a/app/views/ci/charts/_build_times.haml b/app/views/ci/charts/_build_times.haml new file mode 100644 index 00000000000..c3c2f572414 --- /dev/null +++ b/app/views/ci/charts/_build_times.haml @@ -0,0 +1,21 @@ +%fieldset + %legend + Commit duration in minutes for last 30 commits + + %canvas#build_timesChart.padded{width: 800, height: 300} + +:javascript + var data = { + labels : #{@charts[:build_times].labels.to_json}, + datasets : [ + { + fillColor : "#4A3", + strokeColor : "rgba(151,187,205,1)", + pointColor : "rgba(151,187,205,1)", + pointStrokeColor : "#fff", + data : #{@charts[:build_times].build_times.to_json} + } + ] + } + var ctx = $("#build_timesChart").get(0).getContext("2d"); + new Chart(ctx).Line(data,{"scaleOverlay": true}); diff --git a/app/views/ci/charts/_builds.haml b/app/views/ci/charts/_builds.haml new file mode 100644 index 00000000000..1b0039fb834 --- /dev/null +++ b/app/views/ci/charts/_builds.haml @@ -0,0 +1,41 @@ +%fieldset + %legend + Builds chart for last week + (#{date_from_to(Date.today - 7.days, Date.today)}) + + %canvas#weekChart.padded{width: 800, height: 200} + +%fieldset + %legend + Builds chart for last month + (#{date_from_to(Date.today - 30.days, Date.today)}) + + %canvas#monthChart.padded{width: 800, height: 300} + +%fieldset + %legend Builds chart for last year + %canvas#yearChart.padded{width: 800, height: 400} + +- [:week, :month, :year].each do |scope| + :javascript + var data = { + labels : #{@charts[scope].labels.to_json}, + datasets : [ + { + fillColor : "rgba(220,220,220,0.5)", + strokeColor : "rgba(220,220,220,1)", + pointColor : "rgba(220,220,220,1)", + pointStrokeColor : "#EEE", + data : #{@charts[scope].total.to_json} + }, + { + fillColor : "#4A3", + strokeColor : "rgba(151,187,205,1)", + pointColor : "rgba(151,187,205,1)", + pointStrokeColor : "#fff", + data : #{@charts[scope].success.to_json} + } + ] + } + var ctx = $("##{scope}Chart").get(0).getContext("2d"); + new Chart(ctx).Line(data,{"scaleOverlay": true}); diff --git a/app/views/ci/charts/_overall.haml b/app/views/ci/charts/_overall.haml new file mode 100644 index 00000000000..f522f35a629 --- /dev/null +++ b/app/views/ci/charts/_overall.haml @@ -0,0 +1,21 @@ +%fieldset + %legend Overall + %p + Total: + %strong= pluralize @project.builds.count(:all), 'build' + %p + Successful: + %strong= pluralize @project.builds.success.count(:all), 'build' + %p + Failed: + %strong= pluralize @project.builds.failed.count(:all), 'build' + + %p + Success ratio: + %strong + #{success_ratio(@project.builds.success, @project.builds.failed)}% + + %p + Commits covered: + %strong + = @project.commits.count(:all) diff --git a/app/views/ci/charts/show.html.haml b/app/views/ci/charts/show.html.haml new file mode 100644 index 00000000000..b5fcfc1563c --- /dev/null +++ b/app/views/ci/charts/show.html.haml @@ -0,0 +1,4 @@ +#charts + = render 'builds' + = render 'build_times' += render 'overall' diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml new file mode 100644 index 00000000000..a955a5b6479 --- /dev/null +++ b/app/views/ci/commits/_commit.html.haml @@ -0,0 +1,32 @@ +%tr.build.alert{class: commit_status_alert_class(commit)} + %td.status + = commit.status + - if commit.running? + · + = commit.stage + + + %td.build-link + = link_to ci_project_ref_commit_path(commit.project, commit.ref, commit.sha) do + %strong #{commit.short_sha} + + %td.build-message + %span= truncate_first_line(commit.git_commit_message) + + %td.build-branch + - unless @ref + %span + = link_to truncate(commit.ref, length: 25), ci_project_path(@project, ref: commit.ref) + + %td.duration + - if commit.duration > 0 + #{time_interval_in_words commit.duration} + + %td.timestamp + - if commit.finished_at + %span #{time_ago_in_words commit.finished_at} ago + + - if commit.project.coverage_enabled? + %td.coverage + - if commit.coverage + #{commit.coverage}% diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml new file mode 100644 index 00000000000..4cf567c77e6 --- /dev/null +++ b/app/views/ci/commits/show.html.haml @@ -0,0 +1,96 @@ +%h4.page-title + = @project.name + @ + #{gitlab_commit_link(@project, @commit.sha)} +%p + = link_to ci_project_path(@project) do + ← Back to project commits +%hr +.commit-info + %pre.commit-message + #{@commit.git_commit_message} + + .row + .col-sm-6 + - if @commit.compare? + %p + %span.attr-name Compare: + #{gitlab_compare_link(@project, @commit.short_before_sha, @commit.short_sha)} + - else + %p + %span.attr-name Commit: + #{gitlab_commit_link(@project, @commit.sha)} + + %p + %span.attr-name Branch: + #{gitlab_ref_link(@project, @commit.ref)} + .col-sm-6 + %p + %span.attr-name Author: + #{@commit.git_author_name} (#{@commit.git_author_email}) + - if @commit.created_at + %p + %span.attr-name Created at: + #{@commit.created_at.to_s(:short)} + +- if current_user && current_user.has_developer_access?(@project.gitlab_id) + .pull-right + - if @commit.builds.running_or_pending.any? + = link_to "Cancel", cancel_ci_project_ref_commit_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger' + + +- if @commit.yaml_errors.present? + .bs-callout.bs-callout-danger + %h4 Found errors in your .gitlab-ci.yml: + %ul + - @commit.yaml_errors.split(",").each do |error| + %li= error + +- unless @commit.push_data[:ci_yaml_file] + .bs-callout.bs-callout-warning + \.gitlab-ci.yml not found in this commit + +%h3 Status + +.build.alert{class: commit_status_alert_class(@commit)} + .status + = @commit.status.titleize + +%h3 + Builds + - if @commit.duration > 0 + %small.pull-right + %i.fa-time + #{time_interval_in_words @commit.duration} + +%table.builds + %thead + %tr + %th Status + %th Build ID + %th Stage + %th Name + %th Duration + %th Finished at + - if @project.coverage_enabled? + %th Coverage + %th + = render @commit.builds_without_retry_sorted, controls: true + +- if @commit.retried_builds.any? + %h3 + Retried builds + + %table.builds + %thead + %tr + %th Status + %th Build ID + %th Stage + %th Name + %th Duration + %th Finished at + - if @project.coverage_enabled? + %th Coverage + %th + = render @commit.retried_builds diff --git a/app/views/ci/errors/show.haml b/app/views/ci/errors/show.haml new file mode 100644 index 00000000000..2788112c835 --- /dev/null +++ b/app/views/ci/errors/show.haml @@ -0,0 +1,2 @@ +%h3.error Error += @error diff --git a/app/views/ci/events/index.html.haml b/app/views/ci/events/index.html.haml new file mode 100644 index 00000000000..779f49b3d3a --- /dev/null +++ b/app/views/ci/events/index.html.haml @@ -0,0 +1,19 @@ +%h3.page-title Events + +%table.table + %thead + %tr + %th User ID + %th Description + %th When + - @events.each do |event| + %tr + %td + = event.user_id + %td + = event.description + %td.light + = time_ago_in_words event.updated_at + ago + += paginate @events \ No newline at end of file diff --git a/app/views/ci/helps/oauth2.html.haml b/app/views/ci/helps/oauth2.html.haml new file mode 100644 index 00000000000..2031b7340d4 --- /dev/null +++ b/app/views/ci/helps/oauth2.html.haml @@ -0,0 +1,20 @@ +.welcome-block + %h1 + Welcome to GitLab CI + %p + GitLab CI integrates with your GitLab installation and runs tests for your projects. + + %h3 You need only 2 steps to set it up + + %ol + %li + In the GitLab admin area under OAuth applications create a new entry. The redirect url should be + %code= callback_ci_user_sessions_url + %li + Update the GitLab CI config with the application id and the application secret from GitLab. + %li + Restart your GitLab CI instance + %li + Refresh this page when GitLab CI has started again + + diff --git a/app/views/ci/helps/show.html.haml b/app/views/ci/helps/show.html.haml new file mode 100644 index 00000000000..5acdf9fa98a --- /dev/null +++ b/app/views/ci/helps/show.html.haml @@ -0,0 +1,40 @@ +.jumbotron + %h2 + GitLab CI + %span= GitlabCi::VERSION + %small= GitlabCi::REVISION + %p + GitLab CI integrates with your GitLab installation and run tests for your projects. + %br + Login with your GitLab account, add a project with one click and enjoy running your tests. + %br + Read more about GitLab CI at #{link_to "about.gitlab.com/gitlab-ci", "https://about.gitlab.com/gitlab-ci/", target: "_blank"}. + + +.bs-callout.bs-callout-success + %h4 + = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/api' do + %i.fa-cogs + API + %p Explore how you can access GitLab CI via the API. + +.bs-callout.bs-callout-info + %h4 + = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/examples' do + %i.fa-info-sign + Build script examples + %p This includes the build script we use to test GitLab CE. + +.bs-callout.bs-callout-danger + %h4 + = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/issues' do + %i.fa-bug + Issue tracker + %p Reports about recent bugs and problems.. + +.bs-callout.bs-callout-warning + %h4 + = link_to 'http://feedback.gitlab.com/forums/176466-general/category/64310-gitlab-ci' do + %i.fa-thumbs-up + Feedback forum + %p Suggest improvements or new features for GitLab CI. diff --git a/app/views/ci/kaminari/_first_page.html.haml b/app/views/ci/kaminari/_first_page.html.haml new file mode 100644 index 00000000000..a1bbf18690c --- /dev/null +++ b/app/views/ci/kaminari/_first_page.html.haml @@ -0,0 +1,2 @@ +%li + = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote diff --git a/app/views/ci/kaminari/_gap.html.haml b/app/views/ci/kaminari/_gap.html.haml new file mode 100644 index 00000000000..dfe33aac21d --- /dev/null +++ b/app/views/ci/kaminari/_gap.html.haml @@ -0,0 +1,2 @@ +%li.disabled + = link_to raw(t 'views.pagination.truncate'), '#' diff --git a/app/views/ci/kaminari/_last_page.html.haml b/app/views/ci/kaminari/_last_page.html.haml new file mode 100644 index 00000000000..e70697d04ad --- /dev/null +++ b/app/views/ci/kaminari/_last_page.html.haml @@ -0,0 +1,2 @@ +%li + = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} diff --git a/app/views/ci/kaminari/_next_page.html.haml b/app/views/ci/kaminari/_next_page.html.haml new file mode 100644 index 00000000000..ea9af4539e0 --- /dev/null +++ b/app/views/ci/kaminari/_next_page.html.haml @@ -0,0 +1,2 @@ +%li + = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote diff --git a/app/views/ci/kaminari/_page.html.haml b/app/views/ci/kaminari/_page.html.haml new file mode 100644 index 00000000000..9df7ce02f8f --- /dev/null +++ b/app/views/ci/kaminari/_page.html.haml @@ -0,0 +1,2 @@ +%li{class: "#{'active' if page.current?}"} + = link_to page, page.current? ? '#' : url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} diff --git a/app/views/ci/kaminari/_paginator.html.haml b/app/views/ci/kaminari/_paginator.html.haml new file mode 100644 index 00000000000..07fdb1e08a6 --- /dev/null +++ b/app/views/ci/kaminari/_paginator.html.haml @@ -0,0 +1,11 @@ += paginator.render do + %ul.pagination + = first_page_tag unless current_page.first? + = prev_page_tag unless current_page.first? + - each_page do |page| + - if page.left_outer? || page.right_outer? || page.inside_window? + = page_tag page + - elsif !page.was_truncated? + = gap_tag + = next_page_tag unless current_page.last? + = last_page_tag unless current_page.last? diff --git a/app/views/ci/kaminari/_prev_page.html.haml b/app/views/ci/kaminari/_prev_page.html.haml new file mode 100644 index 00000000000..dab3b318dac --- /dev/null +++ b/app/views/ci/kaminari/_prev_page.html.haml @@ -0,0 +1,2 @@ +%li + = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml new file mode 100644 index 00000000000..903b92de689 --- /dev/null +++ b/app/views/ci/lints/_create.html.haml @@ -0,0 +1,39 @@ +- if @status + %p + %b Status: + syntax is correct + %i.fa-ok.correct-syntax + + %table.table.table-bordered + %thead + %tr + %th Parameter + %th Value + %tbody + - @stages.each do |stage| + - @builds.select { |build| build[:stage] == stage }.each do |build| + %tr + %td #{stage.capitalize} Job - #{build[:name]} + %td + %pre + = simple_format build[:script] + + %br + %b Tag list: + = build[:tags] + %br + %b Refs only: + = build[:only] && build[:only].join(", ") + %br + %b Refs except: + = build[:except] && build[:except].join(", ") + +-else + %p + %b Status: + syntax is incorrect + %i.fa-remove.incorrect-syntax + %b Error: + = @error + + diff --git a/app/views/ci/lints/create.js.haml b/app/views/ci/lints/create.js.haml new file mode 100644 index 00000000000..a96c0b11b6e --- /dev/null +++ b/app/views/ci/lints/create.js.haml @@ -0,0 +1,2 @@ +:plain + $(".results").html("#{escape_javascript(render "create")}") \ No newline at end of file diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml new file mode 100644 index 00000000000..b0fd5dd8e58 --- /dev/null +++ b/app/views/ci/lints/show.html.haml @@ -0,0 +1,25 @@ +%h2 Check your .gitlab-ci.yml +%hr + += form_tag ci_lint_path, method: :post, remote: true do + .control-group + = label_tag :content, "Content of .gitlab-ci.yml", class: 'control-label' + .controls + = text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true + + .control-group.clearfix + .controls.pull-left.prepend-top-10 + = submit_tag "Validate", class: 'btn btn-success submit-yml' + + +%p.text-center.loading + %i.fa-refresh.fa-spin + +.results.prepend-top-20 + +:coffeescript + $(".loading").hide() + $('form').bind 'ajax:beforeSend', -> + $(".loading").show() + $('form').bind 'ajax:complete', -> + $(".loading").hide() diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml new file mode 100644 index 00000000000..d818e8b6756 --- /dev/null +++ b/app/views/ci/notify/build_fail_email.html.haml @@ -0,0 +1,19 @@ +- content_for :header do + %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + GitLab CI (build failed) +%h3 + Project: + = link_to ci_project_url(@project) do + = @project.name + +%p + Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)} +%p + Author: #{@build.commit.git_author_name} +%p + Branch: #{@build.commit.ref} +%p + Message: #{@build.commit.git_commit_message} + +%p + Url: #{link_to @build.short_sha, ci_project_build_url(@project, @build)} diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb new file mode 100644 index 00000000000..1add215a1c8 --- /dev/null +++ b/app/views/ci/notify/build_fail_email.text.erb @@ -0,0 +1,9 @@ +Build failed for <%= @project.name %> + +Status: <%= @build.status %> +Commit: <%= @build.commit.short_sha %> +Author: <%= @build.commit.git_author_name %> +Branch: <%= @build.commit.ref %> +Message: <%= @build.commit.git_commit_message %> + +Url: <%= ci_project_build_url(@build.project, @build) %> diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml new file mode 100644 index 00000000000..a20dcaee24e --- /dev/null +++ b/app/views/ci/notify/build_success_email.html.haml @@ -0,0 +1,20 @@ +- content_for :header do + %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + GitLab CI (build successful) + +%h3 + Project: + = link_to ci_project_url(@project) do + = @project.name + +%p + Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)} +%p + Author: #{@build.commit.git_author_name} +%p + Branch: #{@build.commit.ref} +%p + Message: #{@build.commit.git_commit_message} + +%p + Url: #{link_to @build.short_sha, ci_project_build_url(@project, @build)} diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb new file mode 100644 index 00000000000..7ebd17e7270 --- /dev/null +++ b/app/views/ci/notify/build_success_email.text.erb @@ -0,0 +1,9 @@ +Build successful for <%= @project.name %> + +Status: <%= @build.status %> +Commit: <%= @build.commit.short_sha %> +Author: <%= @build.commit.git_author_name %> +Branch: <%= @build.commit.ref %> +Message: <%= @build.commit.git_commit_message %> + +Url: <%= ci_project_build_url(@build.project, @build) %> diff --git a/app/views/ci/projects/_form.html.haml b/app/views/ci/projects/_form.html.haml new file mode 100644 index 00000000000..d50e1a83b06 --- /dev/null +++ b/app/views/ci/projects/_form.html.haml @@ -0,0 +1,101 @@ +.bs-callout.help-callout + %p + If you want to test your .gitlab-ci.yml, you can use special tool - #{link_to "Lint", ci_lint_path} + %p + Edit your + #{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@project)} + += nested_form_for [:ci, @project], html: { class: 'form-horizontal' } do |f| + - if @project.errors.any? + #error_explanation + %p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:" + .alert.alert-error + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + + %fieldset + %legend Build settings + .form-group + = label_tag nil, class: 'control-label' do + Get code + .col-sm-10 + %p Get recent application code using the following command: + .radio + = label_tag do + = f.radio_button :allow_git_fetch, 'false' + %strong git clone + .light Slower but makes sure you have a clean dir before every build + .radio + = label_tag do + = f.radio_button :allow_git_fetch, 'true' + %strong git fetch + .light Faster + .form-group + = f.label :timeout_in_minutes, 'Timeout', class: 'control-label' + .col-sm-10 + = f.number_field :timeout_in_minutes, class: 'form-control', min: '0' + .light per build in minutes + + + %fieldset + %legend Build Schedule + .form-group + = f.label :always_build, 'Schedule build', class: 'control-label' + .col-sm-10 + .checkbox + = f.label :always_build do + = f.check_box :always_build + %span.light Repeat last build after X hours if no builds + .form-group + = f.label :polling_interval, "Build interval", class: 'control-label' + .col-sm-10 + = f.number_field :polling_interval, placeholder: '5', min: '0', class: 'form-control' + .light In hours + + %fieldset + %legend Project settings + .form-group + = f.label :default_ref, "Make tabs for the following branches", class: 'control-label' + .col-sm-10 + = f.text_field :default_ref, class: 'form-control', placeholder: 'master, stable' + .light You will be able to filter builds by the following branches + .form-group + = f.label :public, 'Public mode', class: 'control-label' + .col-sm-10 + .checkbox + = f.label :public do + = f.check_box :public + %span.light Anyone can see project and builds + .form-group + = f.label :coverage_regex, "Test coverage parsing", class: 'control-label' + .col-sm-10 + .input-group + %span.input-group-addon / + = f.text_field :coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + .light We will use this regular expression to find test coverage output in build trace. Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%$ + + + + %fieldset + %legend Advanced settings + .form-group + = f.label :token, "CI token", class: 'control-label' + .col-sm-10 + = f.text_field :token, class: 'form-control', placeholder: 'xEeFCaDAB89' + + .form-actions + = f.submit 'Save changes', class: 'btn btn-save' + = link_to 'Cancel', projects_path, class: 'btn' + - unless @project.new_record? + = link_to 'Remove Project', ci_project_path(@project), method: :delete, data: { confirm: 'Project will be removed. Are you sure?' }, class: 'btn btn-danger pull-right' diff --git a/app/views/ci/projects/_gl_projects.html.haml b/app/views/ci/projects/_gl_projects.html.haml new file mode 100644 index 00000000000..6ed19e13887 --- /dev/null +++ b/app/views/ci/projects/_gl_projects.html.haml @@ -0,0 +1,15 @@ +- @gl_projects.sort_by(&:name_with_namespace).each do |project| + %tr.light + %td + = project.name_with_namespace + %td + %small Not added to CI + %td + %td + - if Ci::Project.already_added?(project) + %strong.cgreen + Added + - else + = form_tag ci_projects_path do + = hidden_field_tag :project, project.to_h.to_json + = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm' diff --git a/app/views/ci/projects/_info.html.haml b/app/views/ci/projects/_info.html.haml new file mode 100644 index 00000000000..1888e1bde93 --- /dev/null +++ b/app/views/ci/projects/_info.html.haml @@ -0,0 +1,2 @@ +- if no_runners_for_project?(@project) + = render 'no_runners' diff --git a/app/views/ci/projects/_no_runners.html.haml b/app/views/ci/projects/_no_runners.html.haml new file mode 100644 index 00000000000..c0a296fb17d --- /dev/null +++ b/app/views/ci/projects/_no_runners.html.haml @@ -0,0 +1,8 @@ +.alert.alert-danger + %p + There are NO runners to build this project. + %br + You can add Specific runner for this project on Runners page + + - if current_user.is_admin + or add Shared runner for whole application in admin are. diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml new file mode 100644 index 00000000000..3e893410df8 --- /dev/null +++ b/app/views/ci/projects/_project.html.haml @@ -0,0 +1,22 @@ +- last_commit = project.last_commit +%tr.alert{class: commit_status_alert_class(last_commit) } + %td + = link_to project do + %strong= project.name + %td + - if last_commit + #{last_commit.status} (#{commit_link(last_commit)}) + - if project.last_commit_date + = time_ago_in_words project.last_commit_date + ago + - else + No builds yet + %td + - if project.public + %i.fa-globe + Public + - else + %i.fa-lock + Private + %td + = project.commits.count diff --git a/app/views/ci/projects/_public.html.haml b/app/views/ci/projects/_public.html.haml new file mode 100644 index 00000000000..c2157ab741a --- /dev/null +++ b/app/views/ci/projects/_public.html.haml @@ -0,0 +1,21 @@ += content_for :title do + %h3.project-title + Public projects + +.bs-callout + = link_to new_ci_user_sessions_path(state: generate_oauth_state(request.fullpath)) do + %strong Login with GitLab + to see your private projects + +- if @projects.present? + .projects + %table.table + %tr + %th Name + %th Last commit + %th Access + %th Commits + = render @projects + = paginate @projects +- else + %h4 No public projects yet diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml new file mode 100644 index 00000000000..37fb804d8d0 --- /dev/null +++ b/app/views/ci/projects/_search.html.haml @@ -0,0 +1,18 @@ +.search + = form_tag "#", method: :get, class: 'navbar-form' do |f| + .form-group + .input-group + = search_field_tag "search", params[:search], placeholder: "Search", class: "search-input form-control" + .input-group-addon + %i.fa-search + + +:coffeescript + $('.search .navbar-form').submit -> + NProgress.start() + query = $('.search .navbar-form .search-input').val() + $.get '#{gitlab_ci_projects_path}', { search: query }, (data) -> + $(".projects").html data.html + NProgress.done() + CiPager.init "#{gitlab_ci_projects_path}" + "?search=" + query, #{Ci::ProjectsController::PROJECTS_BATCH}, false + false diff --git a/app/views/ci/projects/edit.html.haml b/app/views/ci/projects/edit.html.haml new file mode 100644 index 00000000000..298007a6565 --- /dev/null +++ b/app/views/ci/projects/edit.html.haml @@ -0,0 +1,21 @@ +- if @project.generated_yaml_config + %p.alert.alert-danger + CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_project_path(@project)} + or + %a.preview-yml{:href => "#yaml-content", "data-toggle" => "modal"} preview + yaml file which is based on your old jobs. + Put this file to the root of your project and name it .gitlab-ci.yml + += render 'form' + +- if @project.generated_yaml_config + #yaml-content.modal.fade{"aria-hidden" => "true", "aria-labelledby" => ".gitlab-ci.yml", :role => "dialog", :tabindex => "-1"} + .modal-dialog + .modal-content + .modal-header + %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} × + %h4.modal-title Content of .gitlab-ci.yml + .modal-body + = text_area_tag :yaml, @project.generated_yaml_config, size: "70x25", class: "form-control" + .modal-footer + %button.btn.btn-default{"data-dismiss" => "modal", :type => "button"} Close diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml new file mode 100644 index 00000000000..dbc0ea0880f --- /dev/null +++ b/app/views/ci/projects/gitlab.html.haml @@ -0,0 +1,35 @@ +- if @offset == 0 + .clearfix.light + .pull-left.fetch-status + Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink}) + - if params[:search].present? + by keyword: "#{params[:search]}", + #{time_ago_in_words(current_user.sync_at)} ago. + = link_to gitlab_ci_projects_path(reset_cache: true, search: params[:search]), class: 'sync-now btn btn-sm btn-default reset-cache' do + %i.fa-refresh + Sync now + %br + + .pull-right + #{@total_count} projects, #{@projects.size} of them added to CI + %br + + %table.table.projects-table.content-list + %thead + %tr + %th Project Name + %th Last commit + %th Access + %th Commits + + = render @projects + + = render "gl_projects" + + %p.text-center.hide.loading + %i.fa-refresh.fa-spin + +- else + = render @projects + + = render "gl_projects" diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml new file mode 100644 index 00000000000..6243a28f9e2 --- /dev/null +++ b/app/views/ci/projects/index.html.haml @@ -0,0 +1,22 @@ +- if current_user + = content_for :title do + %h3.project-title + Dashboard + .pull-right + = render "search" + + .projects + %p.fetch-status.light + %i.fa-refresh.fa-spin + Please wait while we fetch from GitLab (#{GitlabCi.config.gitlab_server.url}) + :coffeescript + $.get '#{gitlab_ci_projects_path}', (data) -> + $(".projects").html data.html + $('.projects').on 'click', '.reset-cache', -> + $.get '#{gitlab_ci_projects_path}', { reset_cache: true }, (data) -> + $(".projects").html data.html + false + CiPager.init "#{gitlab_ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false + +- else + = render 'public' diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml new file mode 100644 index 00000000000..27899591391 --- /dev/null +++ b/app/views/ci/projects/show.html.haml @@ -0,0 +1,59 @@ += render 'ci/shared/guide' unless @project.setup_finished? + +- if current_user && current_user.can_manage_project?(@project.gitlab_id) && !@project.any_runners? + .alert.alert-danger + Builds for this project wont be served unless you configure runners on + = link_to "Runners page", ci_project_runners_path(@project) + +%ul.nav.nav-tabs.append-bottom-20 + %li{class: ref_tab_class} + = link_to 'All commits', ci_project_path(@project) + - @project.tracked_refs.each do |ref| + %li{class: ref_tab_class(ref)} + = link_to ref, ci_project_path(@project, ref: ref) + + - if @ref && !@project.tracked_refs.include?(@ref) + %li{class: 'active'} + = link_to @ref, ci_project_path(@project, ref: @ref) + + + +- if @ref + %p + Paste build status image for #{@ref} with next link + = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do + Status Badge + .badge-codes-block.bs-callout.bs-callout-info.hide + %p + Status badge for + %span.label.label-info #{@ref} + branch + %div + %label Markdown: + = text_field_tag 'badge_md', markdown_badge_code(@project, @ref), readonly: true, class: 'form-control' + %label Html: + = text_field_tag 'badge_html', html_badge_code(@project, @ref), readonly: true, class: 'form-control' + + + + +%table.builds + %thead + %tr + %th Status + %th Commit + %th Message + %th Branch + %th Total duration + %th Finished at + - if @project.coverage_enabled? + %th Coverage + + = render @commits + += paginate @commits + +- if @commits.empty? + .bs-callout + %h4 No commits yet + diff --git a/app/views/ci/runners/_runner.html.haml b/app/views/ci/runners/_runner.html.haml new file mode 100644 index 00000000000..7ead5736bb1 --- /dev/null +++ b/app/views/ci/runners/_runner.html.haml @@ -0,0 +1,35 @@ +%li.runner{id: dom_id(runner)} + %h4 + = runner_status_icon(runner) + %span.monospace + - if @runners.include?(runner) + = link_to runner.short_sha, [:ci, @project, runner] + %small + =link_to edit_ci_project_runner_path(@project, runner) do + %i.fa.fa-edit.btn + - else + = runner.short_sha + + .pull-right + - if @runners.include?(runner) + - if runner.belongs_to_one_project? + = link_to 'Remove runner', [:ci, @project, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - else + - runner_project = @project.runner_projects.find_by(runner_id: runner) + = link_to 'Disable for this project', [:ci, @project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - elsif runner.specific? + = form_for [:ci, @project, @project.runner_projects.new] do |f| + = f.hidden_field :runner_id, value: runner.id + = f.submit 'Enable for this project', class: 'btn btn-sm' + .pull-right + %small.light + \##{runner.id} + - if runner.description.present? + %p.runner-description + = runner.description + - if runner.tag_list.present? + %p + - runner.tag_list.each do |tag| + %span.label.label-primary + = tag + diff --git a/app/views/ci/runners/_shared_runners.html.haml b/app/views/ci/runners/_shared_runners.html.haml new file mode 100644 index 00000000000..944b3fd930d --- /dev/null +++ b/app/views/ci/runners/_shared_runners.html.haml @@ -0,0 +1,23 @@ +%h3 Shared runners + +.bs-callout.bs-callout-warning + GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. + %hr + - if @project.shared_runners_enabled + = link_to toggle_shared_runners_ci_project_path(@project), class: 'btn btn-warning', method: :post do + Disable shared runners + - else + = link_to toggle_shared_runners_ci_project_path(@project), class: 'btn btn-success', method: :post do + Enable shared runners +   for this project + +- if @shared_runners_count.zero? + This application has no shared runners yet. + Please use specific runners or ask administrator to create one +- else + %h4.underlined-title Available shared runners - #{@shared_runners_count} + %ul.bordered-list.available-shared-runners + = render @shared_runners.first(10) + - if @shared_runners_count > 10 + .light + and #{@shared_runners_count - 10} more... diff --git a/app/views/ci/runners/_specific_runners.html.haml b/app/views/ci/runners/_specific_runners.html.haml new file mode 100644 index 00000000000..0604e7a46c5 --- /dev/null +++ b/app/views/ci/runners/_specific_runners.html.haml @@ -0,0 +1,29 @@ +%h3 Specific runners + +.bs-callout.help-callout + %h4 How to setup a new project specific runner + + %ol + %li + Install GitLab Runner software. + Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it + %li + Specify following URL during runner setup: + %code #{ci_root_url(only_path: false)} + %li + Use the following registration token during setup: + %code #{@project.token} + %li + Start runner! + + +- if @runners.any? + %h4.underlined-title Runners activated for this project + %ul.bordered-list.activated-specific-runners + = render @runners + +- if @specific_runners.any? + %h4.underlined-title Available specific runners + %ul.bordered-list.available-specific-runners + = render @specific_runners + = paginate @specific_runners diff --git a/app/views/ci/runners/edit.html.haml b/app/views/ci/runners/edit.html.haml new file mode 100644 index 00000000000..81c8e58ae2b --- /dev/null +++ b/app/views/ci/runners/edit.html.haml @@ -0,0 +1,27 @@ +%h4 Runner ##{@runner.id} +%hr += form_for [:ci, @project, @runner], html: { class: 'form-horizontal' } do |f| + .form-group + = label :active, "Active", class: 'control-label' + .col-sm-10 + .checkbox + = f.check_box :active + %span.light Paused runners don't accept new builds + .form-group + = label_tag :token, class: 'control-label' do + Token + .col-sm-10 + = f.text_field :token, class: 'form-control', readonly: true + .form-group + = label_tag :description, class: 'control-label' do + Description + .col-sm-10 + = f.text_field :description, class: 'form-control' + .form-group + = label_tag :tag_list, class: 'control-label' do + Tags + .col-sm-10 + = f.text_field :tag_list, class: 'form-control' + .help-block You can setup jobs to only use runners with specific tags + .form-actions + = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/ci/runners/index.html.haml b/app/views/ci/runners/index.html.haml new file mode 100644 index 00000000000..529fb9c296d --- /dev/null +++ b/app/views/ci/runners/index.html.haml @@ -0,0 +1,25 @@ +.light + %p + A 'runner' is a process which runs a build. + You can setup as many runners as you need. + %br + Runners can be placed on separate users, servers, and even on your local machine. + + %p Each runner can be in one of the following states: + %div + %ul + %li + %span.label.label-success active + \- runner is active and can process any new build + %li + %span.label.label-danger paused + \- runner is paused and will not receive any new build + +%hr + +%p.lead To start serving your builds you can either add specific runners to your project or use shared runners +.row + .col-sm-6 + = render 'specific_runners' + .col-sm-6 + = render 'shared_runners' diff --git a/app/views/ci/runners/show.html.haml b/app/views/ci/runners/show.html.haml new file mode 100644 index 00000000000..ffec495f85a --- /dev/null +++ b/app/views/ci/runners/show.html.haml @@ -0,0 +1,64 @@ += content_for :title do + %h3.project-title + Runner ##{@runner.id} + .pull-right + - if @runner.shared? + %span.runner-state.runner-state-shared + Shared + - else + %span.runner-state.runner-state-specific + Specific + +%table.table + %thead + %tr + %th Property Name + %th Value + %tr + %td + Tags + %td + - @runner.tag_list.each do |tag| + %span.label.label-primary + = tag + %tr + %td + Name + %td + = @runner.name + %tr + %td + Version + %td + = @runner.version + %tr + %td + Revision + %td + = @runner.revision + %tr + %td + Platform + %td + = @runner.platform + %tr + %td + Architecture + %td + = @runner.architecture + %tr + %td + Description + %td + = @runner.description + %tr + %td + Last contact + %td + - if @runner.contacted_at + #{time_ago_in_words(@runner.contacted_at)} ago + - else + Never + + + diff --git a/app/views/ci/services/_form.html.haml b/app/views/ci/services/_form.html.haml new file mode 100644 index 00000000000..9110aaa0528 --- /dev/null +++ b/app/views/ci/services/_form.html.haml @@ -0,0 +1,57 @@ +%h3.page-title + = @service.title + = boolean_to_icon @service.activated? + +%p= @service.description + +.back-link + = link_to ci_project_services_path(@project) do + ← to services + +%hr + += form_for(@service, as: :service, url: ci_project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| + - if @service.errors.any? + .alert.alert-danger + %ul + - @service.errors.full_messages.each do |msg| + %li= msg + + - if @service.help.present? + .bs-callout + = @service.help + + .form-group + = f.label :active, "Active", class: "control-label" + .col-sm-10 + = f.check_box :active + + - @service.fields.each do |field| + - name = field[:name] + - label = field[:label] || name + - value = @service.send(name) + - type = field[:type] + - placeholder = field[:placeholder] + - choices = field[:choices] + - default_choice = field[:default_choice] + - help = field[:help] + + .form-group + = f.label label, class: "control-label" + .col-sm-10 + - if type == 'text' + = f.text_field name, class: "form-control", placeholder: placeholder + - elsif type == 'textarea' + = f.text_area name, rows: 5, class: "form-control", placeholder: placeholder + - elsif type == 'checkbox' + = f.check_box name + - elsif type == 'select' + = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } + - if help + .light #{help} + + .form-actions + = f.submit 'Save', class: 'btn btn-save' +   + - if @service.valid? && @service.activated? && @service.can_test? + = link_to 'Test settings', test_ci_project_service_path(@project, @service.to_param), class: 'btn' diff --git a/app/views/ci/services/edit.html.haml b/app/views/ci/services/edit.html.haml new file mode 100644 index 00000000000..bcc5832792f --- /dev/null +++ b/app/views/ci/services/edit.html.haml @@ -0,0 +1 @@ += render 'form' diff --git a/app/views/ci/services/index.html.haml b/app/views/ci/services/index.html.haml new file mode 100644 index 00000000000..37e5723b541 --- /dev/null +++ b/app/views/ci/services/index.html.haml @@ -0,0 +1,22 @@ +%h3.page-title Project services +%p.light Project services allow you to integrate GitLab CI with other applications + +%table.table + %thead + %tr + %th + %th Service + %th Desription + %th Last edit + - @services.sort_by(&:title).each do |service| + %tr + %td + = boolean_to_icon service.activated? + %td + = link_to edit_ci_project_service_path(@project, service.to_param) do + %strong= service.title + %td + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml new file mode 100644 index 00000000000..8a42f29b77c --- /dev/null +++ b/app/views/ci/shared/_guide.html.haml @@ -0,0 +1,15 @@ +.bs-callout.help-callout + %h4 How to setup CI for this project + + %ol + %li + Add at least one runner to the project. + Go to #{link_to 'Runners page', ci_project_runners_path(@project), target: :blank} for instructions. + %li + Put the .gitlab-ci.yml in the root of your repository. Examples can be found in #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. + You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} + %li + Visit #{link_to 'GitLab project settings', @project.gitlab_url + "/services/gitlab_ci/edit", target: :blank} + and press the "Test settings" button. + %li + Return to this page and refresh it, it should show a new build. diff --git a/app/views/ci/shared/_no_runners.html.haml b/app/views/ci/shared/_no_runners.html.haml new file mode 100644 index 00000000000..f56c37d9b37 --- /dev/null +++ b/app/views/ci/shared/_no_runners.html.haml @@ -0,0 +1,7 @@ +.alert.alert-danger + %p + Now you need Runners to process your builds. + %span + Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it + + diff --git a/app/views/ci/triggers/_trigger.html.haml b/app/views/ci/triggers/_trigger.html.haml new file mode 100644 index 00000000000..addfbfcb0d4 --- /dev/null +++ b/app/views/ci/triggers/_trigger.html.haml @@ -0,0 +1,14 @@ +%tr + %td + .clearfix + %span.monospace= trigger.token + + %td + - if trigger.last_trigger_request + #{time_ago_in_words(trigger.last_trigger_request.created_at)} ago + - else + Never + + %td + .pull-right + = link_to 'Revoke', ci_project_trigger_path(@project, trigger), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-danger btn-sm btn-grouped" diff --git a/app/views/ci/triggers/index.html.haml b/app/views/ci/triggers/index.html.haml new file mode 100644 index 00000000000..f04c116231d --- /dev/null +++ b/app/views/ci/triggers/index.html.haml @@ -0,0 +1,67 @@ +%h3 + Triggers + +%p.light + Triggers can be used to force a rebuild of a specific branch or tag with an API call. + +%hr.clearfix + +-if @triggers.any? + %table.table + %thead + %th Token + %th Last used + %th + = render @triggers +- else + %h4 No triggers + += form_for [:ci, @project, @trigger], html: { class: 'form-horizontal' } do |f| + .clearfix + = f.submit "Add Trigger", class: 'btn btn-success pull-right' + +%hr.clearfix + +-if @triggers.any? + %h3 + Use CURL + + %p.light + Copy the token above and set your branch or tag name. This is the reference that will be rebuild. + + + %pre + :plain + curl -X POST \ + -F token=TOKEN \ + #{ci_build_trigger_url(@project.id, 'REF_NAME')} + %h3 + Use .gitlab-ci.yml + + %p.light + Copy the snippet to + %i .gitlab-ci.yml + of dependent project. + At the end of your build it will trigger this project to rebuilt. + + %pre + :plain + trigger: + type: deploy + script: + - "curl -X POST -F token=TOKEN #{ci_build_trigger_url(@project.id, 'REF_NAME')}" + %h3 + Pass build variables + + %p.light + Add + %strong variables[VARIABLE]=VALUE + to API request. + The value of variable could then be used to distinguish triggered build from normal one. + + %pre + :plain + curl -X POST \ + -F token=TOKEN \ + -F "variables[RUN_NIGHTLY_BUILD]=true" \ + #{ci_build_trigger_url(@project.id, 'REF_NAME')} diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml new file mode 100644 index 00000000000..308b217ea78 --- /dev/null +++ b/app/views/ci/user_sessions/new.html.haml @@ -0,0 +1,8 @@ +.login-block + %h2 Login using GitLab account + %p.light + Make sure you have account on GitLab server + = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink + %hr + = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' ) + diff --git a/app/views/ci/user_sessions/show.html.haml b/app/views/ci/user_sessions/show.html.haml new file mode 100644 index 00000000000..43f64a429b2 --- /dev/null +++ b/app/views/ci/user_sessions/show.html.haml @@ -0,0 +1,15 @@ += image_tag user_avatar_url(current_user, 90), class: 'avatar avatar-inline avatar-tile s90', alt: '' +%h3 + Hi, #{@user.name} + + - if @user.is_admin + %span.label.label-success Admin + +.profile-block + %p + %span.light Email: + %strong= @user.email + + %p + %span.light GitLab profile: + %strong= link_to @user.username, GitlabCi.config.gitlab_server.url + '/u/' + @user.username, target: "_blank" diff --git a/app/views/ci/variables/show.html.haml b/app/views/ci/variables/show.html.haml new file mode 100644 index 00000000000..5cced18a09f --- /dev/null +++ b/app/views/ci/variables/show.html.haml @@ -0,0 +1,37 @@ +%h3 Secret Variables +%p.light + These variables will be set to environment by the runner and will be hidden in the build log. + %br + So you can use them for passwords, secret keys or whatever you want. + +%hr + + += nested_form_for @project, url: url_for(controller: 'ci/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| + - if @project.errors.any? + #error_explanation + %p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:" + .alert.alert-error + %ul + - @project.errors.full_messages.each do |msg| + %li= msg + + = f.fields_for :variables do |variable_form| + .form-group + = variable_form.label :key, 'Key', class: 'control-label' + .col-sm-10 + = variable_form.text_field :key, class: 'form-control', placeholder: "PROJECT_VARIABLE" + + .form-group + = variable_form.label :value, 'Value', class: 'control-label' + .col-sm-10 + = variable_form.text_area :value, class: 'form-control', rows: 2, placeholder: "" + + = variable_form.link_to_remove "Remove this variable", class: 'btn btn-danger pull-right prepend-top-10' + %hr + %p + .clearfix + = f.link_to_add "Add a variable", :variables, class: 'btn btn-success pull-right' + + .form-actions + = f.submit 'Save changes', class: 'btn btn-save', return_to: request.original_url diff --git a/app/views/ci/web_hooks/index.html.haml b/app/views/ci/web_hooks/index.html.haml new file mode 100644 index 00000000000..92c43cd1d9d --- /dev/null +++ b/app/views/ci/web_hooks/index.html.haml @@ -0,0 +1,92 @@ +%h3 + Web hooks + +%p.light + Web Hooks can be used for binding events when build completed. + +%hr.clearfix + += form_for [:ci, @project, @web_hook], html: { class: 'form-horizontal' } do |f| + -if @web_hook.errors.any? + .alert.alert-danger + - @web_hook.errors.full_messages.each do |msg| + %p= msg + .form-group + = f.label :url, "URL", class: 'control-label' + .col-sm-10 + = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' + .form-actions + = f.submit "Add Web Hook", class: "btn btn-create" + +-if @web_hooks.any? + %h4 Activated web hooks (#{@web_hooks.count}) + %table.table + - @web_hooks.each do |hook| + %tr + %td + .clearfix + %span.monospace= hook.url + %td + .pull-right + - if @project.commits.any? + = link_to 'Test Hook', test_ci_project_web_hook_path(@project, hook), class: "btn btn-sm btn-grouped" + = link_to 'Remove', ci_project_web_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" + +%h4 Web Hook data example + +:erb +
    +    
    +      {
    +        "build_id": 2,
    +        "build_name":"rspec_linux"
    +        "build_status": "failed",
    +        "build_started_at": "2014-05-05T18:01:02.563Z",
    +        "build_finished_at": "2014-05-05T18:01:07.611Z",
    +        "project_id": 1,
    +        "project_name": "Brightbox \/ Brightbox Cli",
    +        "gitlab_url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli",
    +        "ref": "master",
    +        "sha": "a26cf5de9ed9827746d4970872376b10d9325f40",
    +        "before_sha": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    +        "push_data": {
    +          "before": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    +          "after": "a26cf5de9ed9827746d4970872376b10d9325f40",
    +          "ref": "refs\/heads\/master",
    +          "user_id": 1,
    +          "user_name": "Administrator",
    +          "project_id": 5,
    +          "repository": {
    +            "name": "Brightbox Cli",
    +            "url": "dzaporozhets@localhost:brightbox\/brightbox-cli.git",
    +            "description": "Voluptatibus quae error consectetur voluptas dolores vel excepturi possimus.",
    +            "homepage": "http:\/\/localhost:3000\/brightbox\/brightbox-cli"
    +          },
    +          "commits": [
    +            {
    +              "id": "a26cf5de9ed9827746d4970872376b10d9325f40",
    +              "message": "Release v1.2.2",
    +              "timestamp": "2014-04-22T16:46:42+03:00",
    +              "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/a26cf5de9ed9827746d4970872376b10d9325f40",
    +              "author": {
    +                "name": "Paul Thornthwaite",
    +                "email": "tokengeek@gmail.com"
    +              }
    +            },
    +            {
    +              "id": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    +              "message": "Fix server user data update\n\nIncorrect condition was being used so Base64 encoding option was having\nopposite effect from desired.",
    +              "timestamp": "2014-04-11T18:17:26+03:00",
    +              "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    +              "author": {
    +                "name": "Paul Thornthwaite",
    +                "email": "tokengeek@gmail.com"
    +              }
    +            }
    +          ],
    +          "total_commits_count": 2,
    +          "ci_yaml_file":"rspec_linux:\r\n  script: ls\r\n"
    +        }
    +      }
    +    
    +  
    diff --git a/app/views/layouts/ci/_head.html.haml b/app/views/layouts/ci/_head.html.haml new file mode 100644 index 00000000000..871752c9812 --- /dev/null +++ b/app/views/layouts/ci/_head.html.haml @@ -0,0 +1,11 @@ +%head + %meta{charset: "utf-8"} + %meta{content: "GitLab Continuous Integration", name: "description"} + %title GitLab CI + = stylesheet_link_tag "ci/application", :media => "all" + = javascript_include_tag "ci/application" + = csrf_meta_tags + = favicon_link_tag 'ci/favicon.ico' + :erb + + diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml new file mode 100644 index 00000000000..bce3ce77031 --- /dev/null +++ b/app/views/layouts/ci/_info.html.haml @@ -0,0 +1,9 @@ +.container + - if alert || notice + - if alert + .alert.alert-danger= alert + - if notice + .alert.alert-info= notice + + - if current_user && current_user.is_admin && Ci::Runner.count.zero? + = render 'ci/shared/no_runners' diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml new file mode 100644 index 00000000000..4e944d4d0d6 --- /dev/null +++ b/app/views/layouts/ci/_nav.html.haml @@ -0,0 +1,32 @@ +.navbar.navbar-static-top.navbar-ci + .container + .navbar-header + %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} + %span.sr-only Toggle navigation + %i.fa-reorder + + = link_to 'GitLab CI', ci_root_path, class: "navbar-brand" + + .collapse.navbar-collapse + %ul.nav.navbar-nav + - if current_user && current_user.is_admin + %li + = link_to ci_admin_projects_path do + Admin + %li + = link_to 'Help', ci_help_path + + %ul.nav.navbar-nav.pull-right + - if current_user + %li + = link_to ci_user_sessions_path do + .profile-holder + = image_tag user_avatar_url(current_user, 64), class: 'avatar s32', alt: '' + %span= current_user.name + %li + = link_to ci_user_sessions_path, class: "logout", method: :delete do + %i.fa-signout + Logout + - else + %li + = link_to "Login with GitLab", auth_ci_user_sessions_path, no_turbolink.merge(class: 'btn btn-success btn-login') diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml new file mode 100644 index 00000000000..792a5f1e4dd --- /dev/null +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -0,0 +1,28 @@ +%ul.nav.nav-pills.nav-stacked.admin-menu + = nav_link path: 'projects' do + = link_to ci_admin_projects_path do + %i.fa-list-alt + Projects + = nav_link path: 'events' do + = link_to ci_admin_events_path do + %i.fa-book + Events + = nav_link path: 'runners#index' do + = link_to ci_admin_runners_path do + %i.fa-cog + Runners + %small.pull-right + = Ci::Runner.count(:all) + = nav_link path: 'builds' do + = link_to ci_admin_builds_path do + %i.fa-link + Builds + %small.pull-right + = Ci::Build.count(:all) + %li + %hr + = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do + = link_to ci_admin_application_settings_path do + %i.fa-cogs + %span + Settings diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml new file mode 100644 index 00000000000..24ee1609d25 --- /dev/null +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -0,0 +1,40 @@ +%ul.nav.nav-pills.nav-stacked.project-menu + = nav_link path: 'projects#show' do + = link_to ci_project_path(@project) do + %i.fa-list-alt + Commits + %small.pull-right= @project.commits.count + = nav_link path: 'charts#show' do + = link_to ci_project_charts_path(@project) do + %i.fa-bar-chart + Charts + = nav_link path: ['runners#index', 'runners#show'] do + = link_to ci_project_runners_path(@project) do + %i.fa-cog + Runners + = nav_link path: 'variables#index' do + = link_to ci_project_variables_path(@project) do + %i.fa-code + Variables + = nav_link path: 'web_hooks#index' do + = link_to ci_project_web_hooks_path(@project) do + %i.fa-link + Web Hooks + = nav_link path: 'triggers#index' do + = link_to ci_project_triggers_path(@project) do + %i.fa-retweet + Triggers + = nav_link path: 'services#index' do + = link_to ci_project_services_path(@project) do + %i.fa-share + Services + = nav_link path: 'events#index' do + = link_to ci_project_events_path(@project) do + %i.fa-book + Events + %li + %hr + = nav_link path: 'projects#edit' do + = link_to edit_ci_project_path(@project) do + %i.fa-cogs + Settings diff --git a/app/views/layouts/ci/admin.html.haml b/app/views/layouts/ci/admin.html.haml new file mode 100644 index 00000000000..71b767cc4f1 --- /dev/null +++ b/app/views/layouts/ci/admin.html.haml @@ -0,0 +1,17 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/ci/head' + %body{ :'data-page' => body_data_page } + = render 'layouts/ci/nav' + = render 'layouts/ci/info' + - if content_for?(:title) + .container.container-title + = yield(:title) + %hr + + .container + .row + .col-md-2.append-bottom-20 + = render 'layouts/ci/nav_admin' + .col-md-10 + = yield diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml new file mode 100644 index 00000000000..7306d378e44 --- /dev/null +++ b/app/views/layouts/ci/application.html.haml @@ -0,0 +1,13 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/ci/head' + %body{ :'data-page' => body_data_page } + = render 'layouts/ci/nav' + = render 'layouts/ci/info' + - if content_for?(:title) + .container.container-title + = yield(:title) + %hr + + .container.container-body + = yield diff --git a/app/views/layouts/ci/empty.html.haml b/app/views/layouts/ci/empty.html.haml new file mode 100644 index 00000000000..a36ebee7ef3 --- /dev/null +++ b/app/views/layouts/ci/empty.html.haml @@ -0,0 +1,13 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/ci/head' + %body{ :'data-page' => body_data_page } + = render 'layouts/ci/info' + - if content_for?(:title) + .container.container-title + = yield(:title) + %hr + + .container.container-body + = yield + diff --git a/app/views/layouts/ci/notify.html.haml b/app/views/layouts/ci/notify.html.haml new file mode 100644 index 00000000000..270b206df5e --- /dev/null +++ b/app/views/layouts/ci/notify.html.haml @@ -0,0 +1,19 @@ +%html{lang: "en"} + %head + %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} + %title + GitLab CI + + %body + = yield :header + + %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} + %tr + %td{align: "left", style: "margin: 0; padding: 10px;"} + = yield + %br + %tr + %td{align: "left", style: "margin: 0; padding: 10px;"} + %p{style: "font-size:small;color:#777"} + - if @project + You're receiving this notification because you are the one who triggered a build on the #{@project.name} project. diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml new file mode 100644 index 00000000000..d0c0861669d --- /dev/null +++ b/app/views/layouts/ci/project.html.haml @@ -0,0 +1,26 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/ci/head' + %body{ :'data-page' => body_data_page } + = render 'layouts/ci/nav' + = render 'layouts/ci/info' + .container + %h3.project-title + = @project.name + - if @project.public + %small + %i.fa-globe + Public + + .pull-right + = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' ) + %hr + .container + - if current_user && current_user.can_manage_project?(@project.gitlab_id) + .row + .col-md-2.append-bottom-20 + = render 'layouts/ci/nav_project' + .col-md-10 + = yield + - else + = yield diff --git a/app/workers/ci/hip_chat_notifier_worker.rb b/app/workers/ci/hip_chat_notifier_worker.rb new file mode 100644 index 00000000000..ebb43570e2a --- /dev/null +++ b/app/workers/ci/hip_chat_notifier_worker.rb @@ -0,0 +1,19 @@ +module Ci + class HipChatNotifierWorker + include Sidekiq::Worker + + def perform(message, options={}) + room = options.delete('room') + token = options.delete('token') + server = options.delete('server') + name = options.delete('service_name') + client_opts = { + api_version: 'v2', + server_url: server + } + + client = HipChat::Client.new(token, client_opts) + client[room].send(name, message, options.symbolize_keys) + end + end +end diff --git a/app/workers/ci/slack_notifier_worker.rb b/app/workers/ci/slack_notifier_worker.rb new file mode 100644 index 00000000000..3bbb9b4bec7 --- /dev/null +++ b/app/workers/ci/slack_notifier_worker.rb @@ -0,0 +1,10 @@ +module Ci + class SlackNotifierWorker + include Sidekiq::Worker + + def perform(webhook_url, message, options={}) + notifier = Slack::Notifier.new(webhook_url) + notifier.ping(message, options) + end + end +end diff --git a/app/workers/ci/web_hook_worker.rb b/app/workers/ci/web_hook_worker.rb new file mode 100644 index 00000000000..0bb83845572 --- /dev/null +++ b/app/workers/ci/web_hook_worker.rb @@ -0,0 +1,9 @@ +module Ci + class WebHookWorker + include Sidekiq::Worker + + def perform(hook_id, data) + Ci::WebHook.find(hook_id).execute data + end + end +end diff --git a/bin/background_jobs b/bin/background_jobs index a4895cf6586..d4578f6a222 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -37,7 +37,7 @@ start_no_deamonize() start_sidekiq() { - bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 + bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 } load_ok() diff --git a/bin/ci/upgrade.rb b/bin/ci/upgrade.rb new file mode 100644 index 00000000000..aab4f60ec60 --- /dev/null +++ b/bin/ci/upgrade.rb @@ -0,0 +1,3 @@ +require_relative "../lib/ci/upgrader" + +Ci::Upgrader.new.execute diff --git a/config/environments/development.rb b/config/environments/development.rb index 03af7f07864..d7d6aed1602 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -24,6 +24,11 @@ Gitlab::Application.configure do # Expands the lines which load the assets # config.assets.debug = true + + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true # For having correct urls in mails config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } diff --git a/config/gitlab_ci.yml b/config/gitlab_ci.yml new file mode 100644 index 00000000000..03a86307f40 --- /dev/null +++ b/config/gitlab_ci.yml @@ -0,0 +1,19 @@ +development: + gitlab_server: + url: 'http://gitlab.dev' + app_id: 'cfda7ec2551af42d06acc6dbda9087dbdc8d45b7e2bc240f498fad3f84ff4044' + app_secret: 'd1802d55db9c1aedc950812a9489e2659fa1430dc488babde949bc9c409cc01b' + + gitlab_ci: + host: 'http://ci.gitlab.dev' + port: 80 + https: false +test: + gitlab_server: + url: 'http://demo.gitlab.com/' + app_id: '' + app_secret: '' + gitlab_ci: + host: localhost + port: 80 + https: false diff --git a/config/gitlab_ci.yml.example b/config/gitlab_ci.yml.example new file mode 100644 index 00000000000..dd33daa5578 --- /dev/null +++ b/config/gitlab_ci.yml.example @@ -0,0 +1,68 @@ +# If you change this file in a Merge Request, please also create +# a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests + +defaults: &defaults + gitlab_server: + url: 'https://gitlab.example.com/' # Replace with your gitlab server url + app_id: '' + app_secret: '' + + ## Gitlab CI settings + gitlab_ci: + ## Web server settings + host: localhost + port: 80 + https: false + + ## Email settings + # Email address used in the "From" field in mails sent by GitLab-CI + email_from: gitlab-ci@localhost + + # Email address of your support contact (default: same as email_from) + support_email: support@localhost + + # Default project notifications settings: + # + # Send emails only on broken builds (default: true) + # all_broken_builds: true + # + # Add pusher to recipients list (default: false) + # add_pusher: true + + # The location where build traces are stored (default: builds/). Relative paths are relative to Rails.root + # builds_path: builds/ + + ## Backup settings + backup: + path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) + # keep_time: 604800 # default: 0 (forever) (in seconds) + # upload: + # # Fog storage connection settings, see http://fog.io/storage/ . + # connection: + # provider: AWS + # region: eu-west-1 + # aws_access_key_id: AKIAKIAKI + # aws_secret_access_key: 'secret123' + # # The remote 'directory' to store your backups. For S3, this would be the bucket name. + # remote_directory: 'my.s3.bucket' + # # Use multipart uploads when file size reaches 100MB, see + # # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html + # multipart_chunk_size: 104857600 + + +development: + <<: *defaults + +test: + <<: *defaults + gitlab_server: + url: 'http://demo.gitlab.com/' + app_id: 'id' + app_secret: 'secret' + gitlab_ci: + host: localhost + port: 80 + https: false + +production: + <<: *defaults diff --git a/config/gitlab_ci.yml.example.development b/config/gitlab_ci.yml.example.development new file mode 100644 index 00000000000..d23c4daf464 --- /dev/null +++ b/config/gitlab_ci.yml.example.development @@ -0,0 +1,19 @@ +development: + gitlab_server: + url: 'http://localhost:3000' + app_id: '' + app_secret: '' + + gitlab_ci: + host: localhost + port: 9000 + https: false +test: + gitlab_server: + url: 'http://demo.gitlab.com/' + app_id: '' + app_secret: '' + gitlab_ci: + host: localhost + port: 80 + https: false diff --git a/config/initializers/3_ci_settings.rb b/config/initializers/3_ci_settings.rb new file mode 100644 index 00000000000..5cdff48d316 --- /dev/null +++ b/config/initializers/3_ci_settings.rb @@ -0,0 +1,61 @@ +module Ci + class Settings < Settingslogic + source "#{Rails.root}/config/gitlab_ci.yml" + namespace Rails.env + + class << self + def gitlab_ci_on_non_standard_port? + ![443, 80].include?(gitlab_ci.port.to_i) + end + + private + + def build_gitlab_ci_url + if gitlab_ci_on_non_standard_port? + custom_port = ":#{gitlab_ci.port}" + else + custom_port = nil + end + [ gitlab_ci.protocol, + "://", + gitlab_ci.host, + custom_port, + gitlab_ci.relative_url_root + ].join('') + end + end + end +end + + +# +# GitlabCi +# +Ci::Settings['gitlab_ci'] ||= Settingslogic.new({}) +Ci::Settings.gitlab_ci['https'] = false if Ci::Settings.gitlab_ci['https'].nil? +Ci::Settings.gitlab_ci['host'] ||= 'localhost' +Ci::Settings.gitlab_ci['port'] ||= Ci::Settings.gitlab_ci.https ? 443 : 80 +Ci::Settings.gitlab_ci['relative_url_root'] ||= (ENV['RAILS_RELATIVE_URL_ROOT'] || '') + '/ci' +Ci::Settings.gitlab_ci['protocol'] ||= Ci::Settings.gitlab_ci.https ? "https" : "http" +Ci::Settings.gitlab_ci['email_from'] ||= "gitlab-ci@#{Ci::Settings.gitlab_ci.host}" +Ci::Settings.gitlab_ci['support_email'] ||= Ci::Settings.gitlab_ci.email_from +Ci::Settings.gitlab_ci['all_broken_builds'] = true if Ci::Settings.gitlab_ci['all_broken_builds'].nil? +Ci::Settings.gitlab_ci['add_pusher'] = false if Ci::Settings.gitlab_ci['add_pusher'].nil? +Ci::Settings.gitlab_ci['url'] ||= Ci::Settings.send(:build_gitlab_ci_url) +Ci::Settings.gitlab_ci['builds_path'] = File.expand_path(Ci::Settings.gitlab_ci['builds_path'] || "builds/", Rails.root + '/ci') + +# Compatibility with old config +Ci::Settings['gitlab_server_urls'] ||= Ci::Settings['allowed_gitlab_urls'] + +# +# Backup +# +Ci::Settings['backup'] ||= Settingslogic.new({}) +Ci::Settings.backup['keep_time'] ||= 0 +Ci::Settings.backup['path'] = File.expand_path(Ci::Settings.backup['path'] || "tmp/backups/", Rails.root) +Ci::Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) +# Convert upload connection settings to use symbol keys, to make Fog happy +if Ci::Settings.backup['upload']['connection'] + Ci::Settings.backup['upload']['connection'] = Hash[Ci::Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }] +end +Ci::Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 diff --git a/config/initializers/3_grit_ext.rb b/config/initializers/3_grit_ext.rb deleted file mode 100644 index 6540ac839cb..00000000000 --- a/config/initializers/3_grit_ext.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'grit' - -Grit::Git.git_binary = Gitlab.config.git.bin_path -Grit::Git.git_timeout = Gitlab.config.git.timeout -Grit::Git.git_max_size = Gitlab.config.git.max_size diff --git a/config/initializers/4_ci_app.rb b/config/initializers/4_ci_app.rb new file mode 100644 index 00000000000..60a30bf3bb7 --- /dev/null +++ b/config/initializers/4_ci_app.rb @@ -0,0 +1,10 @@ +module GitlabCi + VERSION = Gitlab::VERSION + REVISION = Gitlab::REVISION + + REGISTRATION_TOKEN = SecureRandom.hex(10) + + def self.config + Ci::Settings + end +end diff --git a/config/initializers/4_sidekiq.rb b/config/initializers/4_sidekiq.rb deleted file mode 100644 index e856499732e..00000000000 --- a/config/initializers/4_sidekiq.rb +++ /dev/null @@ -1,27 +0,0 @@ -# Custom Redis configuration -config_file = Rails.root.join('config', 'resque.yml') - -resque_url = if File.exists?(config_file) - YAML.load_file(config_file)[Rails.env] - else - "redis://localhost:6379" - end - -Sidekiq.configure_server do |config| - config.redis = { - url: resque_url, - namespace: 'resque:gitlab' - } - - config.server_middleware do |chain| - chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] - chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] - end -end - -Sidekiq.configure_client do |config| - config.redis = { - url: resque_url, - namespace: 'resque:gitlab' - } -end diff --git a/config/initializers/6_rack_profiler.rb b/config/initializers/6_rack_profiler.rb deleted file mode 100644 index 1d958904e8f..00000000000 --- a/config/initializers/6_rack_profiler.rb +++ /dev/null @@ -1,10 +0,0 @@ -if Rails.env.development? - require 'rack-mini-profiler' - - # initialization is skipped so trigger it - Rack::MiniProfilerRails.initialize!(Rails.application) - - Rack::MiniProfiler.config.position = 'right' - Rack::MiniProfiler.config.start_hidden = false - Rack::MiniProfiler.config.skip_paths << '/teaspoon' -end diff --git a/config/initializers/7_omniauth.rb b/config/initializers/7_omniauth.rb deleted file mode 100644 index 7f73546ac89..00000000000 --- a/config/initializers/7_omniauth.rb +++ /dev/null @@ -1,28 +0,0 @@ -if Gitlab::LDAP::Config.enabled? - module OmniAuth::Strategies - server = Gitlab.config.ldap.servers.values.first - klass = server['provider_class'] - const_set(klass, Class.new(LDAP)) unless klass == 'LDAP' - end - - OmniauthCallbacksController.class_eval do - server = Gitlab.config.ldap.servers.values.first - alias_method server['provider_name'], :ldap - end -end - -OmniAuth.config.full_host = Settings.gitlab['url'] -OmniAuth.config.allowed_request_methods = [:post] -#In case of auto sign-in, the GET method is used (users don't get to click on a button) -OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? -OmniAuth.config.before_request_phase do |env| - OmniAuth::RequestForgeryProtection.new(env).call -end - -if Gitlab.config.omniauth.enabled - Gitlab.config.omniauth.providers.each do |provider| - if provider['name'] == 'kerberos' - require 'omniauth-kerberos' - end - end -end diff --git a/config/initializers/8_default_url_options.rb b/config/initializers/8_default_url_options.rb deleted file mode 100644 index 8fd27b1d88e..00000000000 --- a/config/initializers/8_default_url_options.rb +++ /dev/null @@ -1,11 +0,0 @@ -default_url_options = { - host: Gitlab.config.gitlab.host, - protocol: Gitlab.config.gitlab.protocol, - script_name: Gitlab.config.gitlab.relative_url_root -} - -unless Gitlab.config.gitlab_on_standard_port? - default_url_options[:port] = Gitlab.config.gitlab.port -end - -Rails.application.routes.default_url_options = default_url_options diff --git a/config/initializers/connection_fix.rb b/config/initializers/connection_fix.rb new file mode 100644 index 00000000000..d831a1838ed --- /dev/null +++ b/config/initializers/connection_fix.rb @@ -0,0 +1,32 @@ +# from http://gist.github.com/238999 +# +# If your workers are inactive for a long period of time, they'll lose +# their MySQL connection. +# +# This hack ensures we re-connect whenever a connection is +# lost. Because, really. why not? +# +# Stick this in RAILS_ROOT/config/initializers/connection_fix.rb (or somewhere similar) +# +# From: +# http://coderrr.wordpress.com/2009/01/08/activerecord-threading-issues-and-resolutions/ + +if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) + module ActiveRecord::ConnectionAdapters + class Mysql2Adapter + alias_method :execute_without_retry, :execute + + def execute(*args) + execute_without_retry(*args) + rescue ActiveRecord::StatementInvalid => e + if e.message =~ /server has gone away/i + warn "Server timed out, retrying" + reconnect! + retry + else + raise e + end + end + end + end +end diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 00000000000..43adac8b2c6 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Gitlab::Application.config.action_dispatch.cookies_serializer = :hybrid diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb new file mode 100644 index 00000000000..f9f88f95db9 --- /dev/null +++ b/config/initializers/default_url_options.rb @@ -0,0 +1,11 @@ +default_url_options = { + host: Gitlab.config.gitlab.host, + protocol: Gitlab.config.gitlab.protocol, + script_name: Gitlab.config.gitlab.relative_url_root +} + +unless Gitlab.config.gitlab_on_standard_port? + default_url_options[:port] = Gitlab.config.gitlab.port +end + +Gitlab::Application.routes.default_url_options = default_url_options diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example index b1bbcca1d61..2155ea14562 100644 --- a/config/initializers/rack_attack.rb.example +++ b/config/initializers/rack_attack.rb.example @@ -4,13 +4,13 @@ # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests paths_to_be_protected = [ - "#{Rails.application.config.relative_url_root}/users/password", - "#{Rails.application.config.relative_url_root}/users/sign_in", - "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json", - "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session", - "#{Rails.application.config.relative_url_root}/users", - "#{Rails.application.config.relative_url_root}/users/confirmation", - "#{Rails.application.config.relative_url_root}/unsubscribes/" + "#{Gitlab::Application.config.relative_url_root}/users/password", + "#{Gitlab::Application.config.relative_url_root}/users/sign_in", + "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session.json", + "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session", + "#{Gitlab::Application.config.relative_url_root}/users", + "#{Gitlab::Application.config.relative_url_root}/users/confirmation", + "#{Gitlab::Application.config.relative_url_root}/unsubscribes/" ] diff --git a/config/initializers/rack_profiler.rb b/config/initializers/rack_profiler.rb new file mode 100644 index 00000000000..7710eeac453 --- /dev/null +++ b/config/initializers/rack_profiler.rb @@ -0,0 +1,10 @@ +if Rails.env.development? + require 'rack-mini-profiler' + + # initialization is skipped so trigger it + Rack::MiniProfilerRails.initialize!(Gitlab::Application) + + Rack::MiniProfiler.config.position = 'right' + Rack::MiniProfiler.config.start_hidden = false + Rack::MiniProfiler.config.skip_paths << '/teaspoon' +end diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 62a54bc8c63..1b518c3becf 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -24,3 +24,27 @@ end Gitlab::Application.config.secret_token = find_secure_token Gitlab::Application.config.secret_key_base = find_secure_token + +# CI +def generate_new_secure_token + SecureRandom.hex(64) +end + +if Gitlab::Application.secrets.db_key_base.blank? + warn "Missing `db_key_base` for '#{Rails.env}' environment. The secrets will be generated and stored in `config/secrets.yml`" + + all_secrets = YAML.load_file('config/secrets.yml') if File.exist?('config/secrets.yml') + all_secrets ||= {} + + # generate secrets + env_secrets = all_secrets[Rails.env.to_s] || {} + env_secrets['db_key_base'] ||= generate_new_secure_token + all_secrets[Rails.env.to_s] = env_secrets + + # save secrets + File.open('config/secrets.yml', 'w', 0600) do |file| + file.write(YAML.dump(all_secrets)) + end + + Gitlab::Application.secrets.db_key_base = env_secrets['db_key_base'] +end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 6d274cd95a1..c3bee1c7cec 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -11,5 +11,5 @@ Gitlab::Application.config.session_store( secure: Gitlab.config.gitlab.https, httponly: true, expire_after: Settings.gitlab['session_expire_delay'] * 60, - path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root + path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root ) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb new file mode 100644 index 00000000000..e856499732e --- /dev/null +++ b/config/initializers/sidekiq.rb @@ -0,0 +1,27 @@ +# Custom Redis configuration +config_file = Rails.root.join('config', 'resque.yml') + +resque_url = if File.exists?(config_file) + YAML.load_file(config_file)[Rails.env] + else + "redis://localhost:6379" + end + +Sidekiq.configure_server do |config| + config.redis = { + url: resque_url, + namespace: 'resque:gitlab' + } + + config.server_middleware do |chain| + chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] + chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] + end +end + +Sidekiq.configure_client do |config| + config.redis = { + url: resque_url, + namespace: 'resque:gitlab' + } +end diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index d9042c652bb..e6d5600edb7 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,4 +1,4 @@ -app = Rails.application +app = Gitlab::Application if app.config.serve_static_assets # The `ActionDispatch::Static` middleware intercepts requests for static files diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index f3db5b7476e..d8bf0878a3d 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -32,10 +32,11 @@ en: send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' updated: 'Your password was changed successfully. You are now signed in.' updated_not_active: 'Your password was changed successfully.' - send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail" + send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." + no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." confirmations: send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' - send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.' + send_paranoid_instructions: 'If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes.' confirmed: 'Your account was successfully confirmed. You are now signed in.' registrations: signed_up: 'Welcome! You have signed up successfully.' @@ -57,4 +58,4 @@ en: reset_password_instructions: subject: 'Reset password instructions' unlock_instructions: - subject: 'Unlock Instructions' \ No newline at end of file + subject: 'Unlock Instructions' diff --git a/config/routes.rb b/config/routes.rb index d7307a61ede..74544d63d86 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,105 @@ require 'sidekiq/web' require 'api/api' Gitlab::Application.routes.draw do + namespace :ci do + # CI API + Ci::API::API.logger Rails.logger + mount Ci::API::API => '/api' + + resource :lint, only: [:show, :create] + + resource :help do + get :oauth2 + end + + resources :projects do + collection do + post :add + get :gitlab + end + + member do + get :status, to: 'projects#badge' + get :integration + post :build + post :toggle_shared_runners + get :dumped_yaml + end + + resources :services, only: [:index, :edit, :update] do + member do + get :test + end + end + + resource :charts, only: [:show] + + resources :refs, constraints: { ref_id: /.*/ }, only: [] do + resources :commits, only: [:show] do + member do + get :status + get :cancel + end + end + end + + resources :builds, only: [:show] do + member do + get :cancel + get :status + post :retry + end + end + + resources :web_hooks, only: [:index, :create, :destroy] do + member do + get :test + end + end + + resources :triggers, only: [:index, :create, :destroy] + + resources :runners, only: [:index, :edit, :update, :destroy, :show] do + member do + get :resume + get :pause + end + end + + resources :runner_projects, only: [:create, :destroy] + + resources :events, only: [:index] + resource :variables, only: [:show, :update] + end + + resource :user_sessions do + get :auth + get :callback + end + + namespace :admin do + resources :runners, only: [:index, :show, :update, :destroy] do + member do + put :assign_all + get :resume + get :pause + end + end + + resources :events, only: [:index] + + resources :projects do + resources :runner_projects + end + + resources :builds, only: :index + + resource :application_settings, only: [:show, :update] + end + + root to: 'projects#index' + end + use_doorkeeper do controllers applications: 'oauth/applications', authorized_applications: 'oauth/authorized_applications', diff --git a/config/schedule.rb b/config/schedule.rb new file mode 100644 index 00000000000..8122f7cc69c --- /dev/null +++ b/config/schedule.rb @@ -0,0 +1,8 @@ +# Use this file to easily define all of your cron jobs. +# +# If you make changes to this file, please create also an issue on +# https://gitlab.com/gitlab-org/omnibus-gitlab/issues . This is necessary +# because the omnibus packages manage cron jobs using Chef instead of Whenever. +every 1.hour do + rake "ci:schedule_builds" +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 00000000000..f63c74d0688 --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,3 @@ +--- +development: + db_key_base: 53ab5c413f37a5a87df3c7e55dc49924793c44b9a40834af258f75ce3cc71067478b7c1f999bf22d9cfb9e6dedffda989dc462684f8c869705f735a92b7230ed diff --git a/config/secrets.yml.example b/config/secrets.yml.example new file mode 100644 index 00000000000..6b408ac6031 --- /dev/null +++ b/config/secrets.yml.example @@ -0,0 +1,12 @@ +production: + # db_key_base is used to encrypt for Variables. Ensure that you don't lose it. + # If you change or lose this key you will be unable to access variables stored in database. + # Make sure the secret is at least 30 characters and all random, + # no regular words or you'll be exposed to dictionary attacks. + # db_key_base: + +development: + db_key_base: development + +test: + db_key_base: test diff --git a/config/sidekiq.yml.example b/config/sidekiq.yml.example new file mode 100644 index 00000000000..c691db67c6c --- /dev/null +++ b/config/sidekiq.yml.example @@ -0,0 +1,2 @@ +-- +:concurrency: 5 \ No newline at end of file diff --git a/db/ci/migrate/20121004140911_create_projects.rb b/db/ci/migrate/20121004140911_create_projects.rb new file mode 100644 index 00000000000..a9fee3aa6c8 --- /dev/null +++ b/db/ci/migrate/20121004140911_create_projects.rb @@ -0,0 +1,14 @@ +class CreateProjects < ActiveRecord::Migration + def up + create_table :projects do |t| + t.string :name, null: false + t.string :path, null: false + t.integer :timeout, null: false, default: 1800 + t.text :scripts, null: false + t.timestamps + end + end + + def down + end +end diff --git a/db/ci/migrate/20121004165038_create_builds.rb b/db/ci/migrate/20121004165038_create_builds.rb new file mode 100644 index 00000000000..547803489fb --- /dev/null +++ b/db/ci/migrate/20121004165038_create_builds.rb @@ -0,0 +1,15 @@ +class CreateBuilds < ActiveRecord::Migration + def up + create_table :builds do |t| + t.integer :project_id + t.string :commit_ref + t.string :status + t.datetime :finished_at + t.text :trace + t.timestamps + end + end + + def down + end +end diff --git a/db/ci/migrate/20121101091638_devise_create_users.rb b/db/ci/migrate/20121101091638_devise_create_users.rb new file mode 100644 index 00000000000..2099d998fa4 --- /dev/null +++ b/db/ci/migrate/20121101091638_devise_create_users.rb @@ -0,0 +1,46 @@ +class DeviseCreateUsers < ActiveRecord::Migration + def change + create_table(:users) do |t| + ## Database authenticatable + t.string :email, :null => false, :default => "" + t.string :encrypted_password, :null => false, :default => "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, :default => 0 + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + ## Token authenticatable + # t.string :authentication_token + + + t.timestamps + end + + add_index :users, :email, :unique => true + add_index :users, :reset_password_token, :unique => true + # add_index :users, :confirmation_token, :unique => true + # add_index :users, :unlock_token, :unique => true + # add_index :users, :authentication_token, :unique => true + end +end diff --git a/db/ci/migrate/20121101121639_add_token_to_project.rb b/db/ci/migrate/20121101121639_add_token_to_project.rb new file mode 100644 index 00000000000..bb66677b6b1 --- /dev/null +++ b/db/ci/migrate/20121101121639_add_token_to_project.rb @@ -0,0 +1,5 @@ +class AddTokenToProject < ActiveRecord::Migration + def change + add_column :projects, :token, :string, null: true + end +end diff --git a/db/ci/migrate/20121106143042_add_ref_functionality.rb b/db/ci/migrate/20121106143042_add_ref_functionality.rb new file mode 100644 index 00000000000..0c26571e305 --- /dev/null +++ b/db/ci/migrate/20121106143042_add_ref_functionality.rb @@ -0,0 +1,10 @@ +class AddRefFunctionality < ActiveRecord::Migration + def change + rename_column :builds, :commit_ref, :ref + add_column :builds, :sha, :string + add_column :projects, :default_ref, :string + end + + def down + end +end diff --git a/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb b/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb new file mode 100644 index 00000000000..8a4e8fd666f --- /dev/null +++ b/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb @@ -0,0 +1,5 @@ +class AddGitlabUrlToProject < ActiveRecord::Migration + def change + add_column :projects, :gitlab_url, :string, null: true + end +end diff --git a/db/ci/migrate/20121108174237_add_started_at_to_build.rb b/db/ci/migrate/20121108174237_add_started_at_to_build.rb new file mode 100644 index 00000000000..b4d65c75004 --- /dev/null +++ b/db/ci/migrate/20121108174237_add_started_at_to_build.rb @@ -0,0 +1,5 @@ +class AddStartedAtToBuild < ActiveRecord::Migration + def change + add_column :builds, :started_at, :datetime, null: true + end +end diff --git a/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb b/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb new file mode 100644 index 00000000000..5853f440f59 --- /dev/null +++ b/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb @@ -0,0 +1,8 @@ +class IncreateTraceColunmLimit < ActiveRecord::Migration + def up + change_column :builds, :trace, :text, :limit => 1073741823 + end + + def down + end +end diff --git a/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb b/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb new file mode 100644 index 00000000000..a9a4e36b5ba --- /dev/null +++ b/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb @@ -0,0 +1,5 @@ +class AddTmpFileToBuild < ActiveRecord::Migration + def change + add_column :builds, :tmp_file, :string + end +end diff --git a/db/ci/migrate/20121116144312_add_before_sha_to_build.rb b/db/ci/migrate/20121116144312_add_before_sha_to_build.rb new file mode 100644 index 00000000000..7b8cfd93caa --- /dev/null +++ b/db/ci/migrate/20121116144312_add_before_sha_to_build.rb @@ -0,0 +1,5 @@ +class AddBeforeShaToBuild < ActiveRecord::Migration + def change + add_column :builds, :before_sha, :string, null: true + end +end diff --git a/db/ci/migrate/20121224092350_add_schedule_to_projects.rb b/db/ci/migrate/20121224092350_add_schedule_to_projects.rb new file mode 100644 index 00000000000..fb3155f1159 --- /dev/null +++ b/db/ci/migrate/20121224092350_add_schedule_to_projects.rb @@ -0,0 +1,6 @@ +class AddScheduleToProjects < ActiveRecord::Migration + def change + add_column :projects, :always_build, :boolean, default: false, null: false + add_column :projects, :polling_interval, :string, null: true + end +end diff --git a/db/ci/migrate/20130114153451_change_schedule_invertal.rb b/db/ci/migrate/20130114153451_change_schedule_invertal.rb new file mode 100644 index 00000000000..accf3eef473 --- /dev/null +++ b/db/ci/migrate/20130114153451_change_schedule_invertal.rb @@ -0,0 +1,25 @@ +class ChangeScheduleInvertal < ActiveRecord::Migration + def up + if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' + connection.execute(%q{ + ALTER TABLE projects + ALTER COLUMN polling_interval + TYPE integer USING CAST(polling_interval AS integer) + }) + else + change_column :projects, :polling_interval, :integer, null: true + end + end + + def down + if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' + connection.execute(%q{ + ALTER TABLE projects + ALTER COLUMN polling_interval + TYPE integer USING CAST(polling_interval AS varchar) + }) + else + change_column :projects, :polling_interval, :string, null: true + end + end +end diff --git a/db/ci/migrate/20130129121754_add_public_flag_to_project.rb b/db/ci/migrate/20130129121754_add_public_flag_to_project.rb new file mode 100644 index 00000000000..2bfe52f0df4 --- /dev/null +++ b/db/ci/migrate/20130129121754_add_public_flag_to_project.rb @@ -0,0 +1,5 @@ +class AddPublicFlagToProject < ActiveRecord::Migration + def change + add_column :projects, :public, :boolean, null: false, default: false + end +end diff --git a/db/ci/migrate/20130531112551_add_data_field_to_build.rb b/db/ci/migrate/20130531112551_add_data_field_to_build.rb new file mode 100644 index 00000000000..ff897bce448 --- /dev/null +++ b/db/ci/migrate/20130531112551_add_data_field_to_build.rb @@ -0,0 +1,5 @@ +class AddDataFieldToBuild < ActiveRecord::Migration + def change + add_column :builds, :push_data, :text + end +end diff --git a/db/ci/migrate/20130531122131_remove_path_field_from_project.rb b/db/ci/migrate/20130531122131_remove_path_field_from_project.rb new file mode 100644 index 00000000000..684c16470a4 --- /dev/null +++ b/db/ci/migrate/20130531122131_remove_path_field_from_project.rb @@ -0,0 +1,8 @@ +class RemovePathFieldFromProject < ActiveRecord::Migration + def up + remove_column :projects, :path + end + + def down + end +end diff --git a/db/ci/migrate/20130531125905_create_runners.rb b/db/ci/migrate/20130531125905_create_runners.rb new file mode 100644 index 00000000000..2619394f51b --- /dev/null +++ b/db/ci/migrate/20130531125905_create_runners.rb @@ -0,0 +1,10 @@ +class CreateRunners < ActiveRecord::Migration + def change + create_table :runners do |t| + t.string :token + t.text :public_key + + t.timestamps + end + end +end diff --git a/db/ci/migrate/20130531133603_add_runner_id_to_build.rb b/db/ci/migrate/20130531133603_add_runner_id_to_build.rb new file mode 100644 index 00000000000..bccc0970835 --- /dev/null +++ b/db/ci/migrate/20130531133603_add_runner_id_to_build.rb @@ -0,0 +1,5 @@ +class AddRunnerIdToBuild < ActiveRecord::Migration + def change + add_column :builds, :runner_id, :integer + end +end diff --git a/db/ci/migrate/20130603130920_remove_users_table.rb b/db/ci/migrate/20130603130920_remove_users_table.rb new file mode 100644 index 00000000000..6948ef265ef --- /dev/null +++ b/db/ci/migrate/20130603130920_remove_users_table.rb @@ -0,0 +1,5 @@ +class RemoveUsersTable < ActiveRecord::Migration + def up + drop_table :users + end +end diff --git a/db/ci/migrate/20130603144030_add_more_fields_to_project.rb b/db/ci/migrate/20130603144030_add_more_fields_to_project.rb new file mode 100644 index 00000000000..0897682285a --- /dev/null +++ b/db/ci/migrate/20130603144030_add_more_fields_to_project.rb @@ -0,0 +1,5 @@ +class AddMoreFieldsToProject < ActiveRecord::Migration + def change + add_column :projects, :ssh_url_to_repo, :string + end +end diff --git a/db/ci/migrate/20130603144959_create_runner_projects.rb b/db/ci/migrate/20130603144959_create_runner_projects.rb new file mode 100644 index 00000000000..c65c8a51bcf --- /dev/null +++ b/db/ci/migrate/20130603144959_create_runner_projects.rb @@ -0,0 +1,10 @@ +class CreateRunnerProjects < ActiveRecord::Migration + def change + create_table :runner_projects do |t| + t.integer :runner_id, null: false + t.integer :project_id, null: false + + t.timestamps + end + end +end diff --git a/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb b/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb new file mode 100644 index 00000000000..3efdbb7af1c --- /dev/null +++ b/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb @@ -0,0 +1,5 @@ +class AddProjectGitlabIdToProject < ActiveRecord::Migration + def change + add_column :projects, :gitlab_id, :integer + end +end diff --git a/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb b/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb new file mode 100644 index 00000000000..5f968b06b5d --- /dev/null +++ b/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb @@ -0,0 +1,5 @@ +class AddIndexProjectIdToBuilds < ActiveRecord::Migration + def change + add_index :builds, :project_id + end +end diff --git a/db/ci/migrate/20130705171042_add_description_to_runner.rb b/db/ci/migrate/20130705171042_add_description_to_runner.rb new file mode 100644 index 00000000000..1e04e98d109 --- /dev/null +++ b/db/ci/migrate/20130705171042_add_description_to_runner.rb @@ -0,0 +1,5 @@ +class AddDescriptionToRunner < ActiveRecord::Migration + def change + add_column :runners, :description, :string + end +end diff --git a/db/ci/migrate/20130710164015_add_db_index.rb b/db/ci/migrate/20130710164015_add_db_index.rb new file mode 100644 index 00000000000..4907fae888b --- /dev/null +++ b/db/ci/migrate/20130710164015_add_db_index.rb @@ -0,0 +1,7 @@ +class AddDbIndex < ActiveRecord::Migration + def change + add_index :builds, :runner_id + add_index :runner_projects, :runner_id + add_index :runner_projects, :project_id + end +end diff --git a/db/ci/migrate/20130816201200_change_push_data_limit.rb b/db/ci/migrate/20130816201200_change_push_data_limit.rb new file mode 100644 index 00000000000..29bd45c2cf9 --- /dev/null +++ b/db/ci/migrate/20130816201200_change_push_data_limit.rb @@ -0,0 +1,5 @@ +class ChangePushDataLimit < ActiveRecord::Migration + def change + change_column :builds, :push_data, :text, :limit => 16777215 + end +end diff --git a/db/ci/migrate/20130906175737_add_sessions_table.rb b/db/ci/migrate/20130906175737_add_sessions_table.rb new file mode 100644 index 00000000000..4c879564a58 --- /dev/null +++ b/db/ci/migrate/20130906175737_add_sessions_table.rb @@ -0,0 +1,12 @@ +class AddSessionsTable < ActiveRecord::Migration + def change + create_table :sessions do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :sessions, :session_id + add_index :sessions, :updated_at + end +end diff --git a/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb b/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb new file mode 100644 index 00000000000..900ea913728 --- /dev/null +++ b/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb @@ -0,0 +1,5 @@ +class AddAllowGitFetchToProject < ActiveRecord::Migration + def change + add_column :projects, :allow_git_fetch, :boolean, default: true, null: false + end +end diff --git a/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb b/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb new file mode 100644 index 00000000000..e0f4943d40f --- /dev/null +++ b/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb @@ -0,0 +1,7 @@ +class AddEmailNotificationFieldsToProject < ActiveRecord::Migration + def change + add_column :projects, :email_recipients, :string, default: '', null: false + add_column :projects, :email_add_committer, :boolean, default: true, null: false + add_column :projects, :email_all_broken_builds, :boolean, default: true, null: false + end +end diff --git a/db/ci/migrate/20140130121538_rename_project_fields.rb b/db/ci/migrate/20140130121538_rename_project_fields.rb new file mode 100644 index 00000000000..3d7d3e8167e --- /dev/null +++ b/db/ci/migrate/20140130121538_rename_project_fields.rb @@ -0,0 +1,5 @@ +class RenameProjectFields < ActiveRecord::Migration + def change + rename_column :projects, :email_all_broken_builds, :email_only_broken_builds + end +end diff --git a/db/ci/migrate/20140222210357_create_web_hook.rb b/db/ci/migrate/20140222210357_create_web_hook.rb new file mode 100644 index 00000000000..743ad816906 --- /dev/null +++ b/db/ci/migrate/20140222210357_create_web_hook.rb @@ -0,0 +1,9 @@ +class CreateWebHook < ActiveRecord::Migration + def change + create_table :web_hooks do |t| + t.string :url, null: false + t.integer :project_id, null: false + t.timestamps + end + end +end diff --git a/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb b/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb new file mode 100644 index 00000000000..3bf9f036ae8 --- /dev/null +++ b/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb @@ -0,0 +1,5 @@ +class RemovePublicKeyFromRunner < ActiveRecord::Migration + def change + remove_column :runners, :public_key + end +end diff --git a/db/ci/migrate/20140823225019_create_commits_from_builds.rb b/db/ci/migrate/20140823225019_create_commits_from_builds.rb new file mode 100644 index 00000000000..15f84b11511 --- /dev/null +++ b/db/ci/migrate/20140823225019_create_commits_from_builds.rb @@ -0,0 +1,22 @@ +class CreateCommitsFromBuilds < ActiveRecord::Migration + def change + create_table :commits do |t| + t.integer :project_id + t.string :ref, nil: false + t.string :sha, nil: false + t.string :before_sha, nil: false + t.text :push_data, nil: false + + t.timestamps + end + + add_column :builds, :commit_id, :integer + + # Remove commit data from builds + #remove_column :builds, :project_id, :integer + #remove_column :builds, :ref, :string + #remove_column :builds, :sha, :string + #remove_column :builds, :before_sha, :string + #remove_column :builds, :push_data, :text + end +end diff --git a/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb b/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb new file mode 100644 index 00000000000..2d7b1a223e2 --- /dev/null +++ b/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb @@ -0,0 +1,5 @@ +class AddSkipRefsToProjects < ActiveRecord::Migration + def change + add_column :projects, :skip_refs, :string + end +end diff --git a/db/ci/migrate/20141001125939_add_coverage_parser.rb b/db/ci/migrate/20141001125939_add_coverage_parser.rb new file mode 100644 index 00000000000..7ea7d6047a9 --- /dev/null +++ b/db/ci/migrate/20141001125939_add_coverage_parser.rb @@ -0,0 +1,5 @@ +class AddCoverageParser < ActiveRecord::Migration + def change + add_column :projects, :coverage_regex, :string + end +end diff --git a/db/ci/migrate/20141001132129_add_coverage_to_build.rb b/db/ci/migrate/20141001132129_add_coverage_to_build.rb new file mode 100644 index 00000000000..442a3dd28c0 --- /dev/null +++ b/db/ci/migrate/20141001132129_add_coverage_to_build.rb @@ -0,0 +1,5 @@ +class AddCoverageToBuild < ActiveRecord::Migration + def change + add_column :builds, :coverage, :float + end +end diff --git a/db/ci/migrate/20141028162820_add_sha_index_to_build.rb b/db/ci/migrate/20141028162820_add_sha_index_to_build.rb new file mode 100644 index 00000000000..bd2a4de5657 --- /dev/null +++ b/db/ci/migrate/20141028162820_add_sha_index_to_build.rb @@ -0,0 +1,6 @@ +class AddShaIndexToBuild < ActiveRecord::Migration + def change + add_index :builds, :sha + add_index :builds, [:project_id, :sha] + end +end diff --git a/db/ci/migrate/20141031114419_migrate_build_to_commits.rb b/db/ci/migrate/20141031114419_migrate_build_to_commits.rb new file mode 100644 index 00000000000..dc90ec6d15e --- /dev/null +++ b/db/ci/migrate/20141031114419_migrate_build_to_commits.rb @@ -0,0 +1,21 @@ +class MigrateBuildToCommits < ActiveRecord::Migration + def change + execute < Variables > Add Variable`. +**This feature requires `gitlab-runner` with version equal or greater than 0.4.0.** +The variables that are defined in the project settings are send along with the build script to the runner. +The secure variables are stored out of the repository. Never store secrets in your projects' .gitlab-ci.yml. +It is also important that secret's value is hidden in the build log. + +You access added variable by prefixing it's name with `$` (on non-Windows runners) or `%` (for Windows Batch runners): +1. `$SECRET_VARIABLE` - use it for non-Windows runners +2. `%SECRET_VARIABLE%` - use it for Windows Batch runners diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md new file mode 100644 index 00000000000..84eaf29efd1 --- /dev/null +++ b/doc/ci/docker/README.md @@ -0,0 +1,4 @@ +# Docker integration + ++ [Using Docker Images](using_docker_images.md) ++ [Using Docker Build](using_docker_build.md) \ No newline at end of file diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md new file mode 100644 index 00000000000..702a6c6b587 --- /dev/null +++ b/doc/ci/docker/using_docker_build.md @@ -0,0 +1,112 @@ +# Using Docker Build + +GitLab CI can allows you to use Docker Engine to build and test docker-based projects. + +**This also allows to you to use `docker-compose` and other docker-enabled tools.** + +This is one of new trends in Continuous Integration/Deployment to: + +1. create application image, +1. run test against created image, +1. push image to remote registry, +1. deploy server from pushed image + +It's also useful in case when your application already has the `Dockerfile` that can be used to create and test image: +```bash +$ docker build -t my-image dockerfiles/ +$ docker run my-docker-image /script/to/run/tests +$ docker tag my-image my-registry:5000/my-image +$ docker push my-registry:5000/my-image +``` + +However, this requires special configuration of GitLab Runner to enable `docker` support during build. +**This requires running GitLab Runner in privileged mode which can be harmful when untrusted code is run.** + +There are two methods to enable the use of `docker build` and `docker run` during build. + +## 1. Use shell executor + +The simplest approach is to install GitLab Runner in `shell` execution mode. +GitLab Runner then executes build scripts as `gitlab-runner` user. + +1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation). + +1. During GitLab Runner installation select `shell` as method of executing build scripts or use command: + + ```bash + $ sudo gitlab-runner register -n \ + --url http://ci.gitlab.com \ + --token RUNNER_TOKEN \ + --executor shell + --description "My Runner" + ``` + +2. Install Docker on server. + + For more information how to install Docker on different systems checkout the [Supported installations](https://docs.docker.com/installation/). + +3. Add `gitlab-runner` user to `docker` group: + + ```bash + $ sudo usermod -aG docker gitlab-runner + ``` + +4. Verify that `gitlab-runner` has access to Docker: + + ```bash + $ sudo -u gitlab-runner -H docker info + ``` + + You can now verify that everything works by adding `docker info` to `.gitlab-ci.yml`: + ```yaml + before_script: + - docker info + + build_image: + script: + - docker build -t my-docker-image . + - docker run my-docker-image /script/to/run/tests + ``` + +5. You can now use `docker` command and install `docker-compose` if needed. + +6. However, by adding `gitlab-runner` to `docker` group you are effectively granting `gitlab-runner` full root permissions. +For more information please checkout [On Docker security: `docker` group considered harmful](https://www.andreas-jung.com/contents/on-docker-security-docker-group-considered-harmful). + +## 2. Use docker-in-docker executor + +Second approach is to use special Docker image with all tools installed (`docker` and `docker-compose`) and run build script in context of that image in privileged mode. +In order to do that follow the steps: + +1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation). + +1. Register GitLab Runner from command line to use `docker` and `privileged` mode: + + ```bash + $ sudo gitlab-runner register -n \ + --url http://ci.gitlab.com \ + --token RUNNER_TOKEN \ + --executor docker \ + --description "My Docker Runner" \ + --docker-image "gitlab/dind:latest" \ + --docker-privileged + ``` + + The above command will register new Runner to use special [gitlab/dind](https://registry.hub.docker.com/u/gitlab/dind/) image which is provided by GitLab Inc. + The image at the start runs Docker daemon in [docker-in-docker](https://blog.docker.com/2013/09/docker-can-now-run-within-docker/) mode. + +1. You can now use `docker` from build script: + + ```yaml + before_script: + - docker info + + build_image: + script: + - docker build -t my-docker-image . + - docker run my-docker-image /script/to/run/tests + ``` + +1. However, by enabling `--docker-privileged` you are effectively disables all security mechanisms of containers and exposing your host to privilege escalation which can lead to container breakout. +For more information you could be interested in checking out [Runtime privilege](https://docs.docker.com/reference/run/#runtime-privilege-linux-capabilities-and-lxc-configuration). + diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md new file mode 100644 index 00000000000..ef449cd45bc --- /dev/null +++ b/doc/ci/docker/using_docker_images.md @@ -0,0 +1,203 @@ +# Using Docker Images +GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects. + +Docker is an open-source project that allows to use predefined images to run applications +in independent "containers" that are run within a single Linux instance. +[Docker Hub](https://registry.hub.docker.com/) have rich database of built images that can be used to build applications. + +Docker when used with GitLab CI runs each build in separate and isolated container using predefined image and always from scratch. +It makes it easier to have simple and reproducible build environment that can also be run on your workstation. +This allows you to test all commands from your shell, rather than having to test them on a CI server. + +### Register Docker runner +To use GitLab Runner with Docker you need to register new runner to use `docker` executor: + +```bash +gitlab-ci-multi-runner register \ + --url "https://ci.gitlab.com/" \ + --registration-token "PROJECT_REGISTRATION_TOKEN" \ + --description "docker-ruby-2.1" \ + --executor "docker" \ + --docker-image ruby:2.1 \ + --docker-postgres latest \ + --docker-mysql latest +``` + +**The registered runner will use `ruby:2.1` image and will run two services (`postgres:latest` and `mysql:latest`) that will be accessible for time of the build.** + +### What is image? +The image is the name of any repository that is present in local Docker Engine or any repository that can be found at [Docker Hub](https://registry.hub.docker.com/). +For more information about the image and Docker Hub please read the [Docker Fundamentals](https://docs.docker.com/introduction/understanding-docker/). + +### What is service? +Service is just another image that is run for time of your build and is linked to your build. This allows you to access the service image during build time. +The service image can run any application, but most common use case is to run some database container, ie.: `mysql`. +It's easier and faster to use existing image, run it as additional container than install `mysql` every time project is built. + +#### How is service linked to the build? +There's good document that describes how Docker linking works: [Linking containers together](https://docs.docker.com/userguide/dockerlinks/). +To summarize: if you add `mysql` as service to your application, the image will be used to create container that is linked to build container. +The service container for MySQL will be accessible under hostname `mysql`. +So, **to access your database service you have to connect to host: `mysql` instead of socket or `localhost`**. + +### How to use other images as services? +You are not limited to have only database services. +You can hand modify `config.toml` to add any image as service found at [Docker Hub](https://registry.hub.docker.com/). +Look for `[runners.docker]` section: +``` +[runners.docker] + image = "ruby:2.1" + services = ["mysql:latest", "postgres:latest"] +``` + +For example you need `wordpress` instance to test some API integration with `Wordpress`. +You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/). +This is image that have fully preconfigured `wordpress` and have `MySQL` server built-in: +``` +[runners.docker] + image = "ruby:2.1" + services = ["mysql:latest", "postgres:latest", "tutum/wordpress:latest"] +``` + +Next time when you run your application the `tutum/wordpress` will be started +and you will have access to it from your build container under hostname: `tutum_wordpress`. + +Alias hostname for the service is made from the image name: +1. Everything after `:` is stripped, +2. '/' is replaced to `_`. + +### Configuring services +Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment. + +GitLab Runner 0.5.0 and up passes all YAML-defined variables to created service containers. + +1. To configure database name for [postgres](https://registry.hub.docker.com/u/library/postgres/) service, +you need to set POSTGRES_DB. + + ```yaml + services: + - postgres + + variables: + POSTGRES_DB: gitlab + ``` + +1. To use [mysql](https://registry.hub.docker.com/u/library/mysql/) service with empty password for time of build, +you need to set MYSQL_ALLOW_EMPTY_PASSWORD. + + ```yaml + services: + - mysql + + variables: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + ``` + +For other possible configuration variables check the +https://registry.hub.docker.com/u/library/mysql/ or https://registry.hub.docker.com/u/library/postgres/ +or README page for any other Docker image. + +**Note: All variables will passed to all service containers. It's not designed to distinguish which variable should go where.** + +### Overwrite image and services +It's possible to overwrite `docker-image` and specify services from `.gitlab-ci.yml`. +If you add to your YAML the `image` and the `services` these parameters +be used instead of the ones that were specified during runner's registration. +``` +image: ruby:2.2 +services: + - postgres:9.3 +before_install: + - bundle install + +test: + script: + - bundle exec rake spec +``` + +It's possible to define image and service per-job: +``` +before_install: + - bundle install + +test:2.1: + image: ruby:2.1 + services: + - postgres:9.3 + script: + - bundle exec rake spec + +test:2.2: + image: ruby:2.2 + services: + - postgres:9.4 + script: + - bundle exec rake spec +``` + +#### How to enable overwriting? +To enable overwriting you have to **enable it first** (it's disabled by default for security reasons). +You can do that by hand modifying runner configuration: `config.toml`. +Please go to section where is `[runners.docker]` definition for your runner. +Add `allowed_images` and `allowed_services` to specify what images are allowed to be picked from `.gitlab-ci.yml`: +``` +[runners.docker] + image = "ruby:2.1" + allowed_images = ["ruby:*", "python:*"] + allowed_services = ["mysql:*", "redis:*"] +``` +This enables you to use in your `.gitlab-ci.yml` any image that matches above wildcards. +You will be able to pick only `ruby` and `python` images. +The same rule can be applied to limit services. + +If you are courageous enough, you can make it fully open and accept everything: +``` +[runners.docker] + image = "ruby:2.1" + allowed_images = ["*", "*/*"] + allowed_services = ["*", "*/*"] +``` + +**It the feature is not enabled, or image isn't allowed the error message will be put into the build log.** + +### How Docker integration works +1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`. +1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example). +1. Create build container and link any service container to build container. +1. Start build container and send build script to the container. +1. Run build script. +1. Checkout code in: `/builds/group-name/project-name/`. +1. Run any step defined in `.gitlab-ci.yml`. +1. Check exit status of build script. +1. Remove build container and all created service containers. + +### How to debug a build locally +1. Create a file with build script: +```bash +$ cat < build_script +git clone https://gitlab.com/gitlab-org/gitlab-ci-multi-runner.git /builds/gitlab-org/gitlab-ci-multi-runner +cd /builds/gitlab-org/gitlab-ci-multi-runner +make <- or any other build step +EOF +``` + +1. Create service containers: +``` +$ docker run -d -n service-mysql mysql:latest +$ docker run -d -n service-postgres postgres:latest +``` +This will create two service containers (MySQL and PostgreSQL). + +1. Create a build container and execute script in its context: +``` +$ cat build_script | docker run -n build -i -l mysql:service-mysql -l postgres:service-postgres ruby:2.1 /bin/bash +``` +This will create build container that has two service containers linked. +The build_script is piped using STDIN to bash interpreter which executes the build script in container. + +1. At the end remove all containers: +``` +docker rm -f -v build service-mysql service-postgres +``` +This will forcefully (the `-f` switch) remove build container and service containers +and all volumes (the `-v` switch) that were created with the container creation. diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md new file mode 100644 index 00000000000..e0b9fa0e25d --- /dev/null +++ b/doc/ci/examples/README.md @@ -0,0 +1,5 @@ +# Build script examples + ++ [Test and deploy Ruby Application to Heroku](test-and-deploy-ruby-application-to-heroku.md) ++ [Test and deploy Python Application to Heroku](test-and-deploy-python-application-to-heroku.md) ++ [Test Clojure applications](examples/test-clojure-application.md) diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md new file mode 100644 index 00000000000..859adf5f465 --- /dev/null +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -0,0 +1,72 @@ +## Test and Deploy a python application +This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application. + +You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://ci.gitlab.com/projects/4080). + +### Configure project +This is what the `.gitlab-ci.yml` file looks like for this project: +```yaml +test: + script: + # this configures django application to use attached postgres database that is run on `postgres` host + - export DATABASE_URL=postgres://postgres:@postgres:5432/python-test-app + - apt-get update -qy + - apt-get install -y python-dev python-pip + - pip install -r requirements.txt + - python manage.py test + +staging: + type: deploy + script: + - apt-get update -qy + - apt-get install -y ruby-dev + - gem install dpl + - dpl --provider=heroku --app=gitlab-ci-python-test-staging --api-key=$HEROKU_STAGING_API_KEY + only: + - master + +production: + type: deploy + script: + - apt-get update -qy + - apt-get install -y ruby-dev + - gem install dpl + - dpl --provider=heroku --app=gitlab-ci-python-test-prod --api-key=$HEROKU_PRODUCTION_API_KEY + only: + - tags +``` + +This project has three jobs: +1. `test` - used to test rails application, +2. `staging` - used to automatically deploy staging environment every push to `master` branch +3. `production` - used to automatically deploy production environmnet for every created tag + +### Store API keys +You'll need to create two variables in `Project > Variables`: +1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app, +2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app. + +Find your Heroku API key in [Manage Account](https://dashboard.heroku.com/account). + +### Create Heroku application +For each of your environments, you'll need to create a new Heroku application. +You can do this through the [Dashboard](https://dashboard.heroku.com/). + +### Create runner +First install [Docker Engine](https://docs.docker.com/installation/). +To build this project you also need to have [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner). +You can use public runners available on `ci.gitlab.com`, but you can register your own: +``` +gitlab-ci-multi-runner register \ + --non-interactive \ + --url "https://ci.gitlab.com/" \ + --registration-token "PROJECT_REGISTRATION_TOKEN" \ + --description "python-3.2" \ + --executor "docker" \ + --docker-image python:3.2 \ + --docker-postgres latest +``` + +With the command above, you create a runner that uses [python:3.2](https://registry.hub.docker.com/u/library/python/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. + +To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md new file mode 100644 index 00000000000..a1265ae8833 --- /dev/null +++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md @@ -0,0 +1,67 @@ +## Test and Deploy a ruby application +This example will guide you how to run tests in your Ruby application and deploy it automatiacally as Heroku application. + +You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://ci.gitlab.com/projects/4050). + +### Configure project +This is what the `.gitlab-ci.yml` file looks like for this project: +```yaml +test: + script: + - apt-get update -qy + - apt-get install -y nodejs + - bundle install --path /cache + - bundle exec rake db:create RAILS_ENV=test + - bundle exec rake test + +staging: + type: deploy + script: + - gem install dpl + - dpl --provider=heroku --app=gitlab-ci-ruby-test-staging --api-key=$HEROKU_STAGING_API_KEY + only: + - master + +production: + type: deploy + script: + - gem install dpl + - dpl --provider=heroku --app=gitlab-ci-ruby-test-prod --api-key=$HEROKU_PRODUCTION_API_KEY + only: + - tags +``` + +This project has three jobs: +1. `test` - used to test rails application, +2. `staging` - used to automatically deploy staging environment every push to `master` branch +3. `production` - used to automatically deploy production environmnet for every created tag + +### Store API keys +You'll need to create two variables in `Project > Variables`: +1. `HEROKU_STAGING_API_KEY` - Heroku API key used to deploy staging app, +2. `HEROKU_PRODUCTION_API_KEY` - Heroku API key used to deploy production app. + +Find your Heroku API key in [Manage Account](https://dashboard.heroku.com/account). + +### Create Heroku application +For each of your environments, you'll need to create a new Heroku application. +You can do this through the [Dashboard](https://dashboard.heroku.com/). + +### Create runner +First install [Docker Engine](https://docs.docker.com/installation/). +To build this project you also need to have [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner). +You can use public runners available on `ci.gitlab.com`, but you can register your own: +``` +gitlab-ci-multi-runner register \ + --non-interactive \ + --url "https://ci.gitlab.com/" \ + --registration-token "PROJECT_REGISTRATION_TOKEN" \ + --description "ruby-2.1" \ + --executor "docker" \ + --docker-image ruby:2.1 \ + --docker-postgres latest +``` + +With the command above, you create a runner that uses [ruby:2.1](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. + +To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. \ No newline at end of file diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md new file mode 100644 index 00000000000..6c6faf8f928 --- /dev/null +++ b/doc/ci/examples/test-clojure-application.md @@ -0,0 +1,35 @@ +## Test Clojure applications + +This example will guide you how to run tests in your Clojure application. + +You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://ci.gitlab.com/projects/6306). + +### Configure project + +This is what the `.gitlab-ci.yml` file looks like for this project: + +```yaml +variables: + POSTGRES_DB: sample-test + DATABASE_URL: "postgresql://postgres@postgres:5432/sample-test" + +before_script: + - apt-get update -y + - apt-get install default-jre postgresql-client -y + - wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein + - chmod a+x lein + - export LEIN_ROOT=1 + - PATH=$PATH:. + - lein deps + - lein migratus migrate + +test: + script: + - lein test +``` + +In before script we install JRE and [Leiningen](http://leiningen.org/). +Sample project uses [migratus](https://github.com/yogthos/migratus) library to manage database migrations. +So we added database migration as last step of `before_script` section + +You can use public runners available on `ci.gitlab.com` for testing your application with such configuration. diff --git a/doc/ci/install/README.md b/doc/ci/install/README.md new file mode 100644 index 00000000000..8cbc858458c --- /dev/null +++ b/doc/ci/install/README.md @@ -0,0 +1,276 @@ +# Select Version to Install +Make sure you view this installation guide from the branch (version) of GitLab CI you would like to install. In most cases +this should be the highest numbered stable branch (example shown below). + +![capture](http://i.imgur.com/fmdlXxa.png) + +If this is unclear check the [GitLab Blog](http://blog.gitlab.org/) for installation guide links by version. + +## GitLab CI 7.12 requires GitLab 7.12 or newer + +other [requirements](requirements.md) + +# Setup: + +## 1. Packages / Dependencies + +`sudo` is not installed on Debian by default. Make sure your system is +up-to-date and install it. + + sudo apt-get update + sudo apt-get upgrade + +**Note:** +During this installation some files will need to be edited manually. If +you are familiar with vim set it as default editor with the commands +below. If you are not familiar with vim please skip this and keep using +the default editor. + + # Install vim + sudo apt-get install vim + sudo update-alternatives --set editor /usr/bin/vim.basic + +Install the required packages: + + sudo apt-get install wget curl gcc checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libreadline6-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev openssh-server git-core libyaml-dev postfix libpq-dev libicu-dev openssl nodejs + sudo apt-get install redis-server + +# 2. Ruby + +Download Ruby and compile it: + + mkdir /tmp/ruby && cd /tmp/ruby + curl --progress http://cache.ruby-lang.org/pub/ruby/ruby-2.1.6.tar.bz2 | tar xj + cd ruby-2.1.6/ + ./configure --disable-install-rdoc + make + sudo make install + +Install the Bundler Gem: + + sudo gem install bundler --no-ri --no-rdoc + + +## 3. GitLab CI user: + + sudo adduser --disabled-login --gecos 'GitLab CI' gitlab_ci + + +## 4. Prepare the database + +We recommend PostgreSQL but you can also use MySQL + +### MySQL + + # Install the database packages + sudo apt-get install mysql-server mysql-client libmysqlclient-dev + + # Login to MySQL + $ mysql -u root -p + + # Create the GitLab CI database + mysql> CREATE DATABASE IF NOT EXISTS `gitlab_ci_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; + + # Create the MySQL User change $password to a real password + mysql> CREATE USER 'gitlab_ci'@'localhost' IDENTIFIED BY '$password'; + + # Grant proper permissions to the MySQL User + mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlab_ci_production`.* TO 'gitlab_ci'@'localhost'; + + # Logout MYSQL + mysql> exit; + +### PostgreSQL + + # Install the database packages + sudo apt-get install -y postgresql-9.1 libpq-dev + + # Login to PostgreSQL + sudo -u postgres psql -d template1 + + # Create a user for GitLab CI. We do not specify a password because we are using peer authentication. + template1=# CREATE USER gitlab_ci; + + # Create the GitLab CI production database & grant all privileges on database + template1=# CREATE DATABASE gitlab_ci_production OWNER gitlab_ci; + + # Quit the database session + template1=# \q + + # Try connecting to the new database with the new user + sudo -u gitlab_ci -H psql -d gitlab_ci_production + +## 5. Get code + + cd /home/gitlab_ci/ + + sudo -u gitlab_ci -H git clone https://gitlab.com/gitlab-org/gitlab-ci.git + + cd gitlab-ci + + sudo -u gitlab_ci -H git checkout 7-12-stable + +## 6. Setup application + + # Edit application settings + # Production + sudo -u gitlab_ci -H cp config/application.yml.example config/application.yml + sudo -u gitlab_ci -H editor config/application.yml + # Development + #sudo -u gitlab_ci -H cp config/application.yml.example.development config/application.yml + + # Copy the example secrets file + sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml + sudo -u gitlab_ci -H chmod 0600 config/secrets.yml + + # Edit web server settings + sudo -u gitlab_ci -H cp config/unicorn.rb.example config/unicorn.rb + sudo -u gitlab_ci -H editor config/unicorn.rb + + # Create socket and pid directories + sudo -u gitlab_ci -H mkdir -p tmp/sockets/ + sudo chmod -R u+rwX tmp/sockets/ + sudo -u gitlab_ci -H mkdir -p tmp/pids/ + sudo chmod -R u+rwX tmp/pids/ + + # Change the permissions of the directory where build traces are stored + sudo chmod -R u+rwX builds/ + +### Install gems + + # For MySQL (note, the option says "without ... postgres") + sudo -u gitlab_ci -H bundle install --without development test postgres --deployment + + # Or for PostgreSQL (note, the option says "without ... mysql") + sudo -u gitlab_ci -H bundle install --without development test mysql --deployment + +### Setup db + + # mysql + sudo -u gitlab_ci -H cp config/database.yml.mysql config/database.yml + + # postgres + sudo -u gitlab_ci -H cp config/database.yml.postgresql config/database.yml + + # Edit user/password (not necessary with default Postgres setup) + sudo -u gitlab_ci -H editor config/database.yml + + # Setup tables + sudo -u gitlab_ci -H bundle exec rake setup RAILS_ENV=production + + # Setup schedules + sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production + +### Secure secrets.yml + +The `secrets.yml` file stores encryption keys for sessions and secure variables. +Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups. +Otherwise your secrets are exposed if one of your backups is compromised. + +## 8. Install Init Script + +Copy the init script (will be /etc/init.d/gitlab_ci): + + sudo cp /home/gitlab_ci/gitlab-ci/lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci + +Make GitLab CI start on boot: + + sudo update-rc.d gitlab_ci defaults 21 + + +Start your GitLab CI instance: + + sudo service gitlab_ci start + # or + sudo /etc/init.d/gitlab_ci start + + +# 8. Nginx + + +## Installation + + sudo apt-get install nginx + +## Site Configuration + +Download an example site config: + + sudo cp /home/gitlab_ci/gitlab-ci/lib/support/nginx/gitlab_ci /etc/nginx/sites-available/gitlab_ci + sudo ln -s /etc/nginx/sites-available/gitlab_ci /etc/nginx/sites-enabled/gitlab_ci + +Make sure to edit the config file to match your setup: + + # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** + # to the IP address and fully-qualified domain name + # of your host serving GitLab CI + sudo editor /etc/nginx/sites-enabled/gitlab_ci + +## Check your configuration + + sudo nginx -t + +## Start nginx + + sudo /etc/init.d/nginx start + +# 9. GitLab OAuth2 application + + +Go to the admin area of GitLab, to the `Application` section. Create an application for the GitLab CI +For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. + +When `app_id` and `app_secret` are generated add them to the GitLab CI config: + +``` +production: + gitlab_server: + url: 'http://gitlab.example.com' + app_id: XXXXXX + app_secret: XXXXXX + +``` + + +# 10. Runners + + +Now you need Runners to process your builds. +Checkout the [Gitlab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it + +# Done! + + +Visit YOUR_SERVER for your first GitLab CI login. +You will be asked to authorize with your GitLab credentials. + +**Enjoy!** + +## Advanced settings + +### SMTP email settings + +If you want to use SMTP do next: + + # Copy config file + sudo -u gitlab_ci -H cp config/initializers/smtp_settings.rb.sample config/initializers/smtp_settings.rb + + # Edit it with your settings + sudo -u gitlab_ci -H editor config/initializers/smtp_settings.rb + +Restart application + +### Custom Redis Connection + +If you'd like Resque to connect to a Redis server on a non-standard port or on +a different host, you can configure its connection string via the +`config/resque.yml` file. + + # example + production: redis://redis.example.tld:6379 + +If you want to connect the Redis server via socket, then use the "unix:" URL scheme +and the path to the Redis socket file in the `config/resque.yml` file. + + # example + production: unix:/path/to/redis/socket diff --git a/doc/ci/install/requirements.md b/doc/ci/install/requirements.md new file mode 100644 index 00000000000..6c12607c3f8 --- /dev/null +++ b/doc/ci/install/requirements.md @@ -0,0 +1,61 @@ +# Requirements + +## Operating Systems + +### Supported Unix distributions + +- Ubuntu +- Debian +- CentOS +- Red Hat Enterprise Linux (please use the CentOS packages and instructions) +- Scientific Linux (please use the CentOS packages and instructions) +- Oracle Linux (please use the CentOS packages and instructions) + +For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). + +### Unsupported Unix distributions + +- OS X +- Arch Linux +- Fedora +- Gentoo +- FreeBSD + +### Non-Unix operating systems such as Windows + +GitLab CI is developed for Unix operating systems. +GitLab CI does **not** run on Windows and we have no plans of supporting it in the near future. +Please consider using a virtual machine to run GitLab CI. + +## Ruby versions + +GitLab requires Ruby (MRI) 2.0 or 2.1 +You will have to use the standard MRI implementation of Ruby. +We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab CI needs several Gems that have native extensions. + + +### Memory + +You need at least 1GB of addressable memory (RAM + swap) to install and use GitLab CI! + +## Unicorn Workers + +It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. + +For most instances we recommend using: CPU cores + 1 = unicorn workers. +So for a machine with 2 cores, 3 unicorn workers is ideal. + +For all machines that have 1GB and up we recommend a minimum of three unicorn workers. +If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. +With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). +If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. + +To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). + +## Supported web browsers + +- Chrome (Latest stable version) +- Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) +- Safari 7+ (known problem: required fields in html5 do not work) +- Opera (Latest released version) +- IE 10+ diff --git a/doc/ci/migration_to_omnibus/README.md b/doc/ci/migration_to_omnibus/README.md new file mode 100644 index 00000000000..ae46f59a7bb --- /dev/null +++ b/doc/ci/migration_to_omnibus/README.md @@ -0,0 +1,29 @@ +## Migrating to packaged CI + +Since version 5.1 GitLab CI is shipping as part of the GitLab omnibus package. This guide describes how to migrate GitLab CI from a source installation to an Omnibus package. + +### 1. Update GitLab + +Update GitLab CI manually to the version that you will install using the omnibus package (at least 7.11). Follow the update [manual for installation from sourse](update/README.md) + +### 2. Backup + +``` +sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production +``` + +This command will create a backup file in the tmp folder +(`/home/gitlab_ci/gitlab_ci/tmp/backups/*_gitlab_ci_backup.tar.gz`). You can read more in the [GitLab CI backup/restore documentation](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/raketasks/backup_restore.md) + +### 2. Install a packaged GitLab CI + +This process is described in the [instruction for enabling GitLab CI](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/gitlab-ci/README.md) + +### 4. Restore backup + +Put backup file to directory `/var/opt/gitlab/backups`. +Run the restore command: + +``` +sudo gitlab-ci-rake backup:restore +``` diff --git a/doc/ci/permissions/README.md b/doc/ci/permissions/README.md new file mode 100644 index 00000000000..d77061c14cd --- /dev/null +++ b/doc/ci/permissions/README.md @@ -0,0 +1,24 @@ +# Users Permissions + +GitLab CI relies on user's role on the GitLab. There are three permissions levels on GitLab CI: admin, master, developer, other. + +Admin user can perform any actions on GitLab CI in scope of instance and project. Also user with admin permission can use admin interface. + + + + +| Action | Guest, Reporter | Developer | Master | Admin | +|---------------------------------------|-----------------|-------------|----------|--------| +| See commits and builds | ✓ | ✓ | ✓ | ✓ | +| Retry or cancel build | | ✓ | ✓ | ✓ | +| Remove project | | | ✓ | ✓ | +| Create project | | | ✓ | ✓ | +| Change project configuration | | | ✓ | ✓ | +| Add specific runners | | | ✓ | ✓ | +| Add shared runners | | | | ✓ | +| See events in the system | | | | ✓ | +| Admin interface | | | | ✓ | + + + + diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md new file mode 100644 index 00000000000..3b9156f7409 --- /dev/null +++ b/doc/ci/quick_start/README.md @@ -0,0 +1,119 @@ +# Quick Start + +To start building projects with GitLab CI a few steps needs to be done. + +## 1. Install GitLab and CI + +First you need to have a working GitLab and GitLab CI instance. + +You can omit this step if you use [GitLab.com](http://GitLab.com/). + +## 2. Create repository on GitLab + +Once you login on your GitLab add a new repository where you will store your source code. +Push your application to that repository. + +## 3. Add project to CI + +The next part is to login to GitLab CI. +Point your browser to the URL you have set GitLab CI or use [ci.gitlab.com](http://ci.gitlab.com/) that is linked to [GitLab.com](http://GitLab.com/). + +On the first screen you will see a list of GitLab's projects that you have access to: + +![Projects](projects.png) + +Click **Add Project to CI**. +This will create project in CI and authorize GitLab CI to fetch sources from GitLab. + +> GitLab CI creates unique token that is used to configure GitLab CI service in GitLab. +> This token allows to access GitLab's repository and configures GitLab to trigger GitLab CI webhook on **Push events** and **Tag push events**. +> You can see that token by going to Project's Settings > Services > GitLab CI. +> You will see there token, the same token is assigned in GitLab CI settings of project. + +## 4. Create project's configuration - .gitlab-ci.yml + +The next: You have to define how your project will be built. +GitLab CI uses [YAML](https://en.wikipedia.org/wiki/YAML) file to store build configuration. +You need to create `.gitlab-ci.yml` in root directory of your repository: + +```yaml +before_script: + - bundle install + +rspec: + script: + - bundle exec rspec + +rubocop: + script: + - bundle exec rubocop +``` + +This is the simplest possible build configuration that will work for most Ruby applications: +1. Define two jobs `rspec` and `rubocop` with two different commands to be executed. +1. Before every job execute commands defined by `before_script`. + +The `.gitlab-ci.yml` defines set of jobs with constrains how and when they should be run. +The jobs are defined as top-level elements with name and always have to contain the `script`. +Jobs are used to create builds, which are then picked by [runners](../runners/README.md) and executed within environment of the runner. +What is important that each job is run independently from each other. + +For more information and complete `.gitlab-ci.yml` syntax, please check the [Configuring project (.gitlab-ci.yml)](../yaml/README.md). + +## 5. Add file and push .gitlab-ci.yml to repository + +Once you created `.gitlab-ci.yml` you should add it to git repository and push it to GitLab. + +```bash +git add .gitlab-ci.yml +git commit +git push origin master +``` + +If you refresh the project's page on GitLab CI you will notice a one new commit: + +![](new_commit.png) + +However the commit has status **pending** which means that commit was not yet picked by runner. + +## 6. Configure runner + +In GitLab CI, Runners run your builds. +A runner is a machine (can be virtual, bare-metal or VPS) that picks up builds through the coordinator API of GitLab CI. + +A runner can be specific to a certain project or serve any project in GitLab CI. +A runner that serves all projects is called a shared runner. +More information about different runner types can be found in [Configuring runner](../runners/README.md). + +To check if you have runners assigned to your project go to **Runners**. You will find there information how to setup project specific runner: + +1. Install GitLab Runner software. Checkout the [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner) section to install it. +1. Specify following URL during runner setup: https://ci.gitlab.com/ +1. Use the following registration token during setup: TOKEN + +If you do it correctly your runner should be shown under **Runners activated for this project**: + +![](runners_activated.png) + +### Shared runners + +If you use [ci.gitlab.com](http://ci.gitlab.com/) you can use **Shared runners** provided by GitLab Inc. +These are special virtual machines that are run on GitLab's infrastructure that can build any project. +To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project. + +## 7. Check status of commit + +If everything went OK and you go to commit, the status of the commit should change from **pending** to either **running**, **success** or **failed**. + +![](commit_status.png) + +You can click **Build ID** to view build log for specific job. + +## 8. Congratulations! + +You managed to build your first project using GitLab CI. +You may need to tune your `.gitlab-ci.yml` file to implement build plan for your project. +A few examples how it can be done you can find on [Examples](../examples/README.md) page. + +GitLab CI also offers **the Lint** tool to verify validity of your `.gitlab-ci.yml` which can be useful to troubleshoot potential problems. +The Lint is available from project's settings or by adding `/lint` to GitLab CI url. diff --git a/doc/ci/quick_start/build_status.png b/doc/ci/quick_start/build_status.png new file mode 100644 index 00000000000..333259e6acd Binary files /dev/null and b/doc/ci/quick_start/build_status.png differ diff --git a/doc/ci/quick_start/commit_status.png b/doc/ci/quick_start/commit_status.png new file mode 100644 index 00000000000..725b79e6f91 Binary files /dev/null and b/doc/ci/quick_start/commit_status.png differ diff --git a/doc/ci/quick_start/new_commit.png b/doc/ci/quick_start/new_commit.png new file mode 100644 index 00000000000..3839e893c17 Binary files /dev/null and b/doc/ci/quick_start/new_commit.png differ diff --git a/doc/ci/quick_start/projects.png b/doc/ci/quick_start/projects.png new file mode 100644 index 00000000000..0b3430a69db Binary files /dev/null and b/doc/ci/quick_start/projects.png differ diff --git a/doc/ci/quick_start/runners.png b/doc/ci/quick_start/runners.png new file mode 100644 index 00000000000..25b4046bc00 Binary files /dev/null and b/doc/ci/quick_start/runners.png differ diff --git a/doc/ci/quick_start/runners_activated.png b/doc/ci/quick_start/runners_activated.png new file mode 100644 index 00000000000..c934bd12f41 Binary files /dev/null and b/doc/ci/quick_start/runners_activated.png differ diff --git a/doc/ci/raketasks/README.md b/doc/ci/raketasks/README.md new file mode 100644 index 00000000000..872be4dc966 --- /dev/null +++ b/doc/ci/raketasks/README.md @@ -0,0 +1,3 @@ +# Rake Tasks + ++ [Backup/Restore](backup_restore.md) \ No newline at end of file diff --git a/doc/ci/raketasks/backup_restore.md b/doc/ci/raketasks/backup_restore.md new file mode 100644 index 00000000000..eed12c46247 --- /dev/null +++ b/doc/ci/raketasks/backup_restore.md @@ -0,0 +1,237 @@ +# Backup restore + +## Create a backup of the GitLab CI + +A backup creates an archive file that contains the database and builds files. +This archive will be saved in backup_path (see `config/application.yml`). +The filename will be `[TIMESTAMP]_gitlab_ci_backup.tar.gz`. This timestamp can be used to restore an specific backup. +You can only restore a backup to exactly the same version of GitLab CI that you created it on, for example 7.10.1. + +*If you are interested in the GitLab backup please follow to the [GitLab backup documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/backup_restore.md)* + +``` +# use this command if you've installed GitLab CI with the Omnibus package +sudo gitlab-ci-rake backup:create + +# if you've installed GitLab from source +sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production +``` + + +Example output: + +``` +Dumping database ... +Dumping PostgreSQL database gitlab_ci_development ... [DONE] +done +Dumping builds ... +done +Creating backup archive: 1430930060_gitlab_ci_backup.tar.gz ... done +Uploading backup archive to remote storage ... skipped +Deleting tmp directories ... done +done +Deleting old backups ... skipping +``` + +## Upload backups to remote (cloud) storage + +You can let the backup script upload the '.tar.gz' file it creates. +It uses the [Fog library](http://fog.io/) to perform the upload. +In the example below we use Amazon S3 for storage. +But Fog also lets you use [other storage providers](http://fog.io/storage/). + +For omnibus packages: + +```ruby +gitlab_ci['backup_upload_connection'] = { + 'provider' => 'AWS', + 'region' => 'eu-west-1', + 'aws_access_key_id' => 'AKIAKIAKI', + 'aws_secret_access_key' => 'secret123' +} +gitlab_ci['backup_upload_remote_directory'] = 'my.s3.bucket' +gitlab_ci['backup_multipart_chunk_size'] = 104857600 +``` + +For installations from source: + +```yaml + backup: + # snip + upload: + # Fog storage connection settings, see http://fog.io/storage/ . + connection: + provider: AWS + region: eu-west-1 + aws_access_key_id: AKIAKIAKI + aws_secret_access_key: 'secret123' + # The remote 'directory' to store your backups. For S3, this would be the bucket name. + remote_directory: 'my.s3.bucket' + multipart_chunk_size: 104857600 +``` + +If you are uploading your backups to S3 you will probably want to create a new +IAM user with restricted access rights. To give the upload user access only for +uploading backups create the following IAM profile, replacing `my.s3.bucket` +with the name of your bucket: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Stmt1412062044000", + "Effect": "Allow", + "Action": [ + "s3:AbortMultipartUpload", + "s3:GetBucketAcl", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:ListBucketMultipartUploads", + "s3:PutObject", + "s3:PutObjectAcl" + ], + "Resource": [ + "arn:aws:s3:::my.s3.bucket/*" + ] + }, + { + "Sid": "Stmt1412062097000", + "Effect": "Allow", + "Action": [ + "s3:GetBucketLocation", + "s3:ListAllMyBuckets" + ], + "Resource": [ + "*" + ] + }, + { + "Sid": "Stmt1412062128000", + "Effect": "Allow", + "Action": [ + "s3:ListBucket" + ], + "Resource": [ + "arn:aws:s3:::my.s3.bucket" + ] + } + ] +} +``` + +## Storing configuration files + +Please be informed that a backup does not store your configuration and secret files. +If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration). +If you have a cookbook installation there should be a copy of your configuration in Chef. +If you have an installation from source: +1. please backup `config/secrets.yml` file that contains key to encrypt variables in database, +but don't store it in the same place as your database backups. +Otherwise your secrets are exposed in case one of your backups is compromised. +1. please consider backing up your `application.yml` file, +1. any SSL keys and certificates, +1. and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). + +## Restore a previously created backup + +You can only restore a backup to exactly the same version of GitLab CI that you created it on, for example 7.10.1. + +### Installation from source + +``` +sudo -u gitlab_ci -H bundle exec rake backup:restore RAILS_ENV=production +``` + +Options + +``` +BACKUP=timestamp_of_backup (required if more than one backup exists) +``` + +### Omnibus package installation + +We will assume that you have installed GitLab CI from an omnibus package and run +`sudo gitlab-ctl reconfigure` at least once. + +First make sure your backup tar file is in `/var/opt/gitlab/backups`. + +```shell +sudo cp 1393513186_gitlab_ci_backup.tar.gz /var/opt/gitlab/backups/ +``` + +Next, restore the backup by running the restore command. You need to specify the +timestamp of the backup you are restoring. + +```shell +# Stop processes that are connected to the database +sudo gitlab-ctl stop ci-unicorn +sudo gitlab-ctl stop ci-sidekiq + +# This command will overwrite the contents of your GitLab CI database! +sudo gitlab-ci-rake backup:restore BACKUP=1393513186 + +# Start GitLab +sudo gitlab-ctl start +``` + +If there is a GitLab version mismatch between your backup tar file and the installed +version of GitLab, the restore command will abort with an error. Install a package for +the [required version](https://www.gitlab.com/downloads/archives/) and try again. + + + +## Configure cron to make daily backups + +### For installation from source: +``` +cd /home/git/gitlab +sudo -u gitlab_ci -H editor config/application.yml # Enable keep_time in the backup section to automatically delete old backups +sudo -u gitlab_ci crontab -e # Edit the crontab for the git user +``` + +Add the following lines at the bottom: + +``` +# Create a backup of the GitLab CI every day at 4am +0 4 * * * cd /home/gitlab_ci/gitlab_ci && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake backup:create RAILS_ENV=production CRON=1 +``` + +The `CRON=1` environment setting tells the backup script to suppress all progress output if there are no errors. +This is recommended to reduce cron spam. + +### Omnibus package installation + +To schedule a cron job that backs up your GitLab CI, use the root user: + +``` +sudo su - +crontab -e +``` + +There, add the following line to schedule the backup for everyday at 2 AM: + +``` +0 2 * * * /opt/gitlab/bin/gitlab-ci-rake backup:create CRON=1 +``` + +You may also want to set a limited lifetime for backups to prevent regular +backups using all your disk space. To do this add the following lines to +`/etc/gitlab/gitlab.rb` and reconfigure: + +``` +# limit backup lifetime to 7 days - 604800 seconds +gitlab_ci['backup_keep_time'] = 604800 +``` + +NOTE: This cron job does not [backup your omnibus-gitlab configuration](#backup-and-restore-omnibus-gitlab-configuration). + +## Known issues + +If you’ve been using GitLab CI since 7.11 or before using MySQL and the official installation guide, you will probably get the following error while making a backup: `Dumping MySQL database gitlab_ci_production ... mysqldump: Got error: 1044: Access denied for user 'gitlab_ci'@'localhost' to database 'gitlab_ci_production' when using LOCK TABLES` .This can be resolved by adding a LOCK TABLES permission to the gitlab_ci MySQL user. Add this permission with: +``` +$ mysql -u root -p +mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlab_ci_production`.* TO 'gitlab_ci'@'localhost'; +``` + diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md new file mode 100644 index 00000000000..68dcfe23ffb --- /dev/null +++ b/doc/ci/runners/README.md @@ -0,0 +1,145 @@ +# Runners + +In GitLab CI, Runners run your [yaml](../yaml/README.md). +A runner is an isolated (virtual) machine that picks up builds +through the coordinator API of GitLab CI. + +A runner can be specific to a certain project or serve any project +in GitLab CI. A runner that serves all projects is called a shared runner. + +## Shared vs. Specific Runners + +A runner that is specific only runs for the specified project. A shared runner +can run jobs for every project that has enabled the option +`Allow shared runners`. + +**Shared runners** are useful for jobs that have similar requirements, +between multiple projects. Rather than having multiple runners idling for +many projects, you can have a single or a small number of runners that handle +multiple projects. This makes it easier to maintain and update runners. + +**Specific runners** are useful for jobs that have special requirements or for +projects with a very demand. If a job has certain requirements, you can set +up the specific runner with this in mind, while not having to do this for all +runners. For example, if you want to deploy a certain project, you can setup +a specific runner to have the right credentials for this. + +Projects with high demand of CI activity can also benefit from using specific runners. +By having dedicated runners you are guaranteed that the runner is not being held +up by another project's jobs. + +You can set up a specific runner to be used by multiple projects. The difference +with a shared runner is that you have to enable each project explicitly for +the runner to be able to run its jobs. + +Specific runners do not get shared with forked projects automatically. +A fork does copy the CI settings (jobs, allow shared, etc) of the cloned repository. + +# Creating and Registering a Runner + +There are several ways to create a runner. Only after creation, upon +registration its status as Shared or Specific is determined. + +[See the documentation for](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation) +the different methods of installing a Runner instance. + +After installing the runner, you can either register it as `Shared` or as `Specific`. +You can only register a Shared Runner if you have admin access to the GitLab instance. + +## Registering a Shared Runner + +You can only register a shared runner if you are an admin on the linked +GitLab instance. + +Grab the shared-runner token on the `admin/runners` page of your GitLab CI +instance. + +![shared token](shared_runner.png) + +Now simply register the runner as any runner: + +``` +sudo gitlab-runner register +``` + +Note that you will have to enable `Allows shared runners` for each project +that you want to make use of a shared runner. This is by default `off`. + +## Registering a Specific Runner + +Registering a specific can be done in two ways: + +1. Creating a runner with the project registration token +1. Converting a shared runner into a specific runner (one-way, admin only) + +There are several ways to create a runner instance. The steps below only +concern registering the runner on GitLab CI. + +### Registering a Specific Runner with a Project Registration token + +To create a specific runner without having admin rights to the GitLab instance, +visit the project you want to make the runner work for in GitLab CI. + +Click on the runner tab and use the registration token you find there to +setup a specific runner for this project. + +![project runners in GitLab CI](project_specific.png) + +To register the runner, run the command below and follow instructions: + +``` +sudo gitlab-runner register +``` + +### Making an existing Shared Runner Specific + +If you are an admin on your GitLab instance, +you can make any shared runner a specific runner, _but you can not +make a specific runner a shared runner_. + +To make a shared runner specific, go to the runner page (`/admin/runners`) +and find your runner. Add any projects on the left to make this runner +run exclusively for these projects, therefore making it a specific runner. + +![making a shared runner specific](shared_to_specific_admin.png) + +## Using Shared Runners Effectively + +If you are planning to use shared runners, there are several things you +should keep in mind. + +### Use Tags + +You must setup a runner to be able to run all the different types of jobs +that it may encounter on the projects it's shared over. This would be +problematic for large amounts of projects, if it wasn't for tags. + +By tagging a Runner for the types of jobs it can handle, you can make sure +shared runners will only run the jobs they are equipped to run. + +For instance, at GitLab we have runners tagged with "rails" if they contain +the appropriate dependencies to run Rails test suites. + +### Be Careful with Sensitive Information + +If you can run a build on a runner, you can get access to any code it runs +and get the token of the runner. With shared runners, this means that anyone +that runs jobs on the runner, can access anyone else's code that runs on the runner. + +In addition, because you can get access to the runner token, it is possible +to create a clone of a runner and submit false builds, for example. + +The above is easily avoided by restricting the usage of shared runners +on large public GitLab instances and controlling access to your GitLab instance. + +### Forks + +Whenever a project is forked, it copies the settings of the jobs that relate +to it. This means that if you have shared runners setup for a project and +someone forks that project, the shared runners will also serve jobs of this +project. + +# Attack vectors in runners + +Mentioned briefly earlier, but the following things of runners can be exploited. +We're always looking for contributions that can mitigate these [Security Considerations](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md). diff --git a/doc/ci/runners/project_specific.png b/doc/ci/runners/project_specific.png new file mode 100644 index 00000000000..f51ea694e78 Binary files /dev/null and b/doc/ci/runners/project_specific.png differ diff --git a/doc/ci/runners/shared_runner.png b/doc/ci/runners/shared_runner.png new file mode 100644 index 00000000000..9755144eb08 Binary files /dev/null and b/doc/ci/runners/shared_runner.png differ diff --git a/doc/ci/runners/shared_to_specific_admin.png b/doc/ci/runners/shared_to_specific_admin.png new file mode 100644 index 00000000000..44a4bef22f7 Binary files /dev/null and b/doc/ci/runners/shared_to_specific_admin.png differ diff --git a/doc/ci/update/3.0-to-3.1.md b/doc/ci/update/3.0-to-3.1.md new file mode 100644 index 00000000000..039e781a6c8 --- /dev/null +++ b/doc/ci/update/3.0-to-3.1.md @@ -0,0 +1,30 @@ +# Update from 3.0 to 3.1 + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 3-1-stable +``` + +### 4. Install libs, migrations etc + +``` +bundle install --without development test --deployment +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/3.1-to-3.2.md b/doc/ci/update/3.1-to-3.2.md new file mode 100644 index 00000000000..dc6ab6514c6 --- /dev/null +++ b/doc/ci/update/3.1-to-3.2.md @@ -0,0 +1,30 @@ +# Update from 3.1 to 3.2 + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 3-2-stable +``` + +### 4. Install libs, migrations etc + +``` +bundle install --without development test --deployment +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/3.2-to-4.0.md b/doc/ci/update/3.2-to-4.0.md new file mode 100644 index 00000000000..50c26e49224 --- /dev/null +++ b/doc/ci/update/3.2-to-4.0.md @@ -0,0 +1,35 @@ +# Update from 3.2 to 4.0 + +## GitLab CI 4.0 requires GitLab 6.3 or higher + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 4-0-stable +``` + +### 4. Install libs, migrations etc + +``` +bundle install --without development test --deployment +bundle exec rake db:migrate RAILS_ENV=production +bundle exec whenever -w +``` + +### 5. Start web application + + sudo service gitlab_ci start + +### 6. Update your runners to version 4.0 diff --git a/doc/ci/update/4.0-to-4.1.md b/doc/ci/update/4.0-to-4.1.md new file mode 100644 index 00000000000..e749b324ee7 --- /dev/null +++ b/doc/ci/update/4.0-to-4.1.md @@ -0,0 +1,49 @@ +# Update from 4.0 to 4.1 + +## GitLab CI 4.x requires GitLab 6.3 or higher + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 4-1-stable +``` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production + +# Update cron +bundle exec whenever -w +``` + +### 5. Start web application + + sudo service gitlab_ci start + +### 6. Make sure your runners are version 4.0 + + + cd path_to_runner + cat VERSION + +To update runners follow this instructions https://github.com/gitlabhq/gitlab-ci-runner#update diff --git a/doc/ci/update/4.1-to-4.2.md b/doc/ci/update/4.1-to-4.2.md new file mode 100644 index 00000000000..81394aa216e --- /dev/null +++ b/doc/ci/update/4.1-to-4.2.md @@ -0,0 +1,47 @@ +# Update from 4.1 to 4.2 + +## GitLab CI 4.x requires GitLab 6.3 or higher + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 4-2-stable +``` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Install the new init script +As a user with sudo rights: + +``` +cd /home/gitlab_ci/gitlab-ci +sudo cp lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci +sudo chmod +x /etc/init.d/gitlab_ci +``` + +### 6. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/4.2-to-4.3.md b/doc/ci/update/4.2-to-4.3.md new file mode 100644 index 00000000000..f340cb61411 --- /dev/null +++ b/doc/ci/update/4.2-to-4.3.md @@ -0,0 +1,61 @@ +# Update from 4.2 to 4.3 + +## GitLab CI 4.x requires GitLab 6.3 or higher + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 4-3-stable +``` + +### 4. Install libs, migrations etc + +Edit web server settings + +``` +cp config/unicorn.rb.example config/unicorn.rb +editor config/unicorn.rb +``` + +Then: + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Setup unicorn +``` +cp config/unicorn.rb.example config/unicorn.rb +``` + +### 6. Install the new init script +As a user with sudo rights: + +``` +cd /home/gitlab_ci/gitlab-ci +sudo cp lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci +sudo chmod +x /etc/init.d/gitlab_ci +``` + +### 7. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/4.3-to-5.0.md b/doc/ci/update/4.3-to-5.0.md new file mode 100644 index 00000000000..23327eac608 --- /dev/null +++ b/doc/ci/update/4.3-to-5.0.md @@ -0,0 +1,42 @@ +# Update from 4.3 to 5.0 + +__GitLab CI 5.0 requires GitLab 6.3 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. get latest code + +``` +git fetch +git checkout 5-0-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.0-to-5.1.md b/doc/ci/update/5.0-to-5.1.md new file mode 100644 index 00000000000..9a37a87a882 --- /dev/null +++ b/doc/ci/update/5.0-to-5.1.md @@ -0,0 +1,42 @@ +# Update from 5.0 to 5.1 + +__GitLab CI 5.1 requires GitLab 6.3 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 5-1-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.1-to-5.2.md b/doc/ci/update/5.1-to-5.2.md new file mode 100644 index 00000000000..6dcf00276f6 --- /dev/null +++ b/doc/ci/update/5.1-to-5.2.md @@ -0,0 +1,42 @@ +# Update from 5.1 to 5.2 + +__GitLab CI 5.2 requires GitLab 7.5 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 5-2-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.2-to-5.3.md b/doc/ci/update/5.2-to-5.3.md new file mode 100644 index 00000000000..2b70f2146ac --- /dev/null +++ b/doc/ci/update/5.2-to-5.3.md @@ -0,0 +1,42 @@ +# Update from 5.2 to 5.3 + +__GitLab CI 5.3 requires GitLab 7.5 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 5-3-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.3-to-5.4.md b/doc/ci/update/5.3-to-5.4.md new file mode 100644 index 00000000000..6d1cd61389a --- /dev/null +++ b/doc/ci/update/5.3-to-5.4.md @@ -0,0 +1,60 @@ +# Update from 5.3 to 5.4 + +__GitLab CI 5.4 requires GitLab 7.5 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 5-4-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Update config +GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), +you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. + +For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. + +You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: + +``` +production: + gitlab_server: + url: 'http://gitlab.example.com' + app_id: XXXXXX + app_secret: XXXXXX + +``` + + +### 6. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/5.4-to-7.8.md b/doc/ci/update/5.4-to-7.8.md new file mode 100644 index 00000000000..71561d047e6 --- /dev/null +++ b/doc/ci/update/5.4-to-7.8.md @@ -0,0 +1,65 @@ +# Update from 5.3 to 7.8 + +## Notice + +With this release we are bumping the GitLab CI version to 7.8 in order to be on par with the current GitLab version and +to avoid naming confusion. + +__GitLab CI 7.8 requires GitLab 7.8 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 7-8-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + +``` +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Update config +GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), +you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. + +For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. + +You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: + +``` +production: + gitlab_server: + url: 'http://gitlab.example.com' + app_id: XXXXXX + app_secret: XXXXXX + +``` + + +### 6. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/7.10-to-7.11.md b/doc/ci/update/7.10-to-7.11.md new file mode 100644 index 00000000000..4e04509e84d --- /dev/null +++ b/doc/ci/update/7.10-to-7.11.md @@ -0,0 +1,45 @@ +# Update from 7.10 to 7.11 + +## Notice + +__GitLab CI 7.11 requires GitLab 7.11 or higher and GitLab Multi Runner 0.3.0 and higher + +### 1. Stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 7-11-stable +``` + +### 4. Install libs, migrations etc + + +``` +# Install nodejs dependency: +sudo apt-get install nodejs + +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + + +### 5. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/7.11-to-7.12.md b/doc/ci/update/7.11-to-7.12.md new file mode 100644 index 00000000000..ad45ea3a660 --- /dev/null +++ b/doc/ci/update/7.11-to-7.12.md @@ -0,0 +1,67 @@ +# Update from 7.11 to 7.12 + +## Notice + +__GitLab CI 7.12 requires GitLab 7.12 or higher and GitLab Multi Runner 0.4.0 or higher + +### 1. Stop CI server + + sudo service gitlab_ci stop + +### 2. Update ruby if needed + +If your ruby version is older than 2.0.0 please update it. + +Update packages: + + sudo apt-get update + sudo apt-get upgrade + +Download Ruby and compile it: + + mkdir /tmp/ruby && cd /tmp/ruby + curl --progress http://cache.ruby-lang.org/pub/ruby/ruby-2.1.6.tar.bz2 | tar xj + cd ruby-2.1.6/ + ./configure --disable-install-rdoc + make + sudo make install + +Install the Bundler Gem: + + sudo gem install bundler --no-ri --no-rdoc + +### 3. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 4. Get latest code + +``` +git fetch +git checkout 7-12-stable +``` + +### 5. Install libs, migrations etc + + +``` +# Install nodejs dependency: +sudo apt-get install nodejs + +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + + +### 6. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/7.12-to-7.13.md b/doc/ci/update/7.12-to-7.13.md new file mode 100644 index 00000000000..2877c297d6f --- /dev/null +++ b/doc/ci/update/7.12-to-7.13.md @@ -0,0 +1,63 @@ +# Update from 7.12 to 7.13 + +## Notice + +__GitLab CI 7.13 requires GitLab 7.12 or higher and GitLab Multi Runner 0.5.0 or higher + +### 1. Stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 7-13-stable +``` + +### 4. Make sure GitLab CI can write to the builds/ directory + +``` +sudo chmod -R u+rwX builds +``` + +### 4. Copy secrets + +The `secrets.yml` file is used to store keys to encrypt sessions and encrypt secure variables. +When you run migrations make sure to store it someplace safe. +Don't store it in the same place as your database backups, +otherwise your secrets are exposed if one of your backups is compromised. + +``` +sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml +sudo -u gitlab_ci -H chmod 0600 config/secrets.yml +``` + +### 5. Install libs, migrations etc + + +``` +# Install nodejs dependency: +sudo apt-get install nodejs + +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + + +### 5. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/7.8-to-7.9.md b/doc/ci/update/7.8-to-7.9.md new file mode 100644 index 00000000000..fc1cefe9ba5 --- /dev/null +++ b/doc/ci/update/7.8-to-7.9.md @@ -0,0 +1,66 @@ +# Update from 7.8 to 7.9 + +## Notice + +__GitLab CI 7.9 requires GitLab 7.9 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 7-9-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + + +``` +# Install nodejs dependency: +sudo apt-get install nodejs + +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Update config +GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), +you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. + +For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. + +You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: + +``` +production: + gitlab_server: + url: 'http://gitlab.example.com' + app_id: XXXXXX + app_secret: XXXXXX + +``` + + +### 6. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/7.9-to-7.10.md b/doc/ci/update/7.9-to-7.10.md new file mode 100644 index 00000000000..2b54874daf4 --- /dev/null +++ b/doc/ci/update/7.9-to-7.10.md @@ -0,0 +1,49 @@ +# Update from 7.9 to 7.10 + +## Notice + +__GitLab CI 7.10 requires GitLab 7.10 or higher and GitLab CI Runner v5__ + +### 1. stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git fetch +git checkout 7-10-stable +``` + +#### Redis config + +If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` + +### 4. Install libs, migrations etc + + +``` +# Install nodejs dependency: +sudo apt-get install nodejs + +# For MySQL users +bundle install --without postgres development test --deployment + +# For Postgres users +bundle install --without mysql development test --deployment + +# Run migrations +bundle exec rake db:migrate RAILS_ENV=production +``` + + +### 5. Start web application + + sudo service gitlab_ci start diff --git a/doc/ci/update/README.md b/doc/ci/update/README.md new file mode 100644 index 00000000000..7a615ef7978 --- /dev/null +++ b/doc/ci/update/README.md @@ -0,0 +1,2 @@ ++ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update) ++ [Patch versions](patch_versions.md) diff --git a/doc/ci/update/patch_versions.md b/doc/ci/update/patch_versions.md new file mode 100644 index 00000000000..c13f69c03c9 --- /dev/null +++ b/doc/ci/update/patch_versions.md @@ -0,0 +1,59 @@ +# Universal update guide for patch versions. For example from 4.0.0 to 4.0.1, also see the [semantic versioning specification](http://semver.org/). + +### 1. Stop CI server + + sudo service gitlab_ci stop + +### 2. Switch to your gitlab_ci user + +``` +sudo su gitlab_ci +cd /home/gitlab_ci/gitlab-ci +``` + +### 3. Get latest code + +``` +git pull origin STABLE_BRANCH +``` + +### 4. Install libs, migrations etc + +``` +bundle install --without development test --deployment +bundle exec rake db:migrate RAILS_ENV=production +``` + +### 5. Start web application + + sudo service gitlab_ci start + + +# One line upgrade command + +You have read through the entire guide and probably already did all the steps one by one. + +Here is a one line command with all above steps for the next time you upgrade: + +``` + sudo service gitlab_ci stop && \ + cd /home/gitlab_ci/gitlab-ci && \ + sudo -u gitlab_ci -H git pull origin `git rev-parse --abbrev-ref HEAD` && \ + sudo -u gitlab_ci -H bundle install --without development test --deployment && \ + sudo -u gitlab_ci -H bundle exec rake db:migrate RAILS_ENV=production && \ + cd && \ + sudo service gitlab_ci start +``` + +Since when we start this `gitlab_ci` service, the document `db/schema.rb` is shown always as modified for git, you could even do like this, **if and only if**, you are sure you only have that modification: + +``` + sudo service gitlab_ci stop && \ + cd /home/gitlab_ci/gitlab-ci && \ + sudo -u gitlab_ci -H git checkout -f `git rev-parse --abbrev-ref HEAD` && \ + sudo -u gitlab_ci -H git pull origin `git rev-parse --abbrev-ref HEAD` && \ + sudo -u gitlab_ci -H bundle install --without development test --deployment && \ + sudo -u gitlab_ci -H bundle exec rake db:migrate RAILS_ENV=production && \ + cd && \ + sudo service gitlab_ci start +``` diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md new file mode 100644 index 00000000000..04c6bf1e3a3 --- /dev/null +++ b/doc/ci/variables/README.md @@ -0,0 +1,95 @@ +## Variables +When receiving a build from GitLab CI, the runner prepares the build environment. +It starts by setting a list of **predefined variables** (Environment Variables) and a list of **user-defined variables** + +The variables can be overwritten. They take precedence over each other in this order: +1. Secure variables +1. YAML-defined variables +1. Predefined variables + +For example, if you define: +1. API_TOKEN=SECURE as Secure Variable +1. API_TOKEN=YAML as YAML-defined variable + +The API_TOKEN will take the Secure Variable value: `SECURE`. + +### Predefined variables (Environment Variables) + +| Variable | Description | +|-------------------------|-------------| +| **CI** | Mark that build is executed in CI environment | +| **GITLAB_CI** | Mark that build is executed in GitLab CI environment | +| **CI_SERVER** | Mark that build is executed in CI environment | +| **CI_SERVER_NAME** | CI server that is used to coordinate builds | +| **CI_SERVER_VERSION** | Not yet defined | +| **CI_SERVER_REVISION** | Not yet defined | +| **CI_BUILD_REF** | The commit revision for which project is built | +| **CI_BUILD_BEFORE_SHA** | The first commit that were included in push request | +| **CI_BUILD_REF_NAME** | The branch or tag name for which project is built | +| **CI_BUILD_ID** | The unique id of the current build that GitLab CI uses internally | +| **CI_BUILD_REPO** | The URL to clone the Git repository | +| **CI_PROJECT_ID** | The unique id of the current project that GitLab CI uses internally | +| **CI_PROJECT_DIR** | The full path where the repository is cloned and where the build is ran | + +Example values: + +```bash +export CI_BUILD_BEFORE_SHA="9df57456fa9de2a6d335ca5edf9750ed812b9df0" +export CI_BUILD_ID="50" +export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a" +export CI_BUILD_REF_NAME="master" +export CI_BUILD_REPO="https://gitlab.com/gitlab-org/gitlab-ce.git" +export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce" +export CI_PROJECT_ID="34" +export CI_SERVER="yes" +export CI_SERVER_NAME="GitLab CI" +export CI_SERVER_REVISION="" +export CI_SERVER_VERSION="" +``` + +### YAML-defined variables +**This feature requires GitLab Runner 0.5.0 or higher** + +GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in build environment. +The variables are stored in repository and are meant to store non-sensitive project configuration, ie. RAILS_ENV or DATABASE_URL. + +```yaml +variables: + DATABASE_URL: "postgres://postgres@postgres/my_database" +``` + +These variables can be later used in all executed commands and scripts. + +The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. + +More information about Docker integration can be found in [Using Docker Images](../docker/using_docker_images.md). + +### User-defined variables (Secure Variables) +**This feature requires GitLab Runner 0.4.0 or higher** + +GitLab CI allows you to define per-project **Secure Variables** that are set in build environment. +The secure variables are stored out of the repository (the `.gitlab-ci.yml`). +These variables are securely stored in GitLab CI database and are hidden in the build log. +It's desired method to use them for storing passwords, secret keys or whatever you want. + +Secure Variables can added by going to `Project > Variables > Add Variable`. + +They will be available for all subsequent builds. + +### Use variables +The variables are set as environment variables in build environment and are accessible with normal methods that are used to access such variables. +In most cases the **bash** is used to execute build script. +To access variables (predefined and user-defined) in bash environment, prefix the variable name with `$`: +``` +job_name: + script: + - echo $CI_BUILD_ID +``` + +You can also list all environment variables with `export` command, +but be aware that this will also expose value of all **Secure Variables** in build log: +``` +job_name: + script: + - export +``` diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md new file mode 100644 index 00000000000..4caeccacb7f --- /dev/null +++ b/doc/ci/yaml/README.md @@ -0,0 +1,204 @@ +# Configuration of your builds with .gitlab-ci.yml +From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML) file (**.gitlab-ci.yml**) for the project configuration. +It is placed in the root of your repository and contains definitions of how your project should be built. + +The YAML file defines a set of jobs with constraints stating when they should be run. +The jobs are defined as top-level elements with a name and always have to contain the `script` clause: + +```yaml +job1: + script: "execute-script-for-job1" + +job2: + script: "execute-script-for-job2" +``` + +The above example is the simplest possible CI configuration with two separate jobs, +where each of the jobs executes a different command. +Of course a command can execute code directly (`./configure;make;make install`) or run a script (`test.sh`) in the repository. + +Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner. +What is important, is that each job is run independently from each other. + +## .gitlab-ci.yml +The YAML syntax allows for using more complex job specifications than in the above example: + +```yaml +image: ruby:2.1 +services: + - postgres + +before_script: + - bundle_install + +stages: + - build + - test + - deploy + +job1: + stage: build + script: + - execute-script-for-job1 + only: + - master + tags: + - docker +``` + +There are a few `keywords` that can't be used as job names: + +| keyword | required | description | +|---------------|----------|-------------| +| image | optional | Use docker image, covered in [Use Docker](../docker/README.md) | +| services | optional | Use docker services, covered in [Use Docker](../docker/README.md) | +| stages | optional | Define build stages | +| types | optional | Alias for `stages` | +| before_script | optional | Define commands prepended for each job's script | +| variables | optional | Define build variables | + +### image and services +This allows to specify a custom Docker image and a list of services that can be used for time of the build. +The configuration of this feature is covered in separate document: [Use Docker](../docker/README.md). + +### before_script +`before_script` is used to define the command that should be run before all builds, including deploy builds. This can be an array or a multiline string. + +### stages +`stages` is used to define build stages that can be used by jobs. +The specification of `stages` allows for having flexible multi stage pipelines. + +The ordering of elements in `stages` defines the ordering of builds' execution: + +1. Builds of the same stage are run in parallel. +1. Builds of next stage are run after success. + +Let's consider the following example, which defines 3 stages: +``` +stages: + - build + - test + - deploy +``` + +1. First all jobs of `build` are executed in parallel. +1. If all jobs of `build` succeeds, the `test` jobs are executed in parallel. +1. If all jobs of `test` succeeds, the `deploy` jobs are executed in parallel. +1. If all jobs of `deploy` succeeds, the commit is marked as `success`. +1. If any of the previous jobs fails, the commit is marked as `failed` and no jobs of further stage are executed. + +There are also two edge cases worth mentioning: + +1. If no `stages` is defined in `.gitlab-ci.yml`, then by default the `build`, `test` and `deploy` are allowed to be used as job's stage by default. +2. If a job doesn't specify `stage`, the job is assigned the `test` stage. + +### types +Alias for [stages](#stages). + +### variables +**This feature requires `gitlab-runner` with version equal or greater than 0.5.0.** + +GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in build environment. +The variables are stored in repository and are meant to store non-sensitive project configuration, ie. RAILS_ENV or DATABASE_URL. + +```yaml +variables: + DATABASE_URL: "postgres://postgres@postgres/my_database" +``` + +These variables can be later used in all executed commands and scripts. + +The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. + +## Jobs +`.gitlab-ci.yml` allows you to specify an unlimited number of jobs. +Each job has to have a unique `job_name`, which is not one of the keywords mentioned above. +A job is defined by a list of parameters that define the build behaviour. + +```yaml +job_name: + script: + - rake spec + - coverage + stage: test + only: + - master + except: + - develop + tags: + - ruby + - postgres + allow_failure: true +``` + +| keyword | required | description | +|---------------|----------|-------------| +| script | required | Defines a shell script which is executed by runner | +| stage | optional (default: test) | Defines a build stage | +| type | optional | Alias for `stage` | +| only | optional | Defines a list of git refs for which build is created | +| except | optional | Defines a list of git refs for which build is not created | +| tags | optional | Defines a list of tags which are used to select runner | +| allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status | + +### script +`script` is a shell script which is executed by runner. The shell script is prepended with `before_script`. + +```yaml +job: + script: "bundle exec rspec" +``` + +This parameter can also contain several commands using an array: +```yaml +job: + script: + - uname -a + - bundle exec rspec +``` + +### stage +`stage` allows to group build into different stages. Builds of the same `stage` are executed in `parallel`. +For more info about the use of `stage` please check the [stages](#stages). + +### only and except +This are two parameters that allow for setting a refs policy to limit when jobs are built: +1. `only` defines the names of branches and tags for which job will be built. +2. `except` defines the names of branches and tags for which the job wil **not** be built. + +There are a few rules that apply to usage of refs policy: + +1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. +1. `only` and `except` allow for using the regexp expressions. +1. `only` and `except` allow for using special keywords: `branches` and `tags`. +These names can be used for example to exclude all tags and all branches. + +```yaml +job: + only: + - /^issue-.*$/ # use regexp + except: + - branches # use special keyword +``` + +### tags +`tags` is used to select specific runners from the list of all runners that are allowed to run this project. + +During registration of a runner, you can specify the runner's tags, ie.: `ruby`, `postgres`, `development`. +`tags` allow you to run builds with runners that have the specified tags assigned: + +``` +job: + tags: + - ruby + - postgres +``` + +The above specification will make sure that `job` is built by a runner that have `ruby` AND `postgres` tags defined. + +## Validate the .gitlab-ci.yml +Each instance of GitLab CI has an embedded debug tool called Lint. +You can find the link to the Lint in the project's settings page or use short url `/lint`. + +## Skipping builds +There is one more way to skip all builds, if your commit message contains tag [ci skip]. In this case, commit will be created but builds will be skipped \ No newline at end of file diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 1f9dd6bc152..b434c4202de 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -8,7 +8,7 @@ module API expose :id, :state, :avatar_url expose :web_url do |user, options| - Rails.application.routes.url_helpers.user_url(user) + Gitlab::Application.routes.url_helpers.user_url(user) end end @@ -81,7 +81,7 @@ module API expose :avatar_url expose :web_url do |group, options| - Rails.application.routes.url_helpers.group_url(group) + Gitlab::Application.routes.url_helpers.group_url(group) end end diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb new file mode 100644 index 00000000000..ac6d667cf8d --- /dev/null +++ b/lib/ci/ansi2html.rb @@ -0,0 +1,224 @@ +# ANSI color library +# +# Implementation per http://en.wikipedia.org/wiki/ANSI_escape_code +module Ci + module Ansi2html + # keys represent the trailing digit in color changing command (30-37, 40-47, 90-97. 100-107) + COLOR = { + 0 => 'black', # not that this is gray in the intense color table + 1 => 'red', + 2 => 'green', + 3 => 'yellow', + 4 => 'blue', + 5 => 'magenta', + 6 => 'cyan', + 7 => 'white', # not that this is gray in the dark (aka default) color table + } + + STYLE_SWITCHES = { + bold: 0x01, + italic: 0x02, + underline: 0x04, + conceal: 0x08, + cross: 0x10, + } + + def self.convert(ansi) + Converter.new().convert(ansi) + end + + class Converter + def on_0(s) reset() end + def on_1(s) enable(STYLE_SWITCHES[:bold]) end + def on_3(s) enable(STYLE_SWITCHES[:italic]) end + def on_4(s) enable(STYLE_SWITCHES[:underline]) end + def on_8(s) enable(STYLE_SWITCHES[:conceal]) end + def on_9(s) enable(STYLE_SWITCHES[:cross]) end + + def on_21(s) disable(STYLE_SWITCHES[:bold]) end + def on_22(s) disable(STYLE_SWITCHES[:bold]) end + def on_23(s) disable(STYLE_SWITCHES[:italic]) end + def on_24(s) disable(STYLE_SWITCHES[:underline]) end + def on_28(s) disable(STYLE_SWITCHES[:conceal]) end + def on_29(s) disable(STYLE_SWITCHES[:cross]) end + + def on_30(s) set_fg_color(0) end + def on_31(s) set_fg_color(1) end + def on_32(s) set_fg_color(2) end + def on_33(s) set_fg_color(3) end + def on_34(s) set_fg_color(4) end + def on_35(s) set_fg_color(5) end + def on_36(s) set_fg_color(6) end + def on_37(s) set_fg_color(7) end + def on_38(s) set_fg_color_256(s) end + def on_39(s) set_fg_color(9) end + + def on_40(s) set_bg_color(0) end + def on_41(s) set_bg_color(1) end + def on_42(s) set_bg_color(2) end + def on_43(s) set_bg_color(3) end + def on_44(s) set_bg_color(4) end + def on_45(s) set_bg_color(5) end + def on_46(s) set_bg_color(6) end + def on_47(s) set_bg_color(7) end + def on_48(s) set_bg_color_256(s) end + def on_49(s) set_bg_color(9) end + + def on_90(s) set_fg_color(0, 'l') end + def on_91(s) set_fg_color(1, 'l') end + def on_92(s) set_fg_color(2, 'l') end + def on_93(s) set_fg_color(3, 'l') end + def on_94(s) set_fg_color(4, 'l') end + def on_95(s) set_fg_color(5, 'l') end + def on_96(s) set_fg_color(6, 'l') end + def on_97(s) set_fg_color(7, 'l') end + def on_99(s) set_fg_color(9, 'l') end + + def on_100(s) set_bg_color(0, 'l') end + def on_101(s) set_bg_color(1, 'l') end + def on_102(s) set_bg_color(2, 'l') end + def on_103(s) set_bg_color(3, 'l') end + def on_104(s) set_bg_color(4, 'l') end + def on_105(s) set_bg_color(5, 'l') end + def on_106(s) set_bg_color(6, 'l') end + def on_107(s) set_bg_color(7, 'l') end + def on_109(s) set_bg_color(9, 'l') end + + def convert(ansi) + @out = "" + @n_open_tags = 0 + reset() + + s = StringScanner.new(ansi.gsub("<", "<")) + while(!s.eos?) + if s.scan(/\e([@-_])(.*?)([@-~])/) + handle_sequence(s) + else + @out << s.scan(/./m) + end + end + + close_open_tags() + @out + end + + def handle_sequence(s) + indicator = s[1] + commands = s[2].split ';' + terminator = s[3] + + # We are only interested in color and text style changes - triggered by + # sequences starting with '\e[' and ending with 'm'. Any other control + # sequence gets stripped (including stuff like "delete last line") + return unless indicator == '[' and terminator == 'm' + + close_open_tags() + + if commands.empty?() + reset() + return + end + + evaluate_command_stack(commands) + + css_classes = [] + + unless @fg_color.nil? + fg_color = @fg_color + # Most terminals show bold colored text in the light color variant + # Let's mimic that here + if @style_mask & STYLE_SWITCHES[:bold] != 0 + fg_color.sub!(/fg-(\w{2,}+)/, 'fg-l-\1') + end + css_classes << fg_color + end + css_classes << @bg_color unless @bg_color.nil? + + STYLE_SWITCHES.each do |css_class, flag| + css_classes << "term-#{css_class}" if @style_mask & flag != 0 + end + + open_new_tag(css_classes) if css_classes.length > 0 + end + + def evaluate_command_stack(stack) + return unless command = stack.shift() + + if self.respond_to?("on_#{command}", true) + self.send("on_#{command}", stack) + end + + evaluate_command_stack(stack) + end + + def open_new_tag(css_classes) + @out << %{} + @n_open_tags += 1 + end + + def close_open_tags + while @n_open_tags > 0 + @out << %{} + @n_open_tags -= 1 + end + end + + def reset + @fg_color = nil + @bg_color = nil + @style_mask = 0 + end + + def enable(flag) + @style_mask |= flag + end + + def disable(flag) + @style_mask &= ~flag + end + + def set_fg_color(color_index, prefix = nil) + @fg_color = get_term_color_class(color_index, ["fg", prefix]) + end + + def set_bg_color(color_index, prefix = nil) + @bg_color = get_term_color_class(color_index, ["bg", prefix]) + end + + def get_term_color_class(color_index, prefix) + color_name = COLOR[color_index] + return nil if color_name.nil? + + get_color_class(["term", prefix, color_name]) + end + + def set_fg_color_256(command_stack) + css_class = get_xterm_color_class(command_stack, "fg") + @fg_color = css_class unless css_class.nil? + end + + def set_bg_color_256(command_stack) + css_class = get_xterm_color_class(command_stack, "bg") + @bg_color = css_class unless css_class.nil? + end + + def get_xterm_color_class(command_stack, prefix) + # the 38 and 48 commands have to be followed by "5" and the color index + return unless command_stack.length >= 2 + return unless command_stack[0] == "5" + + command_stack.shift() # ignore the "5" command + color_index = command_stack.shift().to_i + + return unless color_index >= 0 + return unless color_index <= 255 + + get_color_class(["xterm", prefix, color_index]) + end + + def get_color_class(segments) + [segments].flatten.compact.join('-') + end + end + end +end diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb new file mode 100644 index 00000000000..392fb548001 --- /dev/null +++ b/lib/ci/api/api.rb @@ -0,0 +1,37 @@ +Dir["#{Rails.root}/lib/ci/api/*.rb"].each {|file| require file} + +module Ci + module API + class API < Grape::API + version 'v1', using: :path + + rescue_from ActiveRecord::RecordNotFound do + rack_response({ 'message' => '404 Not found' }.to_json, 404) + end + + rescue_from :all do |exception| + # lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60 + # why is this not wrapped in something reusable? + trace = exception.backtrace + + message = "\n#{exception.class} (#{exception.message}):\n" + message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) + message << " " << trace.join("\n ") + + API.logger.add Logger::FATAL, message + rack_response({ 'message' => '500 Internal Server Error' }, 500) + end + + format :json + + helpers Helpers + + mount Builds + mount Commits + mount Runners + mount Projects + mount Forks + mount Triggers + end + end +end diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb new file mode 100644 index 00000000000..83ca1e6481c --- /dev/null +++ b/lib/ci/api/builds.rb @@ -0,0 +1,53 @@ +module Ci + module API + # Builds API + class Builds < Grape::API + resource :builds do + # Runs oldest pending build by runner - Runners only + # + # Parameters: + # token (required) - The uniq token of runner + # + # Example Request: + # POST /builds/register + post "register" do + authenticate_runner! + update_runner_last_contact + required_attributes! [:token] + not_found! unless current_runner.active? + + build = Ci::RegisterBuildService.new.execute(current_runner) + + if build + update_runner_info + present build, with: Entities::Build + else + not_found! + end + end + + # Update an existing build - Runners only + # + # Parameters: + # id (required) - The ID of a project + # state (optional) - The state of a build + # trace (optional) - The trace of a build + # Example Request: + # PUT /builds/:id + put ":id" do + authenticate_runner! + update_runner_last_contact + build = Ci::Build.where(runner_id: current_runner.id).running.find(params[:id]) + build.update_attributes(trace: params[:trace]) if params[:trace] + + case params[:state].to_s + when 'success' + build.success + when 'failed' + build.drop + end + end + end + end + end +end diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb new file mode 100644 index 00000000000..bac463a5909 --- /dev/null +++ b/lib/ci/api/commits.rb @@ -0,0 +1,66 @@ +module Ci + module API + class Commits < Grape::API + resource :commits do + # Get list of commits per project + # + # Parameters: + # project_id (required) - The ID of a project + # project_token (requires) - Project token + # page (optional) + # per_page (optional) - items per request (default is 20) + # + get do + required_attributes! [:project_id, :project_token] + project = Ci::Project.find(params[:project_id]) + authenticate_project_token!(project) + + commits = project.commits.page(params[:page]).per(params[:per_page] || 20) + present commits, with: Entities::CommitWithBuilds + end + + # Create a commit + # + # Parameters: + # project_id (required) - The ID of a project + # project_token (requires) - Project token + # data (required) - GitLab push data + # + # Sample GitLab push data: + # { + # "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", + # "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + # "ref": "refs/heads/master", + # "commits": [ + # { + # "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + # "message": "Update Catalan translation to e38cb41.", + # "timestamp": "2011-12-12T14:27:31+02:00", + # "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + # "author": { + # "name": "Jordi Mallach", + # "email": "jordi@softcatala.org", + # } + # }, .... more commits + # ] + # } + # + # Example Request: + # POST /commits + post do + required_attributes! [:project_id, :data, :project_token] + project = Ci::Project.find(params[:project_id]) + authenticate_project_token!(project) + commit = Ci::CreateCommitService.new.execute(project, params[:data]) + + if commit.persisted? + present commit, with: Entities::CommitWithBuilds + else + errors = commit.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + end + end + end +end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb new file mode 100644 index 00000000000..2f0e9d36bc4 --- /dev/null +++ b/lib/ci/api/entities.rb @@ -0,0 +1,44 @@ +module Ci + module API + module Entities + class Commit < Grape::Entity + expose :id, :ref, :sha, :project_id, :before_sha, :created_at + expose :status, :finished_at, :duration + expose :git_commit_message, :git_author_name, :git_author_email + end + + class CommitWithBuilds < Commit + expose :builds + end + + class Build < Grape::Entity + expose :id, :commands, :path, :ref, :sha, :project_id, :repo_url, + :before_sha, :timeout, :allow_git_fetch, :project_name, :options + + expose :variables + end + + class Runner < Grape::Entity + expose :id, :token + end + + class Project < Grape::Entity + expose :id, :name, :timeout, :token, :default_ref, :gitlab_url, :path, + :always_build, :polling_interval, :public, :ssh_url_to_repo, :gitlab_id + end + + class RunnerProject < Grape::Entity + expose :id, :project_id, :runner_id + end + + class WebHook < Grape::Entity + expose :id, :project_id, :url + end + + class TriggerRequest < Grape::Entity + expose :id, :variables + expose :commit, using: Commit + end + end + end +end diff --git a/lib/ci/api/forks.rb b/lib/ci/api/forks.rb new file mode 100644 index 00000000000..4ce944df054 --- /dev/null +++ b/lib/ci/api/forks.rb @@ -0,0 +1,40 @@ +module Ci + module API + class Forks < Grape::API + resource :forks do + # Create a fork + # + # Parameters: + # project_id (required) - The ID of a project + # project_token (requires) - Project token + # private_token(required) - User private token + # data (required) - GitLab project data (name_with_namespace, web_url, default_branch, ssh_url_to_repo) + # + # + # Example Request: + # POST /forks + post do + required_attributes! [:project_id, :data, :project_token, :private_token] + project = Ci::Project.find_by!(gitlab_id: params[:project_id]) + authenticate_project_token!(project) + + user_session = Ci::UserSession.new + user = user_session.authenticate(private_token: params[:private_token]) + + fork = Ci::CreateProjectService.new.execute( + user, + params[:data], + Ci::RoutesHelper.ci_project_url(":project_id"), + project + ) + + if fork + present fork, with: Entities::Project + else + not_found! + end + end + end + end + end +end diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb new file mode 100644 index 00000000000..3f58670fb49 --- /dev/null +++ b/lib/ci/api/helpers.rb @@ -0,0 +1,114 @@ +module Ci + module API + module Helpers + PRIVATE_TOKEN_PARAM = :private_token + PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" + ACCESS_TOKEN_PARAM = :access_token + ACCESS_TOKEN_HEADER = "HTTP_ACCESS_TOKEN" + UPDATE_RUNNER_EVERY = 60 + + def current_user + @current_user ||= begin + options = { + access_token: (params[ACCESS_TOKEN_PARAM] || env[ACCESS_TOKEN_HEADER]), + private_token: (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]), + } + Ci::UserSession.new.authenticate(options.compact) + end + end + + def current_runner + @runner ||= Ci::Runner.find_by_token(params[:token].to_s) + end + + def authenticate! + forbidden! unless current_user + end + + def authenticate_runners! + forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN + end + + def authenticate_runner! + forbidden! unless current_runner + end + + def authenticate_project_token!(project) + forbidden! unless project.valid_token?(params[:project_token]) + end + + def update_runner_last_contact + if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= UPDATE_RUNNER_EVERY + current_runner.update_attributes(contacted_at: Time.now) + end + end + + def update_runner_info + return unless params["info"].present? + info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"]) + current_runner.update(info) + end + + # Checks the occurrences of required attributes, each attribute must be present in the params hash + # or a Bad Request error is invoked. + # + # Parameters: + # keys (required) - A hash consisting of keys that must be present + def required_attributes!(keys) + keys.each do |key| + bad_request!(key) unless params[key].present? + end + end + + def attributes_for_keys(keys, custom_params = nil) + params_hash = custom_params || params + attrs = {} + keys.each do |key| + attrs[key] = params_hash[key] if params_hash[key].present? + end + attrs + end + + # error helpers + + def forbidden! + render_api_error!('403 Forbidden', 403) + end + + def bad_request!(attribute) + message = ["400 (Bad request)"] + message << "\"" + attribute.to_s + "\" not given" + render_api_error!(message.join(' '), 400) + end + + def not_found!(resource = nil) + message = ["404"] + message << resource if resource + message << "Not Found" + render_api_error!(message.join(' '), 404) + end + + def unauthorized! + render_api_error!('401 Unauthorized', 401) + end + + def not_allowed! + render_api_error!('Method Not Allowed', 405) + end + + def render_api_error!(message, status) + error!({ 'message' => message }, status) + end + + private + + def abilities + @abilities ||= begin + abilities = Six.new + abilities << Ability + abilities + end + end + end + end +end diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb new file mode 100644 index 00000000000..f9b4937c033 --- /dev/null +++ b/lib/ci/api/projects.rb @@ -0,0 +1,209 @@ +module Ci + module API + # Projects API + class Projects < Grape::API + before { authenticate! } + + resource :projects do + # Register new webhook for project + # + # Parameters + # project_id (required) - The ID of a project + # web_hook (required) - WebHook URL + # Example Request + # POST /projects/:project_id/webhooks + post ":project_id/webhooks" do + required_attributes! [:web_hook] + + project = Ci::Project.find(params[:project_id]) + + unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + + web_hook = project.web_hooks.new({ url: params[:web_hook] }) + + if web_hook.save + present web_hook, with: Entities::WebHook + else + errors = web_hook.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + + # Retrieve all Gitlab CI projects that the user has access to + # + # Example Request: + # GET /projects + get do + gitlab_projects = Ci::Project.from_gitlab( + current_user, :authorized, { page: params[:page], per_page: params[:per_page], ci_enabled_first: true } + ) + ids = gitlab_projects.map { |project| project.id } + + projects = Ci::Project.where("gitlab_id IN (?)", ids).load + present projects, with: Entities::Project + end + + # Retrieve all Gitlab CI projects that the user owns + # + # Example Request: + # GET /projects/owned + get "owned" do + gitlab_projects = Ci::Project.from_gitlab( + current_user, :owned, { page: params[:page], per_page: params[:per_page], ci_enabled_first: true } + ) + ids = gitlab_projects.map { |project| project.id } + + projects = Ci::Project.where("gitlab_id IN (?)", ids).load + present projects, with: Entities::Project + end + + # Retrieve info for a Gitlab CI project + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # GET /projects/:id + get ":id" do + project = Ci::Project.find(params[:id]) + + unauthorized! unless current_user.can_access_project?(project.gitlab_id) + + present project, with: Entities::Project + end + + # Create Gitlab CI project using Gitlab project info + # + # Parameters: + # name (required) - The name of the project + # gitlab_id (required) - The gitlab id of the project + # path (required) - The gitlab project path, ex. randx/six + # ssh_url_to_repo (required) - The gitlab ssh url to the repo + # default_ref - The branch to run against (defaults to `master`) + # Example Request: + # POST /projects + post do + required_attributes! [:name, :gitlab_id, :ssh_url_to_repo] + + filtered_params = { + name: params[:name], + gitlab_id: params[:gitlab_id], + # we accept gitlab_url for backward compatibility for a while (added to 7.11) + path: params[:path] || params[:gitlab_url].sub(/.*\/(.*\/.*)$/, '\1'), + default_ref: params[:default_ref] || 'master', + ssh_url_to_repo: params[:ssh_url_to_repo] + } + + project = Ci::Project.new(filtered_params) + project.build_missing_services + + if project.save + present project, with: Entities::Project + else + errors = project.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + + # Update a Gitlab CI project + # + # Parameters: + # id (required) - The ID of a project + # name - The name of the project + # gitlab_id - The gitlab id of the project + # path - The gitlab project path, ex. randx/six + # ssh_url_to_repo - The gitlab ssh url to the repo + # default_ref - The branch to run against (defaults to `master`) + # Example Request: + # PUT /projects/:id + put ":id" do + project = Ci::Project.find(params[:id]) + + unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + + attrs = attributes_for_keys [:name, :gitlab_id, :path, :gitlab_url, :default_ref, :ssh_url_to_repo] + + # we accept gitlab_url for backward compatibility for a while (added to 7.11) + if attrs[:gitlab_url] && !attrs[:path] + attrs[:path] = attrs[:gitlab_url].sub(/.*\/(.*\/.*)$/, '\1') + end + + if project.update_attributes(attrs) + present project, with: Entities::Project + else + errors = project.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + + # Remove a Gitlab CI project + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # DELETE /projects/:id + delete ":id" do + project = Ci::Project.find(params[:id]) + + unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + + project.destroy + end + + # Link a Gitlab CI project to a runner + # + # Parameters: + # id (required) - The ID of a CI project + # runner_id (required) - The ID of a runner + # Example Request: + # POST /projects/:id/runners/:runner_id + post ":id/runners/:runner_id" do + project = Ci::Project.find(params[:id]) + runner = Ci::Runner.find(params[:runner_id]) + + unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + + options = { + project_id: project.id, + runner_id: runner.id + } + + runner_project = Ci::RunnerProject.new(options) + + if runner_project.save + present runner_project, with: Entities::RunnerProject + else + errors = project.errors.full_messages.join(", ") + render_api_error!(errors, 400) + end + end + + # Remove a Gitlab CI project from a runner + # + # Parameters: + # id (required) - The ID of a CI project + # runner_id (required) - The ID of a runner + # Example Request: + # DELETE /projects/:id/runners/:runner_id + delete ":id/runners/:runner_id" do + project = Ci::Project.find(params[:id]) + runner = Ci::Runner.find(params[:runner_id]) + + unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + + options = { + project_id: project.id, + runner_id: runner.id + } + + runner_project = Ci::RunnerProject.find_by(options) + + if runner_project.present? + runner_project.destroy + else + not_found! + end + end + end + end + end +end diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb new file mode 100644 index 00000000000..1466fe4356e --- /dev/null +++ b/lib/ci/api/runners.rb @@ -0,0 +1,69 @@ +module Ci + module API + # Runners API + class Runners < Grape::API + resource :runners do + # Get list of all available runners + # + # Example Request: + # GET /runners + get do + authenticate! + runners = Ci::Runner.all + + present runners, with: Entities::Runner + end + + # Delete runner + # Parameters: + # token (required) - The unique token of runner + # + # Example Request: + # GET /runners/delete + delete "delete" do + required_attributes! [:token] + authenticate_runner! + Ci::Runner.find_by_token(params[:token]).destroy + end + + # Register a new runner + # + # Note: This is an "internal" API called when setting up + # runners, so it is authenticated differently. + # + # Parameters: + # token (required) - The unique token of runner + # + # Example Request: + # POST /runners/register + post "register" do + required_attributes! [:token] + + runner = + if params[:token] == GitlabCi::REGISTRATION_TOKEN + # Create shared runner. Requires admin access + Ci::Runner.create( + description: params[:description], + tag_list: params[:tag_list], + is_shared: true + ) + elsif project = Ci::Project.find_by(token: params[:token]) + # Create a specific runner for project. + project.runners.create( + description: params[:description], + tag_list: params[:tag_list] + ) + end + + return forbidden! unless runner + + if runner.id + present runner, with: Entities::Runner + else + not_found! + end + end + end + end + end +end diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb new file mode 100644 index 00000000000..40907d6db54 --- /dev/null +++ b/lib/ci/api/triggers.rb @@ -0,0 +1,49 @@ +module Ci + module API + # Build Trigger API + class Triggers < Grape::API + resource :projects do + # Trigger a GitLab CI project build + # + # Parameters: + # id (required) - The ID of a CI project + # ref (required) - The name of project's branch or tag + # token (required) - The uniq token of trigger + # Example Request: + # POST /projects/:id/ref/:ref/trigger + post ":id/refs/:ref/trigger" do + required_attributes! [:token] + + project = Ci::Project.find(params[:id]) + trigger = Ci::Trigger.find_by_token(params[:token].to_s) + not_found! unless project && trigger + unauthorized! unless trigger.project == project + + # validate variables + variables = params[:variables] + if variables + unless variables.is_a?(Hash) + render_api_error!('variables needs to be a hash', 400) + end + + unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) + end + + # convert variables from Mash to Hash + variables = variables.to_h + end + + # create request and trigger builds + trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) + if trigger_request + present trigger_request, with: Entities::TriggerRequest + else + errors = 'No builds created' + render_api_error!(errors, 400) + end + end + end + end + end +end diff --git a/lib/ci/assets/.gitkeep b/lib/ci/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ci/backup/builds.rb b/lib/ci/backup/builds.rb new file mode 100644 index 00000000000..832a5ab8fdc --- /dev/null +++ b/lib/ci/backup/builds.rb @@ -0,0 +1,32 @@ +module Ci + module Backup + class Builds + attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir + + def initialize + @app_builds_dir = File.realpath(Rails.root.join('ci/builds')) + @backup_dir = GitlabCi.config.backup.path + @backup_builds_dir = File.join(GitlabCi.config.backup.path, 'ci/builds') + end + + # Copy builds from builds directory to backup/builds + def dump + FileUtils.mkdir_p(backup_builds_dir) + FileUtils.cp_r(app_builds_dir, backup_dir) + end + + def restore + backup_existing_builds_dir + + FileUtils.cp_r(backup_builds_dir, app_builds_dir) + end + + def backup_existing_builds_dir + timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}") + if File.exists?(app_builds_dir) + FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path)) + end + end + end + end +end diff --git a/lib/ci/backup/database.rb b/lib/ci/backup/database.rb new file mode 100644 index 00000000000..f7fa3f1833a --- /dev/null +++ b/lib/ci/backup/database.rb @@ -0,0 +1,94 @@ +require 'yaml' + +module Ci + module Backup + class Database + attr_reader :config, :db_dir + + def initialize + @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] + @db_dir = File.join(GitlabCi.config.backup.path, 'db') + FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir) + end + + def dump + success = case config["adapter"] + when /^mysql/ then + $progress.print "Dumping MySQL database #{config['database']} ... " + system('mysqldump', *mysql_args, config['database'], out: db_file_name) + when "postgresql" then + $progress.print "Dumping PostgreSQL database #{config['database']} ... " + pg_env + system('pg_dump', config['database'], out: db_file_name) + end + report_success(success) + abort 'Backup failed' unless success + end + + def restore + success = case config["adapter"] + when /^mysql/ then + $progress.print "Restoring MySQL database #{config['database']} ... " + system('mysql', *mysql_args, config['database'], in: db_file_name) + when "postgresql" then + $progress.print "Restoring PostgreSQL database #{config['database']} ... " + # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE + # statements like MySQL. + drop_all_tables + drop_all_postgres_sequences + pg_env + system('psql', config['database'], '-f', db_file_name) + end + report_success(success) + abort 'Restore failed' unless success + end + + protected + + def db_file_name + File.join(db_dir, 'database.sql') + end + + def mysql_args + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set', + 'password' => '--password' + } + args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + end + + def pg_env + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] + end + + def report_success(success) + if success + $progress.puts '[DONE]'.green + else + $progress.puts '[FAILED]'.red + end + end + + def drop_all_tables + connection = ActiveRecord::Base.connection + connection.tables.each do |table| + connection.drop_table(table) + end + end + + def drop_all_postgres_sequences + connection = ActiveRecord::Base.connection + connection.execute("SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';").each do |sequence| + connection.execute("DROP SEQUENCE #{sequence['relname']}") + end + end + end + end +end diff --git a/lib/ci/backup/manager.rb b/lib/ci/backup/manager.rb new file mode 100644 index 00000000000..2e9d6df7139 --- /dev/null +++ b/lib/ci/backup/manager.rb @@ -0,0 +1,158 @@ +module Ci + module Backup + class Manager + def pack + # saving additional informations + s = {} + s[:db_version] = "#{ActiveRecord::Migrator.current_version}" + s[:backup_created_at] = Time.now + s[:gitlab_version] = GitlabCi::VERSION + s[:tar_version] = tar_version + tar_file = "#{s[:backup_created_at].to_i}_gitlab_ci_backup.tar.gz" + + Dir.chdir(GitlabCi.config.backup.path) do + File.open("#{GitlabCi.config.backup.path}/backup_information.yml", + "w+") do |file| + file << s.to_yaml.gsub(/^---\n/,'') + end + + FileUtils.chmod(0700, ["db", "builds"]) + + # create archive + $progress.print "Creating backup archive: #{tar_file} ... " + orig_umask = File.umask(0077) + if Kernel.system('tar', '-czf', tar_file, *backup_contents) + $progress.puts "done".green + else + puts "creating archive #{tar_file} failed".red + abort 'Backup failed' + end + File.umask(orig_umask) + + upload(tar_file) + end + end + + def upload(tar_file) + remote_directory = GitlabCi.config.backup.upload.remote_directory + $progress.print "Uploading backup archive to remote storage #{remote_directory} ... " + + connection_settings = GitlabCi.config.backup.upload.connection + if connection_settings.blank? + $progress.puts "skipped".yellow + return + end + + connection = ::Fog::Storage.new(connection_settings) + directory = connection.directories.get(remote_directory) + + if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, + multipart_chunk_size: GitlabCi.config.backup.upload.multipart_chunk_size) + $progress.puts "done".green + else + puts "uploading backup to #{remote_directory} failed".red + abort 'Backup failed' + end + end + + def cleanup + $progress.print "Deleting tmp directories ... " + + backup_contents.each do |dir| + next unless File.exist?(File.join(GitlabCi.config.backup.path, dir)) + + if FileUtils.rm_rf(File.join(GitlabCi.config.backup.path, dir)) + $progress.puts "done".green + else + puts "deleting tmp directory '#{dir}' failed".red + abort 'Backup failed' + end + end + end + + def remove_old + # delete backups + $progress.print "Deleting old backups ... " + keep_time = GitlabCi.config.backup.keep_time.to_i + + if keep_time > 0 + removed = 0 + + Dir.chdir(GitlabCi.config.backup.path) do + file_list = Dir.glob('*_gitlab_ci_backup.tar.gz') + file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_ci_backup.tar.gz/ } + file_list.sort.each do |timestamp| + if Time.at(timestamp) < (Time.now - keep_time) + if Kernel.system(*%W(rm #{timestamp}_gitlab_ci_backup.tar.gz)) + removed += 1 + end + end + end + end + + $progress.puts "done. (#{removed} removed)".green + else + $progress.puts "skipping".yellow + end + end + + def unpack + Dir.chdir(GitlabCi.config.backup.path) + + # check for existing backups in the backup dir + file_list = Dir.glob("*_gitlab_ci_backup.tar.gz").each.map { |f| f.split(/_/).first.to_i } + puts "no backups found" if file_list.count == 0 + + if file_list.count > 1 && ENV["BACKUP"].nil? + puts "Found more than one backup, please specify which one you want to restore:" + puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup" + exit 1 + end + + tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_ci_backup.tar.gz") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar.gz") + + unless File.exists?(tar_file) + puts "The specified backup doesn't exist!" + exit 1 + end + + $progress.print "Unpacking backup ... " + + unless Kernel.system(*%W(tar -xzf #{tar_file})) + puts "unpacking backup failed".red + exit 1 + else + $progress.puts "done".green + end + + ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 + + # restoring mismatching backups can lead to unexpected problems + if settings[:gitlab_version] != GitlabCi::VERSION + puts "GitLab CI version mismatch:".red + puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI version in the backup!".red + puts " Please switch to the following version and try again:".red + puts " version: #{settings[:gitlab_version]}".red + puts + puts "Hint: git checkout v#{settings[:gitlab_version]}" + exit 1 + end + end + + def tar_version + tar_version = `tar --version` + tar_version.force_encoding('locale').split("\n").first + end + + private + + def backup_contents + ["db", "builds", "backup_information.yml"] + end + + def settings + @settings ||= YAML.load_file("backup_information.yml") + end + end + end +end diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb new file mode 100644 index 00000000000..e50a7a59c27 --- /dev/null +++ b/lib/ci/charts.rb @@ -0,0 +1,71 @@ +module Ci + module Charts + class Chart + attr_reader :labels, :total, :success, :project, :build_times + + def initialize(project) + @labels = [] + @total = [] + @success = [] + @build_times = [] + @project = project + + collect + end + + + def push(from, to, format) + @labels << from.strftime(format) + @total << project.builds. + where('? > builds.created_at AND builds.created_at > ?', to, from). + count(:all) + @success << project.builds. + where('? > builds.created_at AND builds.created_at > ?', to, from). + success.count(:all) + end + end + + class YearChart < Chart + def collect + 13.times do |i| + start_month = (Date.today.years_ago(1) + i.month).beginning_of_month + end_month = start_month.end_of_month + + push(start_month, end_month, "%d %B %Y") + end + end + end + + class MonthChart < Chart + def collect + 30.times do |i| + start_day = Date.today - 30.days + i.days + end_day = Date.today - 30.days + i.day + 1.day + + push(start_day, end_day, "%d %B") + end + end + end + + class WeekChart < Chart + def collect + 7.times do |i| + start_day = Date.today - 7.days + i.days + end_day = Date.today - 7.days + i.day + 1.day + + push(start_day, end_day, "%d %B") + end + end + end + + class BuildTime < Chart + def collect + commits = project.commits.joins(:builds).where('builds.finished_at is NOT NULL AND builds.started_at is NOT NULL').last(30) + commits.each do |commit| + @labels << commit.short_sha + @build_times << (commit.duration / 60) + end + end + end + end +end diff --git a/lib/ci/current_settings.rb b/lib/ci/current_settings.rb new file mode 100644 index 00000000000..fd78b024970 --- /dev/null +++ b/lib/ci/current_settings.rb @@ -0,0 +1,22 @@ +module Ci + module CurrentSettings + def current_application_settings + key = :ci_current_application_settings + + RequestStore.store[key] ||= begin + if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('ci_application_settings') + Ci::ApplicationSetting.current || Ci::ApplicationSetting.create_from_defaults + else + fake_application_settings + end + end + end + + def fake_application_settings + OpenStruct.new( + all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'], + add_pusher: Ci::Settings.gitlab_ci['add_pusher'], + ) + end + end +end diff --git a/lib/ci/git.rb b/lib/ci/git.rb new file mode 100644 index 00000000000..7acc3f38edb --- /dev/null +++ b/lib/ci/git.rb @@ -0,0 +1,5 @@ +module Ci + module Git + BLANK_SHA = '0' * 40 + end +end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb new file mode 100644 index 00000000000..e625e790df8 --- /dev/null +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -0,0 +1,198 @@ +module Ci + class GitlabCiYamlProcessor + class ValidationError < StandardError;end + + DEFAULT_STAGES = %w(build test deploy) + DEFAULT_STAGE = 'test' + ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables] + ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage] + + attr_reader :before_script, :image, :services, :variables + + def initialize(config) + @config = YAML.load(config) + + unless @config.is_a? Hash + raise ValidationError, "YAML should be a hash" + end + + @config = @config.deep_symbolize_keys + + initial_parsing + + validate! + end + + def builds_for_stage_and_ref(stage, ref, tag = false) + builds.select{|build| build[:stage] == stage && process?(build[:only], build[:except], ref, tag)} + end + + def builds + @jobs.map do |name, job| + build_job(name, job) + end + end + + def stages + @stages || DEFAULT_STAGES + end + + private + + def initial_parsing + @before_script = @config[:before_script] || [] + @image = @config[:image] + @services = @config[:services] + @stages = @config[:stages] || @config[:types] + @variables = @config[:variables] || {} + @config.except!(*ALLOWED_YAML_KEYS) + + # anything that doesn't have script is considered as unknown + @config.each do |name, param| + raise ValidationError, "Unknown parameter: #{name}" unless param.is_a?(Hash) && param.has_key?(:script) + end + + unless @config.values.any?{|job| job.is_a?(Hash)} + raise ValidationError, "Please define at least one job" + end + + @jobs = {} + @config.each do |key, job| + stage = job[:stage] || job[:type] || DEFAULT_STAGE + @jobs[key] = { stage: stage }.merge(job) + end + end + + def process?(only_params, except_params, ref, tag) + return true if only_params.nil? && except_params.nil? + + if only_params + return true if tag && only_params.include?("tags") + return true if !tag && only_params.include?("branches") + + only_params.find do |pattern| + match_ref?(pattern, ref) + end + else + return false if tag && except_params.include?("tags") + return false if !tag && except_params.include?("branches") + + except_params.each do |pattern| + return false if match_ref?(pattern, ref) + end + end + end + + def build_job(name, job) + { + stage: job[:stage], + script: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}", + tags: job[:tags] || [], + name: name, + only: job[:only], + except: job[:except], + allow_failure: job[:allow_failure] || false, + options: { + image: job[:image] || @image, + services: job[:services] || @services + }.compact + } + end + + def match_ref?(pattern, ref) + if pattern.first == "/" && pattern.last == "/" + Regexp.new(pattern[1...-1]) =~ ref + else + pattern == ref + end + end + + def normalize_script(script) + if script.is_a? Array + script.join("\n") + else + script + end + end + + def validate! + unless validate_array_of_strings(@before_script) + raise ValidationError, "before_script should be an array of strings" + end + + unless @image.nil? || @image.is_a?(String) + raise ValidationError, "image should be a string" + end + + unless @services.nil? || validate_array_of_strings(@services) + raise ValidationError, "services should be an array of strings" + end + + unless @stages.nil? || validate_array_of_strings(@stages) + raise ValidationError, "stages should be an array of strings" + end + + unless @variables.nil? || validate_variables(@variables) + raise ValidationError, "variables should be a map of key-valued strings" + end + + @jobs.each do |name, job| + validate_job!("#{name} job", job) + end + + true + end + + def validate_job!(name, job) + job.keys.each do |key| + unless ALLOWED_JOB_KEYS.include? key + raise ValidationError, "#{name}: unknown parameter #{key}" + end + end + + if !job[:script].is_a?(String) && !validate_array_of_strings(job[:script]) + raise ValidationError, "#{name}: script should be a string or an array of a strings" + end + + if job[:stage] + unless job[:stage].is_a?(String) && job[:stage].in?(stages) + raise ValidationError, "#{name}: stage parameter should be #{stages.join(", ")}" + end + end + + if job[:image] && !job[:image].is_a?(String) + raise ValidationError, "#{name}: image should be a string" + end + + if job[:services] && !validate_array_of_strings(job[:services]) + raise ValidationError, "#{name}: services should be an array of strings" + end + + if job[:tags] && !validate_array_of_strings(job[:tags]) + raise ValidationError, "#{name}: tags parameter should be an array of strings" + end + + if job[:only] && !validate_array_of_strings(job[:only]) + raise ValidationError, "#{name}: only parameter should be an array of strings" + end + + if job[:except] && !validate_array_of_strings(job[:except]) + raise ValidationError, "#{name}: except parameter should be an array of strings" + end + + if job[:allow_failure] && !job[:allow_failure].in?([true, false]) + raise ValidationError, "#{name}: allow_failure parameter should be an boolean" + end + end + + private + + def validate_array_of_strings(values) + values.is_a?(Array) && values.all? {|tag| tag.is_a?(String)} + end + + def validate_variables(variables) + variables.is_a?(Hash) && variables.all? {|key, value| key.is_a?(Symbol) && value.is_a?(String)} + end + end +end diff --git a/lib/ci/model.rb b/lib/ci/model.rb new file mode 100644 index 00000000000..c42a0ad36db --- /dev/null +++ b/lib/ci/model.rb @@ -0,0 +1,11 @@ +module Ci + module Model + def table_name_prefix + "ci_" + end + + def model_name + @model_name ||= ActiveModel::Name.new(self, nil, self.name.split("::").last) + end + end +end diff --git a/lib/ci/scheduler.rb b/lib/ci/scheduler.rb new file mode 100644 index 00000000000..ee0958f4be1 --- /dev/null +++ b/lib/ci/scheduler.rb @@ -0,0 +1,16 @@ +module Ci + class Scheduler + def perform + projects = Ci::Project.where(always_build: true).all + projects.each do |project| + last_commit = project.commits.last + next unless last_commit && last_commit.last_build + + interval = project.polling_interval + if (last_commit.last_build.created_at + interval.hours) < Time.now + last_commit.retry + end + end + end + end +end diff --git a/lib/ci/static_model.rb b/lib/ci/static_model.rb new file mode 100644 index 00000000000..bb2bdbed495 --- /dev/null +++ b/lib/ci/static_model.rb @@ -0,0 +1,49 @@ +# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database. +module Ci + module StaticModel + extend ActiveSupport::Concern + + module ClassMethods + # Used by ActiveRecord's polymorphic association to set object_id + def primary_key + 'id' + end + + # Used by ActiveRecord's polymorphic association to set object_type + def base_class + self + end + end + + # Used by AR for fetching attributes + # + # Pass it along if we respond to it. + def [](key) + send(key) if respond_to?(key) + end + + def to_param + id + end + + def new_record? + false + end + + def persisted? + false + end + + def destroyed? + false + end + + def ==(other) + if other.is_a? ::Ci::StaticModel + id == other.id + else + super + end + end + end +end diff --git a/lib/ci/version_info.rb b/lib/ci/version_info.rb new file mode 100644 index 00000000000..2a87c91db5e --- /dev/null +++ b/lib/ci/version_info.rb @@ -0,0 +1,52 @@ +class VersionInfo + include Comparable + + attr_reader :major, :minor, :patch + + def self.parse(str) + if str && m = str.match(/(\d+)\.(\d+)\.(\d+)/) + VersionInfo.new(m[1].to_i, m[2].to_i, m[3].to_i) + else + VersionInfo.new + end + end + + def initialize(major = 0, minor = 0, patch = 0) + @major = major + @minor = minor + @patch = patch + end + + def <=>(other) + return unless other.is_a? VersionInfo + return unless valid? && other.valid? + + if other.major < @major + 1 + elsif @major < other.major + -1 + elsif other.minor < @minor + 1 + elsif @minor < other.minor + -1 + elsif other.patch < @patch + 1 + elsif @patch < other.patch + -1 + else + 0 + end + end + + def to_s + if valid? + "%d.%d.%d" % [@major, @minor, @patch] + else + "Unknown" + end + end + + def valid? + @major >= 0 && @minor >= 0 && @patch >= 0 && @major + @minor + @patch > 0 + end +end diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index a9f1ee9c161..52efed66553 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -71,7 +71,7 @@ module Gitlab end def url_for_commit_range(project, range) - h = Rails.application.routes.url_helpers + h = Gitlab::Application.routes.url_helpers h.namespace_project_compare_url(project.namespace, project, range.to_param.merge(only_path: context[:only_path])) end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index eacdf8a6d37..066fb0c1853 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -67,7 +67,7 @@ module Gitlab end def url_for_commit(project, commit) - h = Rails.application.routes.url_helpers + h = Gitlab::Application.routes.url_helpers h.namespace_project_commit_url(project.namespace, project, commit, only_path: context[:only_path]) end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 2186f36f854..76d56359693 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -54,7 +54,7 @@ module Gitlab end def url_for_label(project, label) - h = Rails.application.routes.url_helpers + h = Gitlab::Application.routes.url_helpers h.namespace_project_issues_path(project.namespace, project, label_name: label.name, only_path: context[:only_path]) diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 884f60f9d53..c6313bab94a 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -61,7 +61,7 @@ module Gitlab end def url_for_merge_request(mr, project) - h = Rails.application.routes.url_helpers + h = Gitlab::Application.routes.url_helpers h.namespace_project_merge_request_url(project.namespace, project, mr, only_path: context[:only_path]) end diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb index 92979a356dc..1a2d0fcf98b 100644 --- a/lib/gitlab/markdown/snippet_reference_filter.rb +++ b/lib/gitlab/markdown/snippet_reference_filter.rb @@ -61,7 +61,7 @@ module Gitlab end def url_for_snippet(snippet, project) - h = Rails.application.routes.url_helpers + h = Gitlab::Application.routes.url_helpers h.namespace_project_snippet_url(project.namespace, project, snippet, only_path: context[:only_path]) end diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index a4aec7a05d1..4e1cce2a0c1 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -49,7 +49,7 @@ module Gitlab private def urls - Rails.application.routes.url_helpers + Gitlab::Application.routes.url_helpers end def link_class diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 11b0d44f340..c5d8be5d681 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -1,7 +1,7 @@ module Gitlab class UrlBuilder - include Rails.application.routes.url_helpers - include GitlabRoutingHelper + include Gitlab::Application.routes.url_helpers + include Gitlab::GitlabRoutingHelper def initialize(type) @type = type diff --git a/lib/tasks/ci/.gitkeep b/lib/tasks/ci/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/tasks/ci/backup.rake b/lib/tasks/ci/backup.rake new file mode 100644 index 00000000000..1cb2e43f875 --- /dev/null +++ b/lib/tasks/ci/backup.rake @@ -0,0 +1,62 @@ +namespace :ci do + namespace :backup do + + desc "GITLAB | Create a backup of the GitLab CI database" + task create: :environment do + configure_cron_mode + + $progress.puts "Dumping database ... ".blue + Ci::Backup::Database.new.dump + $progress.puts "done".green + + $progress.puts "Dumping builds ... ".blue + Ci::Backup::Builds.new.dump + $progress.puts "done".green + + backup = Ci::Backup::Manager.new + backup.pack + backup.cleanup + backup.remove_old + end + + desc "GITLAB | Restore a previously created backup" + task restore: :environment do + configure_cron_mode + + backup = Ci::Backup::Manager.new + backup.unpack + + $progress.puts "Restoring database ... ".blue + Ci::Backup::Database.new.restore + $progress.puts "done".green + + $progress.puts "Restoring builds ... ".blue + Ci::Backup::Builds.new.restore + $progress.puts "done".green + + backup.cleanup + end + + def configure_cron_mode + if ENV['CRON'] + # We need an object we can say 'puts' and 'print' to; let's use a + # StringIO. + require 'stringio' + $progress = StringIO.new + else + $progress = $stdout + end + end + end + + # Disable colors for CRON + unless STDOUT.isatty + module Colored + extend self + + def colorize(string, options={}) + string + end + end + end +end diff --git a/lib/tasks/ci/cleanup.rake b/lib/tasks/ci/cleanup.rake new file mode 100644 index 00000000000..2f4d11bd942 --- /dev/null +++ b/lib/tasks/ci/cleanup.rake @@ -0,0 +1,8 @@ +namespace :ci do + namespace :cleanup do + desc "GitLab CI | Clean running builds" + task builds: :environment do + Ci::Build.running.update_all(status: 'canceled') + end + end +end diff --git a/lib/tasks/ci/schedule_builds.rake b/lib/tasks/ci/schedule_builds.rake new file mode 100644 index 00000000000..49435504c67 --- /dev/null +++ b/lib/tasks/ci/schedule_builds.rake @@ -0,0 +1,6 @@ +namespace :ci do + desc "GitLab CI | Clean running builds" + task schedule_builds: :environment do + Ci::Scheduler.new.perform + end +end diff --git a/lib/tasks/ci/setup.rake b/lib/tasks/ci/setup.rake new file mode 100644 index 00000000000..ab83581ec1b --- /dev/null +++ b/lib/tasks/ci/setup.rake @@ -0,0 +1,7 @@ +namespace :ci do + desc "GitLab CI | Setup gitlab db" + task :setup do + Rake::Task["db:setup"].invoke + Rake::Task["ci:add_limits_mysql"].invoke + end +end diff --git a/lib/tasks/ci/sidekiq.rake b/lib/tasks/ci/sidekiq.rake new file mode 100644 index 00000000000..12fd3635933 --- /dev/null +++ b/lib/tasks/ci/sidekiq.rake @@ -0,0 +1,13 @@ +namespace :ci do + namespace :sidekiq do + desc "GitLab CI | Stop sidekiq" + task :stop do + exec({'RAILS_ENV' => Rails.env}, 'script/background_jobs stop') + end + + desc "GitLab CI | Start sidekiq" + task :start do + exec({'RAILS_ENV' => Rails.env}, 'script/background_jobs start') + end + end +end diff --git a/public/ci/build-canceled.svg b/public/ci/build-canceled.svg new file mode 100644 index 00000000000..922e28bf696 --- /dev/null +++ b/public/ci/build-canceled.svg @@ -0,0 +1 @@ +buildbuildcanceledcanceled \ No newline at end of file diff --git a/public/ci/build-failed.svg b/public/ci/build-failed.svg new file mode 100644 index 00000000000..1aefd3f1761 --- /dev/null +++ b/public/ci/build-failed.svg @@ -0,0 +1 @@ +buildbuildfailedfailed \ No newline at end of file diff --git a/public/ci/build-pending.svg b/public/ci/build-pending.svg new file mode 100644 index 00000000000..536931af84d --- /dev/null +++ b/public/ci/build-pending.svg @@ -0,0 +1 @@ +buildbuildpendingpending \ No newline at end of file diff --git a/public/ci/build-running.svg b/public/ci/build-running.svg new file mode 100644 index 00000000000..0d71eef3c34 --- /dev/null +++ b/public/ci/build-running.svg @@ -0,0 +1 @@ +buildbuildrunningrunning \ No newline at end of file diff --git a/public/ci/build-success.svg b/public/ci/build-success.svg new file mode 100644 index 00000000000..43b67e45f42 --- /dev/null +++ b/public/ci/build-success.svg @@ -0,0 +1 @@ +buildbuildsuccesssuccess \ No newline at end of file diff --git a/public/ci/build-unknown.svg b/public/ci/build-unknown.svg new file mode 100644 index 00000000000..c72a2f5a7f5 --- /dev/null +++ b/public/ci/build-unknown.svg @@ -0,0 +1 @@ +buildbuildunknownunknown \ No newline at end of file diff --git a/public/ci/favicon.ico b/public/ci/favicon.ico new file mode 100644 index 00000000000..9663d4d00b9 Binary files /dev/null and b/public/ci/favicon.ico differ diff --git a/scripts/ci/prepare_build.sh b/scripts/ci/prepare_build.sh new file mode 100755 index 00000000000..864a683a1bd --- /dev/null +++ b/scripts/ci/prepare_build.sh @@ -0,0 +1,22 @@ +#!/bin/bash +if [ -f /.dockerinit ]; then + export FLAGS=(--deployment --path /cache) + + apt-get update -qq + apt-get install -y -qq nodejs + + wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_1.9.0-1+b1_amd64.deb + dpkg -i phantomjs_1.9.0-1+b1_amd64.deb + + cp config/database.yml.mysql config/database.yml + sed -i "s/username:.*/username: root/g" config/database.yml + sed -i "s/password:.*/password:/g" config/database.yml + sed -i "s/# socket:.*/host: mysql/g" config/database.yml +else + export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin + + cp config/database.yml.mysql config/database.yml + sed -i "s/username\:.*$/username\: runner/" config/database.yml + sed -i "s/password\:.*$/password\: 'password'/" config/database.yml + sed -i "s/gitlab_ci_test/gitlab_ci_test_$((RANDOM/5000))/" config/database.yml +fi diff --git a/spec/ci/controllers/commits_controller_spec.rb b/spec/ci/controllers/commits_controller_spec.rb new file mode 100644 index 00000000000..f32d6f8c126 --- /dev/null +++ b/spec/ci/controllers/commits_controller_spec.rb @@ -0,0 +1,27 @@ +require "spec_helper" + +describe CommitsController do + before do + @project = FactoryGirl.create :project + end + + describe "GET /status" do + it "returns status of commit" do + commit = FactoryGirl.create :commit, project: @project + get :status, id: commit.sha, ref_id: commit.ref, project_id: @project.id + + expect(response).to be_success + expect(response.code).to eq('200') + JSON.parse(response.body)["status"] == "pending" + end + + it "returns not_found status" do + commit = FactoryGirl.create :commit, project: @project + get :status, id: commit.sha, ref_id: "deploy", project_id: @project.id + + expect(response).to be_success + expect(response.code).to eq('200') + JSON.parse(response.body)["status"] == "not_found" + end + end +end diff --git a/spec/ci/controllers/projects_controller_spec.rb b/spec/ci/controllers/projects_controller_spec.rb new file mode 100644 index 00000000000..0069a782511 --- /dev/null +++ b/spec/ci/controllers/projects_controller_spec.rb @@ -0,0 +1,108 @@ +require "spec_helper" + +describe ProjectsController do + before do + @project = FactoryGirl.create :project + end + + describe "POST #build" do + it 'should respond 200 if params is ok' do + post :build, id: @project.id, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', + token: @project.token, + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + + + expect(response).to be_success + expect(response.code).to eq('201') + end + + it 'should respond 400 if push about removed branch' do + post :build, id: @project.id, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '0000000000000000000000000000000000000000', + token: @project.token, + ci_yaml_file: gitlab_ci_yaml + + expect(response).not_to be_success + expect(response.code).to eq('400') + end + + it 'should respond 400 if some params missed' do + post :build, id: @project.id, token: @project.token, ci_yaml_file: gitlab_ci_yaml + expect(response).not_to be_success + expect(response.code).to eq('400') + end + + it 'should respond 403 if token is wrong' do + post :build, id: @project.id, token: 'invalid-token' + expect(response).not_to be_success + expect(response.code).to eq('403') + end + end + + describe "POST /projects" do + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + + let (:user_data) do + data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + data.merge("url" => gitlab_url) + end + + let(:user) do + User.new(user_data) + end + + it "creates project" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + Network.any_instance.stub(:enable_ci).and_return(true) + Network.any_instance.stub(:project_hooks).and_return(true) + + post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access + + expect(response.code).to eq('302') + expect(assigns(:project)).not_to be_a_new(Project) + end + + it "shows error" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + User.any_instance.stub(:can_manage_project?).and_return(false) + + post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access + + expect(response.code).to eq('302') + expect(flash[:alert]).to include("You have to have at least master role to enable CI for this project") + end + end + + describe "GET /gitlab" do + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + + let (:user_data) do + data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + data.merge("url" => gitlab_url) + end + + let(:user) do + User.new(user_data) + end + + it "searches projects" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + Network.any_instance.should_receive(:projects).with(hash_including(search: 'str'), :authorized) + + xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access + + expect(response).to be_success + expect(response.code).to eq('200') + end + end +end diff --git a/spec/ci/factories/builds.rb b/spec/ci/factories/builds.rb new file mode 100644 index 00000000000..346e0002bf5 --- /dev/null +++ b/spec/ci/factories/builds.rb @@ -0,0 +1,45 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :build do + started_at 'Di 29. Okt 09:51:28 CET 2013' + finished_at 'Di 29. Okt 09:53:28 CET 2013' + commands 'ls -a' + options do + { + image: "ruby:2.1", + services: ["postgres"] + } + end + + factory :not_started_build do + started_at nil + finished_at nil + end + end +end diff --git a/spec/ci/factories/commits.rb b/spec/ci/factories/commits.rb new file mode 100644 index 00000000000..6fdd46fa74b --- /dev/null +++ b/spec/ci/factories/commits.rb @@ -0,0 +1,75 @@ +# == Schema Information +# +# Table name: commits +# +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# + +# Read about factories at https://github.com/thoughtbot/factory_girl +FactoryGirl.define do + factory :commit do + ref 'master' + before_sha '76de212e80737a608d939f648d959671fb0a0142' + sha '97de212e80737a608d939f648d959671fb0a0142' + push_data do + { + ref: 'refs/heads/master', + before: '76de212e80737a608d939f648d959671fb0a0142', + after: '97de212e80737a608d939f648d959671fb0a0142', + user_name: 'Git User', + user_email: 'git@example.com', + repository: { + name: 'test-data', + url: 'ssh://git@gitlab.com/test/test-data.git', + description: '', + homepage: 'http://gitlab.com/test/test-data' + }, + commits: [ + { + id: '97de212e80737a608d939f648d959671fb0a0142', + message: 'Test commit message', + timestamp: '2014-09-23T13:12:25+02:00', + url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142', + author: { + name: 'Git User', + email: 'git@user.com' + } + } + ], + total_commits_count: 1, + ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + } + end + + factory :commit_without_jobs do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({}) + commit.save + end + end + + factory :commit_with_one_job do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }}) + commit.save + end + end + + factory :commit_with_two_jobs do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }, spinach: { script: "ls" }}) + commit.save + end + end + end +end diff --git a/spec/ci/factories/events.rb b/spec/ci/factories/events.rb new file mode 100644 index 00000000000..1dfa52e3529 --- /dev/null +++ b/spec/ci/factories/events.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: events +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# is_admin :integer +# description :text +# created_at :datetime +# updated_at :datetime +# + +FactoryGirl.define do + factory :event, class: Event do + sequence :description do |n| + "updated project settings#{n}" + end + + factory :admin_event do + is_admin true + end + end +end diff --git a/spec/ci/factories/projects.rb b/spec/ci/factories/projects.rb new file mode 100644 index 00000000000..fb5b563f2f2 --- /dev/null +++ b/spec/ci/factories/projects.rb @@ -0,0 +1,56 @@ +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) not null +# timeout :integer default(3600), not null +# created_at :datetime +# updated_at :datetime +# token :string(255) +# default_ref :string(255) +# path :string(255) +# always_build :boolean default(FALSE), not null +# polling_interval :integer +# public :boolean default(FALSE), not null +# ssh_url_to_repo :string(255) +# gitlab_id :integer +# allow_git_fetch :boolean default(TRUE), not null +# email_recipients :string(255) default(""), not null +# email_add_pusher :boolean default(TRUE), not null +# email_only_broken_builds :boolean default(TRUE), not null +# skip_refs :string(255) +# coverage_regex :string(255) +# shared_runners_enabled :boolean default(FALSE) +# generated_yaml_config :text +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :project_without_token, class: Project do + sequence :name do |n| + "GitLab / gitlab-shell#{n}" + end + + default_ref 'master' + + sequence :path do |n| + "gitlab/gitlab-shell#{n}" + end + + sequence :ssh_url_to_repo do |n| + "git@demo.gitlab.com:gitlab/gitlab-shell#{n}.git" + end + + sequence :gitlab_id + + factory :project do + token 'iPWx6WM4lhHNedGfBpPJNP' + end + + factory :public_project do + public true + end + end +end diff --git a/spec/ci/factories/runner_projects.rb b/spec/ci/factories/runner_projects.rb new file mode 100644 index 00000000000..b27632b3429 --- /dev/null +++ b/spec/ci/factories/runner_projects.rb @@ -0,0 +1,19 @@ +# == Schema Information +# +# Table name: runner_projects +# +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :runner_project do + runner_id 1 + project_id 1 + end +end diff --git a/spec/ci/factories/runners.rb b/spec/ci/factories/runners.rb new file mode 100644 index 00000000000..20a80f03268 --- /dev/null +++ b/spec/ci/factories/runners.rb @@ -0,0 +1,38 @@ +# == Schema Information +# +# Table name: runners +# +# id :integer not null, primary key +# token :string(255) +# created_at :datetime +# updated_at :datetime +# description :string(255) +# contacted_at :datetime +# active :boolean default(TRUE), not null +# is_shared :boolean default(FALSE) +# name :string(255) +# version :string(255) +# revision :string(255) +# platform :string(255) +# architecture :string(255) +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :runner do + sequence :description do |n| + "My runner#{n}" + end + + platform "darwin" + + factory :shared_runner do + is_shared true + end + + factory :specific_runner do + is_shared false + end + end +end diff --git a/spec/ci/factories/trigger_requests.rb b/spec/ci/factories/trigger_requests.rb new file mode 100644 index 00000000000..c85d1027ce6 --- /dev/null +++ b/spec/ci/factories/trigger_requests.rb @@ -0,0 +1,13 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :trigger_request do + factory :trigger_request_with_variables do + variables do + { + TRIGGER_KEY: 'TRIGGER_VALUE' + } + end + end + end +end diff --git a/spec/ci/factories/triggers.rb b/spec/ci/factories/triggers.rb new file mode 100644 index 00000000000..a5af47b7d7f --- /dev/null +++ b/spec/ci/factories/triggers.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :trigger_without_token, class: Trigger do + factory :trigger do + token 'token' + end + end +end diff --git a/spec/ci/factories/users.rb b/spec/ci/factories/users.rb new file mode 100644 index 00000000000..26b30eff0e6 --- /dev/null +++ b/spec/ci/factories/users.rb @@ -0,0 +1,6 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :user do + end +end diff --git a/spec/ci/factories/web_hook.rb b/spec/ci/factories/web_hook.rb new file mode 100644 index 00000000000..3c027fb4861 --- /dev/null +++ b/spec/ci/factories/web_hook.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :web_hook do + sequence(:url) { Faker::Internet.uri('http') } + project + end +end diff --git a/spec/ci/features/admin/builds_spec.rb b/spec/ci/features/admin/builds_spec.rb new file mode 100644 index 00000000000..e62e83692da --- /dev/null +++ b/spec/ci/features/admin/builds_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe "Admin Builds" do + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/builds" do + before do + build + visit admin_builds_path + end + + it { page.should have_content "All builds" } + it { page.should have_content build.short_sha } + end + + describe "Tabs" do + it "shows all builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + page.all(".build-link").size.should == 4 + end + + it "shows pending builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".nav.nav-tabs" do + click_on "Pending" + end + + page.find(".build-link").should have_content(build.id) + page.find(".build-link").should_not have_content(build1.id) + page.find(".build-link").should_not have_content(build2.id) + page.find(".build-link").should_not have_content(build3.id) + end + + it "shows running builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".nav.nav-tabs" do + click_on "Running" + end + + page.find(".build-link").should have_content(build1.id) + page.find(".build-link").should_not have_content(build.id) + page.find(".build-link").should_not have_content(build2.id) + page.find(".build-link").should_not have_content(build3.id) + end + end +end diff --git a/spec/ci/features/admin/events_spec.rb b/spec/ci/features/admin/events_spec.rb new file mode 100644 index 00000000000..469c6ed102d --- /dev/null +++ b/spec/ci/features/admin/events_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "Admin Events" do + let(:event) { FactoryGirl.create :admin_event } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/events" do + before do + event + visit admin_events_path + end + + it { page.should have_content "Events" } + it { page.should have_content event.description } + end +end diff --git a/spec/ci/features/admin/projects_spec.rb b/spec/ci/features/admin/projects_spec.rb new file mode 100644 index 00000000000..6f87e368deb --- /dev/null +++ b/spec/ci/features/admin/projects_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe "Admin Projects" do + let(:project) { FactoryGirl.create :project } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/projects" do + before do + project + visit admin_projects_path + end + + it { page.should have_content "Projects" } + end +end diff --git a/spec/ci/features/admin/runners_spec.rb b/spec/ci/features/admin/runners_spec.rb new file mode 100644 index 00000000000..2827a7fc6e5 --- /dev/null +++ b/spec/ci/features/admin/runners_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe "Admin Runners" do + before do + skip_admin_auth + login_as :user + end + + describe "Runners page" do + before do + runner = FactoryGirl.create(:runner) + commit = FactoryGirl.create(:commit) + FactoryGirl.create(:build, commit: commit, runner_id: runner.id) + visit admin_runners_path + end + + it { page.has_text? "Manage Runners" } + it { page.has_text? "To register a new runner" } + it { page.has_text? "Runners with last contact less than a minute ago: 1" } + + describe 'search' do + before do + FactoryGirl.create :runner, description: 'foo' + FactoryGirl.create :runner, description: 'bar' + + fill_in 'search', with: 'foo' + click_button 'Search' + end + + it { page.should have_content("foo") } + it { page.should_not have_content("bar") } + end + end + + describe "Runner show page" do + let(:runner) { FactoryGirl.create :runner } + + before do + FactoryGirl.create(:project, name: "foo") + FactoryGirl.create(:project, name: "bar") + visit admin_runner_path(runner) + end + + describe 'runner info' do + it { find_field('runner_token').value.should eq runner.token } + end + + describe 'projects' do + it { page.should have_content("foo") } + it { page.should have_content("bar") } + end + + describe 'search' do + before do + fill_in 'search', with: 'foo' + click_button 'Search' + end + + it { page.should have_content("foo") } + it { page.should_not have_content("bar") } + end + end +end diff --git a/spec/ci/features/builds_spec.rb b/spec/ci/features/builds_spec.rb new file mode 100644 index 00000000000..fcd7996efd7 --- /dev/null +++ b/spec/ci/features/builds_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe "Builds" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/builds/:id" do + before do + login_as :user + visit project_build_path(@project, @build) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + + describe "GET /:project/builds/:id/cancel" do + before do + login_as :user + @build.run! + visit cancel_project_build_path(@project, @build) + end + + it { page.should have_content 'canceled' } + it { page.should have_content 'Retry' } + end + + describe "POST /:project/builds/:id/retry" do + before do + login_as :user + @build.cancel! + visit project_build_path(@project, @build) + click_link 'Retry' + end + + it { page.should have_content 'pending' } + it { page.should have_content 'Cancel' } + end + + describe "Show page public accessible" do + before do + @project = FactoryGirl.create :public_project + @commit = FactoryGirl.create :commit, project: @project + @runner = FactoryGirl.create :specific_runner + @build = FactoryGirl.create :build, commit: @commit, runner: @runner + + stub_gitlab_calls + visit project_build_path(@project, @build) + end + + it { page.should have_content @commit.sha[0..7] } + end +end diff --git a/spec/ci/features/commits_spec.rb b/spec/ci/features/commits_spec.rb new file mode 100644 index 00000000000..202f05c516f --- /dev/null +++ b/spec/ci/features/commits_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe "Commits" do + context "Authenticated user" do + before do + login_as :user + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/commits/:sha" do + before do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + + describe "Cancel commit" do + it "cancels commit" do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + click_on "Cancel" + + page.should have_content "canceled" + end + end + + describe ".gitlab-ci.yml not found warning" do + it "does not show warning" do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + + page.should_not have_content ".gitlab-ci.yml not found in this commit" + end + + it "shows warning" do + @commit.push_data[:ci_yaml_file] = nil + @commit.save + + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + + page.should have_content ".gitlab-ci.yml not found in this commit" + end + end + end + + context "Public pages" do + before do + @project = FactoryGirl.create :public_project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/commits/:sha" do + before do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + end +end diff --git a/spec/ci/features/events_spec.rb b/spec/ci/features/events_spec.rb new file mode 100644 index 00000000000..77d1fba5769 --- /dev/null +++ b/spec/ci/features/events_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "Events" do + let(:project) { FactoryGirl.create :project } + let(:event) { FactoryGirl.create :admin_event, project: project } + + before do + login_as :user + end + + describe "GET /project/:id/events" do + before do + event + visit project_events_path(project) + end + + it { page.should have_content "Events" } + it { page.should have_content event.description } + end +end diff --git a/spec/ci/features/lint_spec.rb b/spec/ci/features/lint_spec.rb new file mode 100644 index 00000000000..0b3d4e099fb --- /dev/null +++ b/spec/ci/features/lint_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe "Lint" do + before do + login_as :user + end + + it "Yaml parsing", js: true do + content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + visit lint_path + fill_in "content", with: content + click_on "Validate" + within "table" do + page.should have_content("Job - rspec") + page.should have_content("Job - spinach") + page.should have_content("Deploy Job - staging") + page.should have_content("Deploy Job - production") + end + end + + it "Yaml parsing with error", js: true do + visit lint_path + fill_in "content", with: "" + click_on "Validate" + page.should have_content("Status: syntax is incorrect") + page.should have_content("Error: Please provide content of .gitlab-ci.yml") + end +end diff --git a/spec/ci/features/projects_spec.rb b/spec/ci/features/projects_spec.rb new file mode 100644 index 00000000000..3f21af92a2b --- /dev/null +++ b/spec/ci/features/projects_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe "Projects" do + before do + login_as :user + @project = FactoryGirl.create :project, name: "GitLab / gitlab-shell" + end + + describe "GET /projects", js: true do + before do + stub_js_gitlab_calls + visit projects_path + end + + it { page.should have_content "GitLab / gitlab-shell" } + it { page.should have_selector ".search input#search" } + end + + describe "GET /projects/:id" do + before do + visit project_path(@project) + end + + it { page.should have_content @project.name } + it { page.should have_content 'All commits' } + end + + describe "GET /projects/:id/edit" do + before do + visit edit_project_path(@project) + end + + it { page.should have_content @project.name } + it { page.should have_content 'Build Schedule' } + + it "updates configuration" do + fill_in 'Timeout', with: '70' + click_button 'Save changes' + + page.should have_content 'was successfully updated' + + find_field('Timeout').value.should eq '70' + end + end + + describe "GET /projects/:id/charts" do + before do + visit project_charts_path(@project) + end + + it { page.should have_content 'Overall' } + it { page.should have_content 'Builds chart for last week' } + it { page.should have_content 'Builds chart for last month' } + it { page.should have_content 'Builds chart for last year' } + it { page.should have_content 'Commit duration in minutes for last 30 commits' } + end +end diff --git a/spec/ci/features/runners_spec.rb b/spec/ci/features/runners_spec.rb new file mode 100644 index 00000000000..c41dc5b2e2e --- /dev/null +++ b/spec/ci/features/runners_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe "Runners" do + before do + login_as :user + end + + describe "specific runners" do + before do + @project = FactoryGirl.create :project + @project2 = FactoryGirl.create :project + stub_js_gitlab_calls + + # all projects should be authorized for user + Network.any_instance.stub(:projects).and_return([ + OpenStruct.new({id: @project.gitlab_id}), + OpenStruct.new({id: @project2.gitlab_id}) + ]) + + @shared_runner = FactoryGirl.create :shared_runner + @specific_runner = FactoryGirl.create :specific_runner + @specific_runner2 = FactoryGirl.create :specific_runner + @project.runners << @specific_runner + @project2.runners << @specific_runner2 + end + + it "places runners in right places" do + visit project_runners_path(@project) + page.find(".available-specific-runners").should have_content(@specific_runner2.display_name) + page.find(".activated-specific-runners").should have_content(@specific_runner.display_name) + page.find(".available-shared-runners").should have_content(@shared_runner.display_name) + end + + it "enables specific runner for project" do + visit project_runners_path(@project) + + within ".available-specific-runners" do + click_on "Enable for this project" + end + + page.find(".activated-specific-runners").should have_content(@specific_runner2.display_name) + end + + it "disables specific runner for project" do + @project2.runners << @specific_runner + + visit project_runners_path(@project) + + within ".activated-specific-runners" do + click_on "Disable for this project" + end + + page.find(".available-specific-runners").should have_content(@specific_runner.display_name) + end + + it "removes specific runner for project if this is last project for that runners" do + visit project_runners_path(@project) + + within ".activated-specific-runners" do + click_on "Remove runner" + end + + Runner.exists?(id: @specific_runner).should be_false + end + end + + describe "shared runners" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + end + + it "enables shared runners" do + visit project_runners_path(@project) + + click_on "Enable shared runners" + + @project.reload.shared_runners_enabled.should be_true + end + end + + describe "show page" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + @specific_runner = FactoryGirl.create :specific_runner + @project.runners << @specific_runner + end + + it "shows runner information" do + visit project_runners_path(@project) + + click_on @specific_runner.short_sha + + page.should have_content(@specific_runner.platform) + end + end +end diff --git a/spec/ci/features/triggers_spec.rb b/spec/ci/features/triggers_spec.rb new file mode 100644 index 00000000000..2076429383d --- /dev/null +++ b/spec/ci/features/triggers_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'Variables' do + before do + login_as :user + @project = FactoryGirl.create :project + stub_js_gitlab_calls + visit project_triggers_path(@project) + end + + context 'create a trigger' do + before do + click_on 'Add Trigger' + @project.triggers.count.should == 1 + end + + it 'contains trigger token' do + page.should have_content(@project.triggers.first.token) + end + + it 'revokes the trigger' do + click_on 'Revoke' + @project.triggers.count.should == 0 + end + end +end diff --git a/spec/ci/features/variables_spec.rb b/spec/ci/features/variables_spec.rb new file mode 100644 index 00000000000..2bb0d9dedde --- /dev/null +++ b/spec/ci/features/variables_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe "Variables" do + before do + login_as :user + end + + describe "specific runners" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + end + + it "creates variable", js: true do + visit project_variables_path(@project) + click_on "Add a variable" + fill_in "Key", with: "SECRET_KEY" + fill_in "Value", with: "SECRET_VALUE" + click_on "Save changes" + + page.should have_content("Variables were successfully updated.") + @project.variables.count.should == 1 + end + + end +end diff --git a/spec/ci/helpers/application_helper_spec.rb b/spec/ci/helpers/application_helper_spec.rb new file mode 100644 index 00000000000..c2b1058a8fa --- /dev/null +++ b/spec/ci/helpers/application_helper_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe ApplicationHelper do + describe "#duration_in_words" do + it "returns minutes and seconds" do + intervals_in_words = { + 100 => "1 minute 40 seconds", + 121 => "2 minutes 1 second", + 3721 => "62 minutes 1 second", + 0 => "0 seconds" + } + + intervals_in_words.each do |interval, expectation| + duration_in_words(Time.now + interval, Time.now).should == expectation + end + end + + it "calculates interval from now if there is no finished_at" do + duration_in_words(nil, Time.now - 5).should == "5 seconds" + end + end + + describe "#time_interval_in_words" do + it "returns minutes and seconds" do + intervals_in_words = { + 100 => "1 minute 40 seconds", + 121 => "2 minutes 1 second", + 3721 => "62 minutes 1 second", + 0 => "0 seconds" + } + + intervals_in_words.each do |interval, expectation| + time_interval_in_words(interval).should == expectation + end + end + end +end diff --git a/spec/ci/helpers/runners_helper_spec.rb b/spec/ci/helpers/runners_helper_spec.rb new file mode 100644 index 00000000000..02d497b40d2 --- /dev/null +++ b/spec/ci/helpers/runners_helper_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe RunnersHelper do + it "returns - not contacted yet" do + runner = FactoryGirl.build :runner + runner_status_icon(runner).should include("not connected yet") + end + + it "returns offline text" do + runner = FactoryGirl.build(:runner, contacted_at: 1.day.ago, active: true) + runner_status_icon(runner).should include("Runner is offline") + end + + it "returns online text" do + runner = FactoryGirl.build(:runner, contacted_at: 1.hour.ago, active: true) + runner_status_icon(runner).should include("Runner is online") + end +end diff --git a/spec/ci/helpers/user_helper_spec.rb b/spec/ci/helpers/user_helper_spec.rb new file mode 100644 index 00000000000..7215dc41a85 --- /dev/null +++ b/spec/ci/helpers/user_helper_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe UserHelper do + describe :user_avatar_url do + let (:user) { User.new({'avatar_url' => avatar_url}) } + + context 'no avatar' do + let (:avatar_url) { nil } + + it 'should return a generic avatar' do + user_avatar_url(user).should == 'ci/no_avatar.png' + end + end + + context 'plain gravatar' do + let (:base_url) { 'http://www.gravatar.com/avatar/abcdefgh' } + let (:avatar_url) { "#{base_url}?s=40&d=mm" } + + it 'should return gravatar with default size' do + user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" + end + + it 'should return gravatar with custom size' do + user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" + end + end + + context 'secure gravatar' do + let (:base_url) { 'https://secure.gravatar.com/avatar/abcdefgh' } + let (:avatar_url) { "#{base_url}?s=40&d=mm" } + + it 'should return gravatar with default size' do + user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" + end + + it 'should return gravatar with custom size' do + user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" + end + end + + context 'custom avatar' do + let (:avatar_url) { 'http://example.local/avatar.png' } + + it 'should return custom avatar' do + user_avatar_url(user).should == avatar_url + end + end + end +end diff --git a/spec/ci/helpers/user_sessions_helper_spec.rb b/spec/ci/helpers/user_sessions_helper_spec.rb new file mode 100644 index 00000000000..a2ab1f1e023 --- /dev/null +++ b/spec/ci/helpers/user_sessions_helper_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe UserSessionsHelper do + describe :generate_oauth_hmac do + let (:salt) { 'a' } + let (:salt2) { 'b' } + let (:return_to) { 'b' } + + it 'should return null if return_to is also null' do + generate_oauth_hmac(salt, nil).should be_nil + end + + it 'should return not null if return_to is also not null' do + generate_oauth_hmac(salt, return_to).should_not be_nil + end + + it 'should return different hmacs for different salts' do + secret1 = generate_oauth_hmac(salt, return_to) + secret2 = generate_oauth_hmac(salt2, return_to) + secret1.should_not eq(secret2) + end + end + + describe :generate_oauth_state do + let (:return_to) { 'b' } + + it 'should return null if return_to is also null' do + generate_oauth_state(nil).should be_nil + end + + it 'should return two different states for same return_to' do + state1 = generate_oauth_state(return_to) + state2 = generate_oauth_state(return_to) + state1.should_not eq(state2) + end + end + + describe :get_ouath_state_return_to do + let (:return_to) { 'a' } + let (:state) { generate_oauth_state(return_to) } + + it 'should return return_to' do + get_ouath_state_return_to(state).should eq(return_to) + end + end + + describe :is_oauth_state_valid? do + let (:return_to) { 'a' } + let (:state) { generate_oauth_state(return_to) } + let (:forged) { "forged#{state}" } + let (:invalid) { 'aa' } + let (:invalid2) { 'aa:bb' } + let (:invalid3) { 'aa:bb:' } + + it 'should validate oauth state' do + is_oauth_state_valid?(state).should be_true + end + + it 'should not validate forged state' do + is_oauth_state_valid?(forged).should be_false + end + + it 'should not validate invalid state' do + is_oauth_state_valid?(invalid).should be_false + is_oauth_state_valid?(invalid2).should be_false + is_oauth_state_valid?(invalid3).should be_false + end + end +end diff --git a/spec/ci/lib/ansi2html_spec.rb b/spec/ci/lib/ansi2html_spec.rb new file mode 100644 index 00000000000..aa60011685b --- /dev/null +++ b/spec/ci/lib/ansi2html_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe Ansi2html do + + it "prints non-ansi as-is" do + Ansi2html::convert("Hello").should == 'Hello' + end + + it "strips non-color-changing controll sequences" do + Ansi2html::convert("Hello \e[2Kworld").should == 'Hello world' + end + + it "prints simply red" do + Ansi2html::convert("\e[31mHello\e[0m").should == 'Hello' + end + + it "prints simply red without trailing reset" do + Ansi2html::convert("\e[31mHello").should == 'Hello' + end + + it "prints simply yellow" do + Ansi2html::convert("\e[33mHello\e[0m").should == 'Hello' + end + + it "prints default on blue" do + Ansi2html::convert("\e[39;44mHello").should == 'Hello' + end + + it "prints red on blue" do + Ansi2html::convert("\e[31;44mHello").should == 'Hello' + end + + it "resets colors after red on blue" do + Ansi2html::convert("\e[31;44mHello\e[0m world").should == 'Hello world' + end + + it "performs color change from red/blue to yellow/blue" do + Ansi2html::convert("\e[31;44mHello \e[33mworld").should == 'Hello world' + end + + it "performs color change from red/blue to yellow/green" do + Ansi2html::convert("\e[31;44mHello \e[33;42mworld").should == 'Hello world' + end + + it "performs color change from red/blue to reset to yellow/green" do + Ansi2html::convert("\e[31;44mHello\e[0m \e[33;42mworld").should == 'Hello world' + end + + it "ignores unsupported codes" do + Ansi2html::convert("\e[51mHello\e[0m").should == 'Hello' + end + + it "prints light red" do + Ansi2html::convert("\e[91mHello\e[0m").should == 'Hello' + end + + it "prints default on light red" do + Ansi2html::convert("\e[101mHello\e[0m").should == 'Hello' + end + + it "performs color change from red/blue to default/blue" do + Ansi2html::convert("\e[31;44mHello \e[39mworld").should == 'Hello world' + end + + it "performs color change from light red/blue to default/blue" do + Ansi2html::convert("\e[91;44mHello \e[39mworld").should == 'Hello world' + end + + it "prints bold text" do + Ansi2html::convert("\e[1mHello").should == 'Hello' + end + + it "resets bold text" do + Ansi2html::convert("\e[1mHello\e[21m world").should == 'Hello world' + Ansi2html::convert("\e[1mHello\e[22m world").should == 'Hello world' + end + + it "prints italic text" do + Ansi2html::convert("\e[3mHello").should == 'Hello' + end + + it "resets italic text" do + Ansi2html::convert("\e[3mHello\e[23m world").should == 'Hello world' + end + + it "prints underlined text" do + Ansi2html::convert("\e[4mHello").should == 'Hello' + end + + it "resets underlined text" do + Ansi2html::convert("\e[4mHello\e[24m world").should == 'Hello world' + end + + it "prints concealed text" do + Ansi2html::convert("\e[8mHello").should == 'Hello' + end + + it "resets concealed text" do + Ansi2html::convert("\e[8mHello\e[28m world").should == 'Hello world' + end + + it "prints crossed-out text" do + Ansi2html::convert("\e[9mHello").should == 'Hello' + end + + it "resets crossed-out text" do + Ansi2html::convert("\e[9mHello\e[29m world").should == 'Hello world' + end + + it "can print 256 xterm fg colors" do + Ansi2html::convert("\e[38;5;16mHello").should == 'Hello' + end + + it "can print 256 xterm fg colors on normal magenta background" do + Ansi2html::convert("\e[38;5;16;45mHello").should == 'Hello' + end + + it "can print 256 xterm bg colors" do + Ansi2html::convert("\e[48;5;240mHello").should == 'Hello' + end + + it "can print 256 xterm bg colors on normal magenta foreground" do + Ansi2html::convert("\e[48;5;16;35mHello").should == 'Hello' + end + + it "prints bold colored text vividly" do + Ansi2html::convert("\e[1;31mHello\e[0m").should == 'Hello' + end + + it "prints bold light colored text correctly" do + Ansi2html::convert("\e[1;91mHello\e[0m").should == 'Hello' + end +end diff --git a/spec/ci/lib/charts_spec.rb b/spec/ci/lib/charts_spec.rb new file mode 100644 index 00000000000..236cfc2a1f6 --- /dev/null +++ b/spec/ci/lib/charts_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe "Charts" do + + context "build_times" do + before do + @project = FactoryGirl.create(:project) + @commit = FactoryGirl.create(:commit, project: @project) + FactoryGirl.create(:build, commit: @commit) + end + + it 'should return build times in minutes' do + chart = Charts::BuildTime.new(@project) + chart.build_times.should == [2] + end + end +end diff --git a/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb b/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb new file mode 100644 index 00000000000..ed3d4e84054 --- /dev/null +++ b/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb @@ -0,0 +1,311 @@ +require 'spec_helper' + +describe GitlabCiYamlProcessor do + + describe "#builds_for_ref" do + let (:type) { 'test' } + + it "returns builds if no branch specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 + config_processor.builds_for_stage_and_ref(type, "master").first.should == { + stage: "test", + except: nil, + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: {}, + allow_failure: false + } + end + + it "does not return builds if only has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["deploy"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 + end + + it "does not return builds if only has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["/^deploy$/"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 + end + + it "returns builds if only has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["master"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 + end + + it "does not build tags" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", except: ["tags"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "0-1", true).size.should == 0 + end + + it "returns builds if only has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", type: type, only: ["master", "deploy"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 + end + + it "returns build only for specified type" do + + config = YAML.dump({ + before_script: ["pwd"], + build: {script: "build", type: "build", only: ["master", "deploy"]}, + rspec: {script: "rspec", type: type, only: ["master", "deploy"]}, + staging: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, + production: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("production", "deploy").size.should == 0 + config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 + config_processor.builds_for_stage_and_ref("deploy", "deploy").size.should == 2 + end + end + + describe "Image and service handling" do + it "returns image and service when defined" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 + config_processor.builds_for_stage_and_ref("test", "master").first.should == { + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.1", + services: ["mysql"] + }, + allow_failure: false + } + end + + it "returns image and service when overridden for job" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: {image: "ruby:2.5", services: ["postgresql"], script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 + config_processor.builds_for_stage_and_ref("test", "master").first.should == { + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.5", + services: ["postgresql"] + }, + allow_failure: false + } + end + end + + describe "Variables" do + it "returns variables when defined" do + variables = { + var1: "value1", + var2: "value2", + } + config = YAML.dump({ + variables: variables, + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + config_processor.variables.should == variables + end + end + + describe "Error handling" do + it "indicates that object is invalid" do + expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) + end + + it "returns errors if tags parameter is invalid" do + config = YAML.dump({rspec: {script: "test", tags: "mysql"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") + end + + it "returns errors if before_script parameter is invalid" do + config = YAML.dump({before_script: "bundle update", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") + end + + it "returns errors if image parameter is invalid" do + config = YAML.dump({image: ["test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") + end + + it "returns errors if job image parameter is invalid" do + config = YAML.dump({rspec: {script: "test", image: ["test"]}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") + end + + it "returns errors if services parameter is not an array" do + config = YAML.dump({services: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if services parameter is not an array of strings" do + config = YAML.dump({services: [10, "test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if job services parameter is not an array" do + config = YAML.dump({rspec: {script: "test", services: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if job services parameter is not an array of strings" do + config = YAML.dump({rspec: {script: "test", services: [10, "test"]}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if there are unknown parameters" do + config = YAML.dump({extra: "bundle update"}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do + config = YAML.dump({extra: {services: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there is no any jobs defined" do + config = YAML.dump({before_script: ["bundle update"]}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") + end + + it "returns errors if job allow_failure parameter is not an boolean" do + config = YAML.dump({rspec: {script: "test", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") + end + + it "returns errors if job stage is not a string" do + config = YAML.dump({rspec: {script: "test", type: 1, allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a pre-defined stage" do + config = YAML.dump({rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a defined stage" do + config = YAML.dump({types: ["build", "test"], rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") + end + + it "returns errors if stages is not an array" do + config = YAML.dump({types: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if stages is not an array of strings" do + config = YAML.dump({types: [true, "test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if variables is not a map" do + config = YAML.dump({variables: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end + + it "returns errors if variables is not a map of key-valued strings" do + config = YAML.dump({variables: {test: false}, rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end + end +end diff --git a/spec/ci/lib/upgrader_spec.rb b/spec/ci/lib/upgrader_spec.rb new file mode 100644 index 00000000000..40a98307ad2 --- /dev/null +++ b/spec/ci/lib/upgrader_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Upgrader do + let(:upgrader) { Upgrader.new } + let(:current_version) { GitlabCi::VERSION } + + describe 'current_version_raw' do + it { upgrader.current_version_raw.should == current_version } + end + + describe 'latest_version?' do + it 'should be true if newest version' do + upgrader.stub(latest_version_raw: current_version) + upgrader.latest_version?.should be_true + end + end + + describe 'latest_version_raw' do + it 'should be latest version for GitlabCI 3' do + allow(upgrader).to receive(:current_version_raw).and_return('3.0.0') + expect(upgrader.latest_version_raw).to eq('v3.2.0') + end + + it 'should get the latest version from tags' do + allow(upgrader).to receive(:fetch_git_tags).and_return([ + '1b5bee25b51724214c7a3307ef94027ab93ec982 refs/tags/v7.8.1', + '424cb42e35947fa304ef83eb211ffc657e31aef3 refs/tags/v7.8.1^{}', + '498e5ba63be1bb99e30c6e720902d864aac4413c refs/tags/v7.9.0.rc1', + '96aaf45ae93bd43e8b3f5d4d353d64d3cbe1e63b refs/tags/v7.9.0.rc1^{}', + '94aaf45ae93bd43e8b3fad4a353d64d3cbe1e62b refs/tags/v7.1.0', + '96aaf45ae93ba13e8b3f5d4d353d64d3cbe1e251 refs/tags/v7.1.0^{}', + '29359d64442bf54b4ca1d8b439fd9e5f9cd83252 refs/tags/v7.10.0', + '4d9213a6378bff43a69ae099702fb81e29335e7a refs/tags/v7.10.0^{}', + '1d93e1626bda93622ca7a2ae2825e2e94dabf3c6 refs/tags/v7.12.0', + '0188a9d1c2efdc52bfad36ad303686be997de713 refs/tags/v7.12.0^{}']) + expect(upgrader.latest_version_raw).to eq("v7.12.0") + end + end +end diff --git a/spec/ci/mailers/notify_spec.rb b/spec/ci/mailers/notify_spec.rb new file mode 100644 index 00000000000..6a2c845cd0e --- /dev/null +++ b/spec/ci/mailers/notify_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Notify do + include EmailSpec::Helpers + include EmailSpec::Matchers + + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe 'build success' do + subject { Notify.build_success_email(@build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build success for/ + end + + it 'contains name of project' do + should have_body_text /build successful/ + end + end + + describe 'build fail' do + subject { Notify.build_fail_email(@build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build failed for/ + end + + it 'contains name of project' do + should have_body_text /build failed/ + end + end +end diff --git a/spec/ci/models/build_spec.rb b/spec/ci/models/build_spec.rb new file mode 100644 index 00000000000..733398176bf --- /dev/null +++ b/spec/ci/models/build_spec.rb @@ -0,0 +1,350 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# + +require 'spec_helper' + +describe Build do + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit } + + it { should belong_to(:commit) } + it { should validate_presence_of :status } + + it { should respond_to :success? } + it { should respond_to :failed? } + it { should respond_to :running? } + it { should respond_to :pending? } + it { should respond_to :trace_html } + + describe :first_pending do + let(:first) { FactoryGirl.create :build, commit: commit, status: 'pending', created_at: Date.yesterday } + let(:second) { FactoryGirl.create :build, commit: commit, status: 'pending' } + before { first; second } + subject { Build.first_pending } + + it { should be_a(Build) } + it('returns with the first pending build') { should eq(first) } + end + + describe :create_from do + before do + build.status = 'success' + build.save + end + let(:create_from_build) { Build.create_from build } + + it ('there should be a pending task') do + expect(Build.pending.count(:all)).to eq 0 + create_from_build + expect(Build.pending.count(:all)).to be > 0 + end + end + + describe :started? do + subject { build.started? } + + context 'without started_at' do + before { build.started_at = nil } + + it { should be_false } + end + + %w(running success failed).each do |status| + context "if build status is #{status}" do + before { build.status = status } + + it { should be_true } + end + end + + %w(pending canceled).each do |status| + context "if build status is #{status}" do + before { build.status = status } + + it { should be_false } + end + end + end + + describe :active? do + subject { build.active? } + + %w(pending running).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_true } + end + end + + %w(success failed canceled).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_false } + end + end + end + + describe :complete? do + subject { build.complete? } + + %w(success failed canceled).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_true } + end + end + + %w(pending running).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_false } + end + end + end + + describe :ignored? do + subject { build.ignored? } + + context 'if build is not allowed to fail' do + before { build.allow_failure = false } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { should be_false } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { should be_false } + end + end + + context 'if build is allowed to fail' do + before { build.allow_failure = true } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { should be_false } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { should be_true } + end + end + end + + describe :trace do + subject { build.trace_html } + + it { should be_empty } + + context 'if build.trace contains text' do + let(:text) { 'example output' } + before { build.trace = text } + + it { should include(text) } + it { should have_at_least(text.length).items } + end + end + + describe :timeout do + subject { build.timeout } + + it { should eq(commit.project.timeout) } + end + + describe :duration do + subject { build.duration } + + it { should eq(120.0) } + + context 'if the building process has not started yet' do + before do + build.started_at = nil + build.finished_at = nil + end + + it { should be_nil } + end + + context 'if the building process has started' do + before do + build.started_at = Time.now - 1.minute + build.finished_at = nil + end + + it { should be_a(Float) } + it { should > 0.0 } + end + end + + describe :options do + let(:options) { + { + :image => "ruby:2.1", + :services => [ + "postgres" + ] + } + } + + subject { build.options } + it { should eq(options) } + end + + describe :ref do + subject { build.ref } + + it { should eq(commit.ref) } + end + + describe :sha do + subject { build.sha } + + it { should eq(commit.sha) } + end + + describe :short_sha do + subject { build.short_sha } + + it { should eq(commit.short_sha) } + end + + describe :before_sha do + subject { build.before_sha } + + it { should eq(commit.before_sha) } + end + + describe :allow_git_fetch do + subject { build.allow_git_fetch } + + it { should eq(project.allow_git_fetch) } + end + + describe :project do + subject { build.project } + + it { should eq(commit.project) } + end + + describe :project_id do + subject { build.project_id } + + it { should eq(commit.project_id) } + end + + describe :project_name do + subject { build.project_name } + + it { should eq(project.name) } + end + + describe :repo_url do + subject { build.repo_url } + + it { should eq(project.repo_url_with_auth) } + end + + describe :extract_coverage do + context 'valid content & regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { should eq(98.29) } + end + + context 'valid content & bad regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } + + it { should be_nil } + end + + context 'no coverage content & regex' do + subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } + + it { should be_nil } + end + + context 'multiple results in content & regex' do + subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { should eq(98.29) } + end + end + + describe :variables do + context 'returns variables' do + subject { build.variables } + + let(:variables) { + [ + {key: :DB_NAME, value: 'postgres', public: true} + ] + } + + it { should eq(variables) } + + context 'and secure variables' do + let(:secure_variables) { + [ + {key: 'SECRET_KEY', value: 'secret_value', public: false} + ] + } + + before do + build.project.variables << Variable.new(key: 'SECRET_KEY', value: 'secret_value') + end + + it { should eq(variables + secure_variables) } + + context 'and trigger variables' do + let(:trigger) { FactoryGirl.create :trigger, project: project } + let(:trigger_request) { FactoryGirl.create :trigger_request_with_variables, commit: commit, trigger: trigger } + let(:trigger_variables) { + [ + {key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false} + ] + } + + before do + build.trigger_request = trigger_request + end + + it { should eq(variables + secure_variables + trigger_variables) } + end + end + end + end +end diff --git a/spec/ci/models/commit_spec.rb b/spec/ci/models/commit_spec.rb new file mode 100644 index 00000000000..6f644d20aaf --- /dev/null +++ b/spec/ci/models/commit_spec.rb @@ -0,0 +1,264 @@ +# == Schema Information +# +# Table name: commits +# +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# + +require 'spec_helper' + +describe Commit do + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:commit_with_project) { FactoryGirl.create :commit, project: project } + let(:config_processor) { GitlabCiYamlProcessor.new(gitlab_ci_yaml) } + + it { should belong_to(:project) } + it { should have_many(:builds) } + it { should validate_presence_of :before_sha } + it { should validate_presence_of :sha } + it { should validate_presence_of :ref } + it { should validate_presence_of :push_data } + + it { should respond_to :git_author_name } + it { should respond_to :git_author_email } + it { should respond_to :short_sha } + + describe :last_build do + subject { commit.last_build } + before do + @first = FactoryGirl.create :build, commit: commit, created_at: Date.yesterday + @second = FactoryGirl.create :build, commit: commit + end + + it { should be_a(Build) } + it('returns with the most recently created build') { should eq(@second) } + end + + describe :retry do + before do + @first = FactoryGirl.create :build, commit: commit, created_at: Date.yesterday + @second = FactoryGirl.create :build, commit: commit + end + + it "creates new build" do + expect(commit.builds.count(:all)).to eq 2 + commit.retry + expect(commit.builds.count(:all)).to eq 3 + end + end + + describe :project_recipients do + + context 'always sending notification' do + it 'should return commit_pusher_email as only recipient when no additional recipients are given' do + project = FactoryGirl.create :project, + email_add_pusher: true, + email_recipients: '' + commit = FactoryGirl.create :commit, project: project + expected = 'commit_pusher_email' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == [expected] + end + + it 'should return commit_pusher_email and additional recipients' do + project = FactoryGirl.create :project, + email_add_pusher: true, + email_recipients: 'rec1 rec2' + commit = FactoryGirl.create :commit, project: project + expected = 'commit_pusher_email' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == ['rec1', 'rec2', expected] + end + + it 'should return recipients' do + project = FactoryGirl.create :project, + email_add_pusher: false, + email_recipients: 'rec1 rec2' + commit = FactoryGirl.create :commit, project: project + commit.project_recipients.should == ['rec1', 'rec2'] + end + + it 'should return unique recipients only' do + project = FactoryGirl.create :project, + email_add_pusher: true, + email_recipients: 'rec1 rec1 rec2' + commit = FactoryGirl.create :commit, project: project + expected = 'rec2' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == ['rec1', 'rec2'] + end + end + end + + describe :valid_commit_sha do + context 'commit.sha can not start with 00000000' do + before do + commit.sha = '0' * 40 + commit.valid_commit_sha + end + + it('commit errors should not be empty') { commit.errors.should_not be_empty } + end + end + + describe :compare? do + subject { commit_with_project.compare? } + + context 'if commit.before_sha are not nil' do + it { should be_true } + end + end + + describe :short_sha do + subject { commit.short_before_sha } + + it { should have(8).items } + it { commit.before_sha.should start_with(subject) } + end + + describe :short_sha do + subject { commit.short_sha } + + it { should have(8).items } + it { commit.sha.should start_with(subject) } + end + + describe :create_next_builds do + before do + commit.stub(:config_processor).and_return(config_processor) + end + + it "creates builds for next type" do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_next_builds(nil).should be_true + commit.builds.reload + commit.builds.size.should == 4 + + commit.create_next_builds(nil).should be_true + commit.builds.reload + commit.builds.size.should == 5 + + commit.create_next_builds(nil).should be_false + end + end + + describe :create_builds do + before do + commit.stub(:config_processor).and_return(config_processor) + end + + it 'creates builds' do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + end + + context 'for build triggers' do + let(:trigger) { FactoryGirl.create :trigger, project: project } + let(:trigger_request) { FactoryGirl.create :trigger_request, commit: commit, trigger: trigger } + + it 'creates builds' do + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + end + + it 'rebuilds commit' do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 4 + end + + it 'creates next builds' do + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_next_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 4 + end + + context 'for [ci skip]' do + before do + commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]' + commit.save + end + + it 'rebuilds commit' do + commit.status.should == 'skipped' + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + commit.status.should == 'pending' + end + end + end + end + + describe "#finished_at" do + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + + it "returns finished_at of latest build" do + build = FactoryGirl.create :build, commit: commit, finished_at: Time.now - 60 + build1 = FactoryGirl.create :build, commit: commit, finished_at: Time.now - 120 + + commit.finished_at.to_i.should == build.finished_at.to_i + end + + it "returns nil if there is no finished build" do + build = FactoryGirl.create :not_started_build, commit: commit + + commit.finished_at.should be_nil + end + end + + describe "coverage" do + let(:project) { FactoryGirl.create :project, coverage_regex: "/.*/" } + let(:commit) { FactoryGirl.create :commit, project: project } + + it "calculates average when there are two builds with coverage" do + FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there are two builds with coverage and one with nil" do + FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit + FactoryGirl.create :build, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there are two builds with coverage and one is retried" do + FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :build, name: "rubocop", coverage: 30, commit: commit + FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there is one build without coverage" do + FactoryGirl.create :build, commit: commit + commit.coverage.should be_nil + end + end +end diff --git a/spec/ci/models/mail_service_spec.rb b/spec/ci/models/mail_service_spec.rb new file mode 100644 index 00000000000..d66a6591f8f --- /dev/null +++ b/spec/ci/models/mail_service_spec.rb @@ -0,0 +1,184 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe MailService do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + context "active" do + before do + subject.active = true + end + end + end + + describe 'Sends email for' do + let(:mail) { MailService.new } + + describe 'failed build' do + let(:project) { FactoryGirl.create(:project, email_add_pusher: true) } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :failed, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_fail_email).with(build.id, email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + end + end + + describe 'successfull build' do + let(:project) { FactoryGirl.create(:project, email_add_pusher: true, email_only_broken_builds: false) } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successfull build and project has email_recipients' do + let(:project) { + FactoryGirl.create(:project, + email_add_pusher: true, + email_only_broken_builds: false, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + should_email("jeroen@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successful build and notify only broken builds' do + let(:project) { + FactoryGirl.create(:project, + email_add_pusher: true, + email_only_broken_builds: true, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email(commit.git_author_email) + should_email("jeroen@example.com") + mail.execute(build) if mail.can_execute?(build) + end + + def should_email(email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successful build and can test service' do + let(:project) { + FactoryGirl.create(:project, + email_add_pusher: true, + email_only_broken_builds: false, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + build + end + + it do + mail.can_test?.should == true + end + end + + describe 'retried build should not receive email' do + let(:project) { + FactoryGirl.create(:project, + email_add_pusher: true, + email_only_broken_builds: true, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:build) { FactoryGirl.create(:build, status: :failed, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + Build.retry(build) + should_email(commit.git_author_email) + should_email("jeroen@example.com") + mail.execute(build) if mail.can_execute?(build) + end + + def should_email(email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + end +end diff --git a/spec/ci/models/network_spec.rb b/spec/ci/models/network_spec.rb new file mode 100644 index 00000000000..b80adba5b08 --- /dev/null +++ b/spec/ci/models/network_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Network do + let(:network) { Network.new } + + describe :enable_ci do + subject { network.enable_ci '', '', '' } + + context 'on success' do + before do + response = double + response.stub(:code) { 200 } + network.class.stub(:put) { response } + end + + it { should be_true } + end + + context 'on failure' do + before do + response = double + response.stub(:code) { 404 } + network.class.stub(:put) { response } + end + + it { should be_nil } + end + end + + describe :disable_ci do + let(:response) { double } + subject { network.disable_ci '', '' } + + context 'on success' do + let(:parsed_response) { 'parsed' } + before do + response.stub(:code) { 200 } + response.stub(:parsed_response) { parsed_response } + network.class.stub(:delete) { response } + end + + it { should equal(parsed_response) } + end + + context 'on failure' do + before do + response.stub(:code) { 404 } + network.class.stub(:delete) { response } + end + + it { should be_nil } + end + end +end diff --git a/spec/ci/models/project_services/hip_chat_message_spec.rb b/spec/ci/models/project_services/hip_chat_message_spec.rb new file mode 100644 index 00000000000..f1ad875ebcf --- /dev/null +++ b/spec/ci/models/project_services/hip_chat_message_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe HipChatMessage do + subject { HipChatMessage.new(build) } + + let(:project) { FactoryGirl.create(:project) } + + context "One build" do + let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + + let(:build) do + commit.create_builds + commit.builds.first + end + + context 'when build succeeds' do + it 'returns a successful message' do + build.update(status: "success") + + expect( subject.status_color ).to eq 'green' + expect( subject.notify? ).to be_false + expect( subject.to_s ).to match(/Build '[^']+' #\d+/) + expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) + end + end + + context 'when build fails' do + it 'returns a failure message' do + build.update(status: "failed") + + expect( subject.status_color ).to eq 'red' + expect( subject.notify? ).to be_true + expect( subject.to_s ).to match(/Build '[^']+' #\d+/) + expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) + end + end + end + + context "Several builds" do + let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + + let(:build) do + commit.builds.first + end + + context 'when all matrix builds succeed' do + it 'returns a successful message' do + commit.create_builds + commit.builds.update_all(status: "success") + commit.reload + + expect( subject.status_color ).to eq 'green' + expect( subject.notify? ).to be_false + expect( subject.to_s ).to match(/Commit #\d+/) + expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) + end + end + + context 'when at least one matrix build fails' do + it 'returns a failure message' do + commit.create_builds + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + expect( subject.status_color ).to eq 'red' + expect( subject.notify? ).to be_true + expect( subject.to_s ).to match(/Commit #\d+/) + expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) + end + end + end +end diff --git a/spec/ci/models/project_services/hip_chat_service_spec.rb b/spec/ci/models/project_services/hip_chat_service_spec.rb new file mode 100644 index 00000000000..37ce4905af8 --- /dev/null +++ b/spec/ci/models/project_services/hip_chat_service_spec.rb @@ -0,0 +1,75 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + + +require 'spec_helper' + +describe HipChatService do + + describe "Validations" do + + context "active" do + before do + subject.active = true + end + + it { should validate_presence_of :hipchat_room } + it { should validate_presence_of :hipchat_token } + + end + end + + describe "Execute" do + + let(:service) { HipChatService.new } + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } + + before do + service.stub( + project: project, + project_id: project.id, + notify_only_broken_builds: false, + hipchat_room: 123, + hipchat_token: 'a1b2c3d4e5f6' + ) + + WebMock.stub_request(:post, api_url) + end + + + it "should call the HipChat API" do + service.execute(build) + HipChatNotifierWorker.drain + + expect( WebMock ).to have_requested(:post, api_url).once + end + + it "calls the worker with expected arguments" do + expect( HipChatNotifierWorker ).to receive(:perform_async) \ + .with(an_instance_of(String), hash_including( + token: 'a1b2c3d4e5f6', + room: 123, + server: 'https://api.hipchat.com', + color: 'red', + notify: true + )) + + service.execute(build) + end + end +end + diff --git a/spec/ci/models/project_services/slack_message_spec.rb b/spec/ci/models/project_services/slack_message_spec.rb new file mode 100644 index 00000000000..88e0f373206 --- /dev/null +++ b/spec/ci/models/project_services/slack_message_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' + +describe SlackMessage do + subject { SlackMessage.new(commit) } + + let(:project) { FactoryGirl.create :project } + + context "One build" do + let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + + let(:build) do + commit.create_builds + commit.builds.first + end + + context 'when build succeeded' do + let(:color) { 'good' } + + it 'returns a message with succeeded build' do + build.update(status: "success") + + subject.color.should == color + subject.fallback.should include('Build') + subject.fallback.should include("\##{build.id}") + subject.fallback.should include('succeeded') + subject.attachments.first[:fields].should be_empty + end + end + + context 'when build failed' do + let(:color) { 'danger' } + + it 'returns a message with failed build' do + build.update(status: "failed") + + subject.color.should == color + subject.fallback.should include('Build') + subject.fallback.should include("\##{build.id}") + subject.fallback.should include('failed') + subject.attachments.first[:fields].should be_empty + end + end + end + + context "Several builds" do + let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + + context 'when all matrix builds succeeded' do + let(:color) { 'good' } + + it 'returns a message with success' do + commit.create_builds + commit.builds.update_all(status: "success") + commit.reload + + subject.color.should == color + subject.fallback.should include('Commit') + subject.fallback.should include("\##{commit.id}") + subject.fallback.should include('succeeded') + subject.attachments.first[:fields].should be_empty + end + end + + context 'when one of matrix builds failed' do + let(:color) { 'danger' } + + it 'returns a message with information about failed build' do + commit.create_builds + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + subject.color.should == color + subject.fallback.should include('Commit') + subject.fallback.should include("\##{commit.id}") + subject.fallback.should include('failed') + subject.attachments.first[:fields].size.should == 1 + subject.attachments.first[:fields].first[:title].should == second_build.name + subject.attachments.first[:fields].first[:value].should include("\##{second_build.id}") + end + end + end +end diff --git a/spec/ci/models/project_services/slack_service_spec.rb b/spec/ci/models/project_services/slack_service_spec.rb new file mode 100644 index 00000000000..e1c14281274 --- /dev/null +++ b/spec/ci/models/project_services/slack_service_spec.rb @@ -0,0 +1,58 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe SlackService do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + context "active" do + before do + subject.active = true + end + + it { should validate_presence_of :webhook } + end + end + + describe "Execute" do + let(:slack) { SlackService.new } + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } + let(:notify_only_broken_builds) { false } + + before do + slack.stub( + project: project, + project_id: project.id, + webhook: webhook_url, + notify_only_broken_builds: notify_only_broken_builds + ) + + WebMock.stub_request(:post, webhook_url) + end + + it "should call Slack API" do + slack.execute(build) + SlackNotifierWorker.drain + + WebMock.should have_requested(:post, webhook_url).once + end + end +end diff --git a/spec/ci/models/project_spec.rb b/spec/ci/models/project_spec.rb new file mode 100644 index 00000000000..aa76b99154b --- /dev/null +++ b/spec/ci/models/project_spec.rb @@ -0,0 +1,185 @@ +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) not null +# timeout :integer default(3600), not null +# created_at :datetime +# updated_at :datetime +# token :string(255) +# default_ref :string(255) +# path :string(255) +# always_build :boolean default(FALSE), not null +# polling_interval :integer +# public :boolean default(FALSE), not null +# ssh_url_to_repo :string(255) +# gitlab_id :integer +# allow_git_fetch :boolean default(TRUE), not null +# email_recipients :string(255) default(""), not null +# email_add_pusher :boolean default(TRUE), not null +# email_only_broken_builds :boolean default(TRUE), not null +# skip_refs :string(255) +# coverage_regex :string(255) +# shared_runners_enabled :boolean default(FALSE) +# generated_yaml_config :text +# + +require 'spec_helper' + +describe Project do + subject { FactoryGirl.build :project } + + it { should have_many(:commits) } + + it { should validate_presence_of :name } + it { should validate_presence_of :timeout } + it { should validate_presence_of :default_ref } + + describe 'before_validation' do + it 'should set an random token if none provided' do + project = FactoryGirl.create :project_without_token + project.token.should_not == "" + end + + it 'should not set an random toke if one provided' do + project = FactoryGirl.create :project + project.token.should == "iPWx6WM4lhHNedGfBpPJNP" + end + end + + describe "ordered_by_last_commit_date" do + it "returns ordered projects" do + newest_project = FactoryGirl.create :project + oldest_project = FactoryGirl.create :project + project_without_commits = FactoryGirl.create :project + + FactoryGirl.create :commit, committed_at: 1.hour.ago, project: newest_project + FactoryGirl.create :commit, committed_at: 2.hour.ago, project: oldest_project + + Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits] + end + end + + context :valid_project do + let(:project) { FactoryGirl.create :project } + + context :project_with_commit_and_builds do + before do + commit = FactoryGirl.create(:commit, project: project) + FactoryGirl.create(:build, commit: commit) + end + + it { project.status.should == 'pending' } + it { project.last_commit.should be_kind_of(Commit) } + it { project.human_status.should == 'pending' } + end + end + + describe '#email_notification?' do + it do + project = FactoryGirl.create :project, email_add_pusher: true + project.email_notification?.should == true + end + + it do + project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: 'test tesft' + project.email_notification?.should == true + end + + it do + project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: '' + project.email_notification?.should == false + end + end + + describe '#broken_or_success?' do + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(true) + project.stub(:success?).and_return(true) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(true) + project.stub(:success?).and_return(false) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(false) + project.stub(:success?).and_return(true) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(false) + project.stub(:success?).and_return(false) + project.broken_or_success?.should == false + } + end + + describe 'Project.parse' do + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + let(:parsed_project) { Project.parse(project_dump) } + + + it { parsed_project.should be_valid } + it { parsed_project.should be_kind_of(Project) } + it { parsed_project.name.should eq("GitLab / api.gitlab.org") } + it { parsed_project.gitlab_id.should eq(189) } + it { parsed_project.gitlab_url.should eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } + + it "parses plain hash" do + Project.parse(project_dump).name.should eq("GitLab / api.gitlab.org") + end + end + + describe :repo_url_with_auth do + let(:project) { FactoryGirl.create :project } + subject { project.repo_url_with_auth } + + it { should be_a(String) } + it { should end_with(".git") } + it { should start_with(project.gitlab_url[0..6]) } + it { should include(project.token) } + it { should include('gitlab-ci-token') } + it { should include(project.gitlab_url[7..-1]) } + end + + describe :search do + let!(:project) { FactoryGirl.create(:project, name: "foo") } + + it { Project.search('fo').should include(project) } + it { Project.search('bar').should be_empty } + end + + describe :any_runners do + it "there are no runners available" do + project = FactoryGirl.create(:project) + project.any_runners?.should be_false + end + + it "there is a specific runner" do + project = FactoryGirl.create(:project) + project.runners << FactoryGirl.create(:specific_runner) + project.any_runners?.should be_true + end + + it "there is a shared runner" do + project = FactoryGirl.create(:project, shared_runners_enabled: true) + FactoryGirl.create(:shared_runner) + project.any_runners?.should be_true + end + + it "there is a shared runner, but they are prohibited to use" do + project = FactoryGirl.create(:project) + FactoryGirl.create(:shared_runner) + project.any_runners?.should be_false + end + end +end diff --git a/spec/ci/models/runner_project_spec.rb b/spec/ci/models/runner_project_spec.rb new file mode 100644 index 00000000000..cbefb24705a --- /dev/null +++ b/spec/ci/models/runner_project_spec.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: runner_projects +# +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +require 'spec_helper' + +describe RunnerProject do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/ci/models/runner_spec.rb b/spec/ci/models/runner_spec.rb new file mode 100644 index 00000000000..6902c0a94e6 --- /dev/null +++ b/spec/ci/models/runner_spec.rb @@ -0,0 +1,70 @@ +# == Schema Information +# +# Table name: runners +# +# id :integer not null, primary key +# token :string(255) +# created_at :datetime +# updated_at :datetime +# description :string(255) +# contacted_at :datetime +# active :boolean default(TRUE), not null +# is_shared :boolean default(FALSE) +# name :string(255) +# version :string(255) +# revision :string(255) +# platform :string(255) +# architecture :string(255) +# + +require 'spec_helper' + +describe Runner do + describe '#display_name' do + it 'should return the description if it has a value' do + runner = FactoryGirl.build(:runner, description: 'Linux/Ruby-1.9.3-p448') + expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448' + end + + it 'should return the token if it does not have a description' do + runner = FactoryGirl.create(:runner) + expect(runner.display_name).to eq runner.description + end + + it 'should return the token if the description is an empty string' do + runner = FactoryGirl.build(:runner, description: '') + expect(runner.display_name).to eq runner.token + end + end + + describe :assign_to do + let!(:project) { FactoryGirl.create :project } + let!(:shared_runner) { FactoryGirl.create(:shared_runner) } + + before { shared_runner.assign_to(project) } + + it { shared_runner.should be_specific } + it { shared_runner.projects.should == [project] } + it { shared_runner.only_for?(project).should be_true } + end + + describe "belongs_to_one_project?" do + it "returns false if there are two projects runner assigned to" do + runner = FactoryGirl.create(:specific_runner) + project = FactoryGirl.create(:project) + project1 = FactoryGirl.create(:project) + project.runners << runner + project1.runners << runner + + runner.belongs_to_one_project?.should be_false + end + + it "returns true" do + runner = FactoryGirl.create(:specific_runner) + project = FactoryGirl.create(:project) + project.runners << runner + + runner.belongs_to_one_project?.should be_true + end + end +end diff --git a/spec/ci/models/service_spec.rb b/spec/ci/models/service_spec.rb new file mode 100644 index 00000000000..22a49e10a6c --- /dev/null +++ b/spec/ci/models/service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe Service do + + describe "Associations" do + it { should belong_to :project } + end + + describe "Mass assignment" do + end + + describe "Test Button" do + before do + @service = Service.new + end + + describe "Testable" do + let (:project) { FactoryGirl.create :project } + let (:commit) { FactoryGirl.create :commit, project: project } + let (:build) { FactoryGirl.create :build, commit: commit } + + before do + @service.stub( + project: project + ) + build + @testable = @service.can_test? + end + + describe :can_test do + it { @testable.should == true } + end + end + end +end diff --git a/spec/ci/models/trigger_spec.rb b/spec/ci/models/trigger_spec.rb new file mode 100644 index 00000000000..bba638e7817 --- /dev/null +++ b/spec/ci/models/trigger_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Trigger do + let(:project) { FactoryGirl.create :project } + + describe 'before_validation' do + it 'should set an random token if none provided' do + trigger = FactoryGirl.create :trigger_without_token, project: project + trigger.token.should_not be_nil + end + + it 'should not set an random token if one provided' do + trigger = FactoryGirl.create :trigger, project: project + trigger.token.should == 'token' + end + end +end diff --git a/spec/ci/models/user_spec.rb b/spec/ci/models/user_spec.rb new file mode 100644 index 00000000000..73a7a7d5fbc --- /dev/null +++ b/spec/ci/models/user_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +describe User do + + describe "has_developer_access?" do + before do + @user = User.new({}) + end + + let(:project_with_owner_access) do + { + "name" => "gitlab-shell", + "permissions" => { + "project_access" => { + "access_level"=> 10, + "notification_level" => 3 + }, + "group_access" => { + "access_level" => 50, + "notification_level" => 3 + } + } + } + end + + let(:project_with_reporter_access) do + { + "name" => "gitlab-shell", + "permissions" => { + "project_access" => { + "access_level" => 20, + "notification_level" => 3 + }, + "group_access" => { + "access_level" => 10, + "notification_level" => 3 + } + } + } + end + + it "returns false for reporter" do + @user.stub(:project_info).and_return(project_with_reporter_access) + + @user.has_developer_access?(1).should be_false + end + + it "returns true for owner" do + @user.stub(:project_info).and_return(project_with_owner_access) + + @user.has_developer_access?(1).should be_true + end + end + + describe "authorized_projects" do + let (:user) { User.new({}) } + + before do + FactoryGirl.create :project, gitlab_id: 1 + FactoryGirl.create :project, gitlab_id: 2 + gitlab_project = OpenStruct.new({id: 1}) + gitlab_project1 = OpenStruct.new({id: 2}) + User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + end + + it "returns projects" do + User.any_instance.stub(:can_manage_project?).and_return(true) + + user.authorized_projects.count.should == 2 + end + + it "empty list if user miss manage permission" do + User.any_instance.stub(:can_manage_project?).and_return(false) + + user.authorized_projects.count.should == 0 + end + end + + describe "authorized_runners" do + it "returns authorized runners" do + project = FactoryGirl.create :project, gitlab_id: 1 + project1 = FactoryGirl.create :project, gitlab_id: 2 + gitlab_project = OpenStruct.new({id: 1}) + gitlab_project1 = OpenStruct.new({id: 2}) + User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + User.any_instance.stub(:can_manage_project?).and_return(true) + user = User.new({}) + + runner = FactoryGirl.create :specific_runner + runner1 = FactoryGirl.create :specific_runner + runner2 = FactoryGirl.create :specific_runner + + project.runners << runner + project1.runners << runner1 + + user.authorized_runners.should include(runner, runner1) + user.authorized_runners.should_not include(runner2) + end + end +end diff --git a/spec/ci/models/variable_spec.rb b/spec/ci/models/variable_spec.rb new file mode 100644 index 00000000000..4575115ccfb --- /dev/null +++ b/spec/ci/models/variable_spec.rb @@ -0,0 +1,44 @@ +# == Schema Information +# +# Table name: variables +# +# id :integer not null, primary key +# project_id :integer not null +# key :string(255) +# value :text +# encrypted_value :text +# encrypted_value_salt :string(255) +# encrypted_value_iv :string(255) +# + +require 'spec_helper' + +describe Variable do + subject { Variable.new } + + let(:secret_value) { 'secret' } + + before :each do + subject.value = secret_value + end + + describe :value do + it 'stores the encrypted value' do + subject.encrypted_value.should_not be_nil + end + + it 'stores an iv for value' do + subject.encrypted_value_iv.should_not be_nil + end + + it 'stores a salt for value' do + subject.encrypted_value_salt.should_not be_nil + end + + it 'fails to decrypt if iv is incorrect' do + subject.encrypted_value_iv = nil + subject.instance_variable_set(:@value, nil) + expect { subject.value }.to raise_error + end + end +end diff --git a/spec/ci/models/web_hook_spec.rb b/spec/ci/models/web_hook_spec.rb new file mode 100644 index 00000000000..0f0f175a7a3 --- /dev/null +++ b/spec/ci/models/web_hook_spec.rb @@ -0,0 +1,64 @@ +# == Schema Information +# +# Table name: web_hooks +# +# id :integer not null, primary key +# url :string(255) not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +require 'spec_helper' + +describe WebHook do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + it { should validate_presence_of(:url) } + + context "url format" do + it { should allow_value("http://example.com").for(:url) } + it { should allow_value("https://excample.com").for(:url) } + it { should allow_value("http://test.com/api").for(:url) } + it { should allow_value("http://test.com/api?key=abc").for(:url) } + it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } + + it { should_not allow_value("example.com").for(:url) } + it { should_not allow_value("ftp://example.com").for(:url) } + it { should_not allow_value("herp-and-derp").for(:url) } + end + end + + describe "execute" do + before(:each) do + @web_hook = FactoryGirl.create(:web_hook) + @project = @web_hook.project + @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} + + WebMock.stub_request(:post, @web_hook.url) + end + + it "POSTs to the web hook URL" do + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).once + end + + it "POSTs the data as JSON" do + json = @data.to_json + + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).with(body: json).once + end + + it "catches exceptions" do + WebHook.should_receive(:post).and_raise("Some HTTP Post error") + + lambda { + @web_hook.execute(@data) + }.should raise_error + end + end +end diff --git a/spec/ci/requests/api/builds_spec.rb b/spec/ci/requests/api/builds_spec.rb new file mode 100644 index 00000000000..be55e9ff479 --- /dev/null +++ b/spec/ci/requests/api/builds_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + let(:runner) { FactoryGirl.create(:runner, tag_list: ["mysql", "ruby"]) } + let(:project) { FactoryGirl.create(:project) } + + describe "Builds API for runners" do + let(:shared_runner) { FactoryGirl.create(:runner, token: "SharedRunner") } + let(:shared_project) { FactoryGirl.create(:project, name: "SharedProject") } + + before do + FactoryGirl.create :runner_project, project_id: project.id, runner_id: runner.id + end + + describe "POST /builds/register" do + it "should start a build" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + build = commit.builds.first + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response['sha'].should == build.sha + runner.reload.platform.should == "darwin" + end + + it "should return 404 error if no pending build found" do + post api("/builds/register"), token: runner.token + + response.status.should == 404 + end + + it "should return 404 error if no builds for specific runner" do + commit = FactoryGirl.create(:commit, project: shared_project) + FactoryGirl.create(:build, commit: commit, status: 'pending' ) + + post api("/builds/register"), token: runner.token + + response.status.should == 404 + end + + it "should return 404 error if no builds for shared runner" do + commit = FactoryGirl.create(:commit, project: project) + FactoryGirl.create(:build, commit: commit, status: 'pending' ) + + post api("/builds/register"), token: shared_runner.token + + response.status.should == 404 + end + + it "returns options" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["options"].should == {"image" => "ruby:2.1", "services" => ["postgres"]} + end + + it "returns variables" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["variables"].should == [ + {"key" => "DB_NAME", "value" => "postgres", "public" => true}, + {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, + ] + end + + it "returns variables for triggers" do + trigger = FactoryGirl.create(:trigger, project: project) + commit = FactoryGirl.create(:commit, project: project) + + trigger_request = FactoryGirl.create(:trigger_request_with_variables, commit: commit, trigger: trigger) + commit.create_builds(trigger_request) + project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["variables"].should == [ + {"key" => "DB_NAME", "value" => "postgres", "public" => true}, + {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, + {"key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false}, + ] + end + end + + describe "PUT /builds/:id" do + let(:commit) { FactoryGirl.create(:commit, project: project)} + let(:build) { FactoryGirl.create(:build, commit: commit, runner_id: runner.id) } + + it "should update a running build" do + build.run! + put api("/builds/#{build.id}"), token: runner.token + response.status.should == 200 + end + + it 'Should not override trace information when no trace is given' do + build.run! + build.update!(trace: 'hello_world') + put api("/builds/#{build.id}"), token: runner.token + expect(build.reload.trace).to eq 'hello_world' + end + end + end +end diff --git a/spec/ci/requests/api/commits_spec.rb b/spec/ci/requests/api/commits_spec.rb new file mode 100644 index 00000000000..190df70c1a5 --- /dev/null +++ b/spec/ci/requests/api/commits_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe API::API, 'Commits' do + include ApiHelpers + + let(:project) { FactoryGirl.create(:project) } + let(:commit) { FactoryGirl.create(:commit, project: project) } + + let(:options) { + { + project_token: project.token, + project_id: project.id + } + } + + describe "GET /commits" do + before { commit } + + it "should return commits per project" do + get api("/commits"), options + + response.status.should == 200 + json_response.count.should == 1 + json_response.first["project_id"].should == project.id + json_response.first["sha"].should == commit.sha + end + end + + describe "POST /commits" do + let(:data) { + { + "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref" => "refs/heads/master", + "commits" => [ + { + "id" => "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "message" => "Update Catalan translation to e38cb41.", + "timestamp" => "2011-12-12T14:27:31+02:00", + "url" => "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "author" => { + "name" => "Jordi Mallach", + "email" => "jordi@softcatala.org", + } + } + ], + ci_yaml_file: gitlab_ci_yaml + } + } + + it "should create a build" do + post api("/commits"), options.merge(data: data) + + response.status.should == 201 + json_response['sha'].should == "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" + end + + it "should return 400 error if no data passed" do + post api("/commits"), options + + response.status.should == 400 + json_response['message'].should == "400 (Bad request) \"data\" not given" + end + end +end diff --git a/spec/ci/requests/api/forks_spec.rb b/spec/ci/requests/api/forks_spec.rb new file mode 100644 index 00000000000..af523421c65 --- /dev/null +++ b/spec/ci/requests/api/forks_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + let(:project) { FactoryGirl.create(:project) } + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + + let(:options) { + { + private_token: private_token, + url: gitlab_url + } + } + + before { + stub_gitlab_calls + } + + + describe "POST /forks" do + let(:project_info) { + { + project_id: project.gitlab_id, + project_token: project.token, + data: { + id: 2, + name_with_namespace: "Gitlab.org / Underscore", + path_with_namespace: "gitlab-org/underscore", + default_branch: "master", + ssh_url_to_repo: "git@example.com:gitlab-org/underscore" + } + } + } + + context "with valid info" do + before do + options.merge!(project_info) + end + + it "should create a project with valid data" do + post api("/forks"), options + response.status.should == 201 + json_response['name'].should == "Gitlab.org / Underscore" + end + end + + context "with invalid project info" do + before do + options.merge!({}) + end + + it "should error with invalid data" do + post api("/forks"), options + response.status.should == 400 + end + end + end +end diff --git a/spec/ci/requests/api/projects_spec.rb b/spec/ci/requests/api/projects_spec.rb new file mode 100644 index 00000000000..014a9efc617 --- /dev/null +++ b/spec/ci/requests/api/projects_spec.rb @@ -0,0 +1,251 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + + let(:options) { + { + private_token: private_token, + url: gitlab_url + } + } + + before { + stub_gitlab_calls + } + + context "requests for scoped projects" do + # NOTE: These ids are tied to the actual projects on demo.gitlab.com + describe "GET /projects" do + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "gitlab-ci", gitlab_id: 4) } + + it "should return all projects on the CI instance" do + get api("/projects"), options + response.status.should == 200 + json_response.count.should == 2 + json_response.first["id"].should == project1.id + json_response.last["id"].should == project2.id + end + end + + describe "GET /projects/owned" do + # NOTE: This user doesn't own any of these projects on demo.gitlab.com + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "random-project", gitlab_id: 9898) } + + it "should return all projects on the CI instance" do + get api("/projects/owned"), options + + response.status.should == 200 + json_response.count.should == 0 + end + end + end + + describe "POST /projects/:project_id/webhooks" do + let!(:project) { FactoryGirl.create(:project) } + + context "Valid Webhook URL" do + let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } + + before do + options.merge!(webhook) + end + + it "should create webhook for specified project" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 201 + json_response["url"].should == webhook[:web_hook] + end + + it "fails to create webhook for non existsing project" do + post api("/projects/non-existant-id/webhooks"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 401 + end + end + + context "Invalid Webhook URL" do + let!(:webhook) { {web_hook: "ala_ma_kota" } } + + before do + options.merge!(webhook) + end + + it "fails to create webhook for not valid url" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 400 + end + end + + context "Missed web_hook parameter" do + it "fails to create webhook for not provided url" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 400 + end + end + end + + describe "GET /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + + context "with an existing project" do + it "should retrieve the project info" do + get api("/projects/#{project.id}"), options + response.status.should == 200 + json_response['id'].should == project.id + end + end + + context "with a non-existing project" do + it "should return 404 error if project not found" do + get api("/projects/non_existent_id"), options + response.status.should == 404 + end + end + end + + describe "PUT /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + let!(:project_info) { {name: "An updated name!" } } + + before do + options.merge!(project_info) + end + + it "should update a specific project's information" do + put api("/projects/#{project.id}"), options + response.status.should == 200 + json_response["name"].should == project_info[:name] + end + + it "fails to update a non-existing project" do + put api("/projects/non-existant-id"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + put api("/projects/#{project.id}"), options + response.status.should == 401 + end + end + + describe "DELETE /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + + it "should delete a specific project" do + delete api("/projects/#{project.id}"), options + response.status.should == 200 + + expect { project.reload }.to raise_error + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + delete api("/projects/#{project.id}"), options + response.status.should == 401 + end + + it "is getting not found error" do + delete api("/projects/not-existing_id"), options + response.status.should == 404 + end + end + + describe "POST /projects" do + let(:project_info) { + { + name: "My project", + gitlab_id: 1, + path: "testing/testing", + ssh_url_to_repo: "ssh://example.com/testing/testing.git" + } + } + + let(:invalid_project_info) { {} } + + context "with valid project info" do + before do + options.merge!(project_info) + end + + it "should create a project with valid data" do + post api("/projects"), options + response.status.should == 201 + json_response['name'].should == project_info[:name] + end + end + + context "with invalid project info" do + before do + options.merge!(invalid_project_info) + end + + it "should error with invalid data" do + post api("/projects"), options + response.status.should == 400 + end + end + + describe "POST /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:project) } + let(:runner) { FactoryGirl.create(:runner) } + + it "should add the project to the runner" do + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 201 + + project.reload + project.runners.first.id.should == runner.id + end + + it "should fail if it tries to link a non-existing project or runner" do + post api("/projects/#{project.id}/runners/non-existing"), options + response.status.should == 404 + + post api("/projects/non-existing/runners/#{runner.id}"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 401 + end + end + + describe "DELETE /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:project) } + let(:runner) { FactoryGirl.create(:runner) } + + before do + post api("/projects/#{project.id}/runners/#{runner.id}"), options + end + + it "should remove the project from the runner" do + project.runners.should be_present + delete api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 200 + + project.reload + project.runners.should be_empty + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 401 + end + end + end +end diff --git a/spec/ci/requests/api/runners_spec.rb b/spec/ci/requests/api/runners_spec.rb new file mode 100644 index 00000000000..47de3c2a95c --- /dev/null +++ b/spec/ci/requests/api/runners_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + include StubGitlabCalls + + before { + stub_gitlab_calls + } + + describe "GET /runners" do + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + let(:options) { + { + :private_token => private_token, + :url => gitlab_url + } + } + + before do + 5.times { FactoryGirl.create(:runner) } + end + + it "should retrieve a list of all runners" do + get api("/runners"), options + response.status.should == 200 + json_response.count.should == 5 + json_response.last.should have_key("id") + json_response.last.should have_key("token") + end + end + + describe "POST /runners/register" do + describe "should create a runner if token provided" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } + + it { response.status.should == 201 } + end + + describe "should create a runner with description" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } + + it { response.status.should == 201 } + it { Runner.first.description.should == "server.hostname" } + end + + describe "should create a runner with tags" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } + + it { response.status.should == 201 } + it { Runner.first.tag_list.sort.should == ["tag1", "tag2"] } + end + + describe "should create a runner if project token provided" do + let(:project) { FactoryGirl.create(:project) } + before { post api("/runners/register"), token: project.token } + + it { response.status.should == 201 } + it { project.runners.size.should == 1 } + end + + it "should return 403 error if token is invalid" do + post api("/runners/register"), token: 'invalid' + + response.status.should == 403 + end + + it "should return 400 error if no token" do + post api("/runners/register") + + response.status.should == 400 + end + end + + describe "DELETE /runners/delete" do + let!(:runner) { FactoryGirl.create(:runner) } + before { delete api("/runners/delete"), token: runner.token } + + it { response.status.should == 200 } + it { Runner.count.should == 0 } + end +end diff --git a/spec/ci/requests/api/triggers_spec.rb b/spec/ci/requests/api/triggers_spec.rb new file mode 100644 index 00000000000..6e56c4b3b22 --- /dev/null +++ b/spec/ci/requests/api/triggers_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + describe 'POST /projects/:project_id/refs/:ref/trigger' do + let!(:trigger_token) { 'secure token' } + let!(:project) { FactoryGirl.create(:project) } + let!(:project2) { FactoryGirl.create(:project) } + let!(:trigger) { FactoryGirl.create(:trigger, project: project, token: trigger_token) } + let(:options) { + { + token: trigger_token + } + } + + context 'Handles errors' do + it 'should return bad request if token is missing' do + post api("/projects/#{project.id}/refs/master/trigger") + response.status.should == 400 + end + + it 'should return not found if project is not found' do + post api('/projects/0/refs/master/trigger'), options + response.status.should == 404 + end + + it 'should return unauthorized if token is for different project' do + post api("/projects/#{project2.id}/refs/master/trigger"), options + response.status.should == 401 + end + end + + context 'Have a commit' do + before do + @commit = FactoryGirl.create(:commit, project: project) + end + + it 'should create builds' do + post api("/projects/#{project.id}/refs/master/trigger"), options + response.status.should == 201 + @commit.builds.reload + @commit.builds.size.should == 2 + end + + it 'should return bad request with no builds created if there\'s no commit for that ref' do + post api("/projects/#{project.id}/refs/other-branch/trigger"), options + response.status.should == 400 + json_response['message'].should == 'No builds created' + end + + context 'Validates variables' do + let(:variables) { + {'TRIGGER_KEY' => 'TRIGGER_VALUE'} + } + + it 'should validate variables to be a hash' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') + response.status.should == 400 + json_response['message'].should == 'variables needs to be a hash' + end + + it 'should validate variables needs to be a map of key-valued strings' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: {key: %w(1 2)}) + response.status.should == 400 + json_response['message'].should == 'variables needs to be a map of key-valued strings' + end + + it 'create trigger request with variables' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) + response.status.should == 201 + @commit.builds.reload + @commit.builds.first.trigger_request.variables.should == variables + end + end + end + end +end diff --git a/spec/ci/requests/builds_spec.rb b/spec/ci/requests/builds_spec.rb new file mode 100644 index 00000000000..73d540e372a --- /dev/null +++ b/spec/ci/requests/builds_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe "Builds" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/builds/:id/status.json" do + before do + get status_project_build_path(@project, @build), format: :json + end + + it { response.status.should == 200 } + it { response.body.should include(@build.sha) } + end +end diff --git a/spec/ci/requests/commits_spec.rb b/spec/ci/requests/commits_spec.rb new file mode 100644 index 00000000000..e9d8366c41a --- /dev/null +++ b/spec/ci/requests/commits_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe "Commits" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + end + + describe "GET /:project/refs/:ref_name/commits/:id/status.json" do + before do + get status_project_ref_commit_path(@project, @commit.ref, @commit.sha), format: :json + end + + it { response.status.should == 200 } + it { response.body.should include(@commit.sha) } + end +end diff --git a/spec/ci/services/create_commit_service_spec.rb b/spec/ci/services/create_commit_service_spec.rb new file mode 100644 index 00000000000..34e00d5b3c0 --- /dev/null +++ b/spec/ci/services/create_commit_service_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper' + +describe CreateCommitService do + let(:service) { CreateCommitService.new } + let(:project) { FactoryGirl.create(:project) } + + describe :execute do + context 'valid params' do + let(:commit) do + service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + end + + it { commit.should be_kind_of(Commit) } + it { commit.should be_valid } + it { commit.should be_persisted } + it { commit.should == project.commits.last } + it { commit.builds.first.should be_kind_of(Build) } + end + + context "skip tag if there is no build for it" do + it "creates commit if there is appropriate job" do + result = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + result.should be_persisted + end + + it "creates commit if there is no appropriate job but deploy job has right ref setting" do + config = YAML.dump({deploy: {deploy: "ls", only: ["0_1"]}}) + + result = service.execute(project, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: config, + commits: [ { message: "Message" } ] + ) + result.should be_persisted + end + end + + describe :ci_skip? do + it "skips builds creation if there is [ci skip] tag in commit message" do + commits = [{message: "some message[ci skip]"}] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.any?.should be_false + commit.status.should == "skipped" + end + + it "does not skips builds creation if there is no [ci skip] tag in commit message" do + commits = [{message: "some message"}] + + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + + commit.builds.first.name.should == "staging" + end + + it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do + commits = [{message: "some message[ci skip]"}] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: "invalid: file" + ) + commit.builds.any?.should be_false + commit.status.should == "skipped" + end + end + + it "skips build creation if there are already builds" do + commits = [{message: "message"}] + commit = service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.count(:all).should == 2 + + commit = service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.count(:all).should == 2 + end + + it "creates commit with failed status if yaml is invalid" do + commits = [{message: "some message"}] + + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: "invalid: file" + ) + + commit.status.should == "failed" + commit.builds.any?.should be_false + end + end +end diff --git a/spec/ci/services/create_project_service_spec.rb b/spec/ci/services/create_project_service_spec.rb new file mode 100644 index 00000000000..31614968d55 --- /dev/null +++ b/spec/ci/services/create_project_service_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe CreateProjectService do + let(:service) { CreateProjectService.new } + let(:current_user) { double.as_null_object } + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + + before { Network.any_instance.stub(enable_ci: true) } + + describe :execute do + context 'valid params' do + let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } + + it { project.should be_kind_of(Project) } + it { project.should be_persisted } + end + + context 'without project dump' do + it 'should raise exception' do + expect { service.execute(current_user, '', '') }.to raise_error + end + end + + context "forking" do + it "uses project as a template for settings and jobs" do + origin_project = FactoryGirl.create(:project) + origin_project.shared_runners_enabled = true + origin_project.public = true + origin_project.allow_git_fetch = true + origin_project.save! + + project = service.execute(current_user, project_dump, 'http://localhost/projects/:project_id', origin_project) + + project.shared_runners_enabled.should be_true + project.public.should be_true + project.allow_git_fetch.should be_true + end + end + end +end diff --git a/spec/ci/services/create_trigger_request_service_spec.rb b/spec/ci/services/create_trigger_request_service_spec.rb new file mode 100644 index 00000000000..41db01c2235 --- /dev/null +++ b/spec/ci/services/create_trigger_request_service_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe CreateTriggerRequestService do + let(:service) { CreateTriggerRequestService.new } + let(:project) { FactoryGirl.create :project } + let(:trigger) { FactoryGirl.create :trigger, project: project } + + describe :execute do + context 'valid params' do + subject { service.execute(project, trigger, 'master') } + + before do + @commit = FactoryGirl.create :commit, project: project + end + + it { subject.should be_kind_of(TriggerRequest) } + it { subject.commit.should == @commit } + end + + context 'no commit for ref' do + subject { service.execute(project, trigger, 'other-branch') } + + it { subject.should be_nil } + end + + context 'no builds created' do + subject { service.execute(project, trigger, 'master') } + + before do + FactoryGirl.create :commit_without_jobs, project: project + end + + it { subject.should be_nil } + end + + context 'for multiple commits' do + subject { service.execute(project, trigger, 'master') } + + before do + @commit1 = FactoryGirl.create :commit, committed_at: 2.hour.ago, project: project + @commit2 = FactoryGirl.create :commit, committed_at: 1.hour.ago, project: project + @commit3 = FactoryGirl.create :commit, committed_at: 3.hour.ago, project: project + end + + context 'retries latest one' do + it { subject.should be_kind_of(TriggerRequest) } + it { subject.should be_persisted } + it { subject.commit.should == @commit2 } + end + end + end +end diff --git a/spec/ci/services/event_service_spec.rb b/spec/ci/services/event_service_spec.rb new file mode 100644 index 00000000000..f7b9bf58127 --- /dev/null +++ b/spec/ci/services/event_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe EventService do + let (:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } + let (:user) { double(username: "root", id: 1) } + + before do + Event.destroy_all + end + + describe :remove_project do + it "creates event" do + EventService.new.remove_project(user, project) + + Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been removed by root" + end + end + + describe :create_project do + it "creates event" do + EventService.new.create_project(user, project) + + Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been created by root" + end + end + + describe :change_project_settings do + it "creates event" do + EventService.new.change_project_settings(user, project) + + Event.last.description.should == "User \"root\" updated projects settings" + end + end +end \ No newline at end of file diff --git a/spec/ci/services/image_for_build_service_spec.rb b/spec/ci/services/image_for_build_service_spec.rb new file mode 100644 index 00000000000..4c7094146bb --- /dev/null +++ b/spec/ci/services/image_for_build_service_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe ImageForBuildService do + let(:service) { ImageForBuildService.new } + let(:project) { FactoryGirl.create(:project) } + let(:commit) { FactoryGirl.create(:commit, project: project, ref: 'master') } + let(:build) { FactoryGirl.create(:build, commit: commit) } + + describe :execute do + before { build } + + context 'branch name' do + before { build.run! } + let(:image) { service.execute(project, ref: 'master') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-running.svg') } + it { image.name.should == 'build-running.svg' } + end + + context 'unknown branch name' do + let(:image) { service.execute(project, ref: 'feature') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-unknown.svg') } + it { image.name.should == 'build-unknown.svg' } + end + + context 'commit sha' do + before { build.run! } + let(:image) { service.execute(project, sha: build.sha) } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-running.svg') } + it { image.name.should == 'build-running.svg' } + end + + context 'unknown commit sha' do + let(:image) { service.execute(project, sha: '0000000') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-unknown.svg') } + it { image.name.should == 'build-unknown.svg' } + end + end +end diff --git a/spec/ci/services/register_build_service_spec.rb b/spec/ci/services/register_build_service_spec.rb new file mode 100644 index 00000000000..b5af777dd1d --- /dev/null +++ b/spec/ci/services/register_build_service_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe RegisterBuildService do + let!(:service) { RegisterBuildService.new } + let!(:project) { FactoryGirl.create :project } + let!(:commit) { FactoryGirl.create :commit, project: project } + let!(:pending_build) { FactoryGirl.create :build, project: project, commit: commit } + let!(:shared_runner) { FactoryGirl.create(:runner, is_shared: true) } + let!(:specific_runner) { FactoryGirl.create(:runner, is_shared: false) } + + before do + specific_runner.assign_to(project) + end + + describe :execute do + context 'runner follow tag list' do + it "picks build with the same tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["linux"] + service.execute(specific_runner).should == pending_build + end + + it "does not pick build with different tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["win32"] + service.execute(specific_runner).should be_false + end + + it "picks build without tag" do + service.execute(specific_runner).should == pending_build + end + + it "does not pick build with tag" do + pending_build.tag_list = ["linux"] + pending_build.save + service.execute(specific_runner).should be_false + end + + it "pick build without tag" do + specific_runner.tag_list = ["win32"] + service.execute(specific_runner).should == pending_build + end + end + + context 'allow shared runners' do + before do + project.shared_runners_enabled = true + project.save + end + + context 'shared runner' do + let(:build) { service.execute(shared_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == shared_runner } + end + + context 'specific runner' do + let(:build) { service.execute(specific_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == specific_runner } + end + end + + context 'disallow shared runners' do + context 'shared runner' do + let(:build) { service.execute(shared_runner) } + + it { build.should be_nil } + end + + context 'specific runner' do + let(:build) { service.execute(specific_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == specific_runner } + end + end + end +end diff --git a/spec/ci/services/web_hook_service_spec.rb b/spec/ci/services/web_hook_service_spec.rb new file mode 100644 index 00000000000..2bb153942e8 --- /dev/null +++ b/spec/ci/services/web_hook_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe WebHookService do + let (:project) { FactoryGirl.create :project } + let (:commit) { FactoryGirl.create :commit, project: project } + let (:build) { FactoryGirl.create :build, commit: commit } + let (:hook) { FactoryGirl.create :web_hook, project: project } + + describe :execute do + it "should execute successfully" do + stub_request(:post, hook.url).to_return(status: 200) + WebHookService.new.build_end(build).should be_true + end + end + + context 'build_data' do + it "contains all needed fields" do + build_data(build).should include( + :build_id, + :project_id, + :ref, + :build_status, + :build_started_at, + :build_finished_at, + :before_sha, + :project_name, + :gitlab_url, + :build_name + ) + end + end + + def build_data(build) + WebHookService.new.send :build_data, build + end +end diff --git a/spec/ci/six.tar.gz b/spec/ci/six.tar.gz new file mode 100644 index 00000000000..80a8c6644e4 Binary files /dev/null and b/spec/ci/six.tar.gz differ diff --git a/spec/ci/spec_helper.rb b/spec/ci/spec_helper.rb new file mode 100644 index 00000000000..54d3068845d --- /dev/null +++ b/spec/ci/spec_helper.rb @@ -0,0 +1,60 @@ +if ENV['SIMPLECOV'] + require 'simplecov' + SimpleCov.start +end + +if ENV['COVERALLS'] + require 'coveralls' + Coveralls.wear!('rails') +end + +ENV["RAILS_ENV"] ||= 'test' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'rspec/autorun' +require 'sidekiq/testing/inline' +require 'capybara/poltergeist' + +Capybara.javascript_driver = :poltergeist +Capybara.default_wait_time = 10 + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} + +require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) + +RSpec.configure do |config| + config.include LoginHelpers, type: :feature + + config.include StubGitlabCalls + config.include StubGitlabData + + # ## Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = false + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" +end diff --git a/spec/ci/support/api_helpers.rb b/spec/ci/support/api_helpers.rb new file mode 100644 index 00000000000..555980f2ea7 --- /dev/null +++ b/spec/ci/support/api_helpers.rb @@ -0,0 +1,35 @@ +module ApiHelpers + # Public: Prepend a request path with the path to the API + # + # path - Path to append + # user - User object - If provided, automatically appends private_token query + # string for authenticated requests + # + # Examples + # + # >> api('/issues') + # => "/api/v2/issues" + # + # >> api('/issues', User.last) + # => "/api/v2/issues?private_token=..." + # + # >> api('/issues?foo=bar', User.last) + # => "/api/v2/issues?foo=bar&private_token=..." + # + # Returns the relative path to the requested API resource + def api(path, user = nil) + "/api/#{API::API.version}#{path}" + + + # Normalize query string + (path.index('?') ? '' : '?') + + + # Append private_token if given a User object + (user.respond_to?(:private_token) ? + "&private_token=#{user.private_token}" : "") + end + + def json_response + JSON.parse(response.body) + end + +end diff --git a/spec/ci/support/db_cleaner.rb b/spec/ci/support/db_cleaner.rb new file mode 100644 index 00000000000..d2d532d9738 --- /dev/null +++ b/spec/ci/support/db_cleaner.rb @@ -0,0 +1,39 @@ +# RSpec.configure do |config| + +# config.around(:each) do |example| +# DatabaseCleaner.strategy = :transaction +# DatabaseCleaner.clean_with(:truncation) +# DatabaseCleaner.cleaning do +# example.run +# end +# end + +# config.around(:each, js: true) do |example| +# DatabaseCleaner.strategy = :truncation +# DatabaseCleaner.clean_with(:truncation) +# DatabaseCleaner.cleaning do +# example.run +# end +# end +# end +RSpec.configure do |config| + config.before(:suite) do + DatabaseCleaner.clean_with(:truncation) + end + + config.before(:each) do + DatabaseCleaner.strategy = :transaction + end + + config.before(:each, :js => true) do + DatabaseCleaner.strategy = :truncation + end + + config.before(:each) do + DatabaseCleaner.start + end + + config.after(:each) do + DatabaseCleaner.clean + end +end diff --git a/spec/ci/support/gitlab_stubs/gitlab_ci.yml b/spec/ci/support/gitlab_stubs/gitlab_ci.yml new file mode 100644 index 00000000000..3482145404e --- /dev/null +++ b/spec/ci/support/gitlab_stubs/gitlab_ci.yml @@ -0,0 +1,63 @@ +image: ruby:2.1 +services: + - postgres + +before_script: + - gem install bundler + - bundle install + - bundle exec rake db:create + +variables: + DB_NAME: postgres + +types: + - test + - deploy + - notify + +rspec: + script: "rake spec" + tags: + - ruby + - postgres + only: + - branches + +spinach: + script: "rake spinach" + allow_failure: true + tags: + - ruby + - mysql + except: + - tags + +staging: + script: "cap deploy stating" + type: deploy + tags: + - capistrano + - debian + except: + - stable + +production: + type: deploy + script: + - cap deploy production + - cap notify + tags: + - capistrano + - debian + only: + - master + - /^deploy-.*$/ + +dockerhub: + type: notify + script: "curl http://dockerhub/URL" + tags: + - ruby + - postgres + only: + - branches diff --git a/spec/ci/support/gitlab_stubs/project_8.json b/spec/ci/support/gitlab_stubs/project_8.json new file mode 100644 index 00000000000..f0a9fce859c --- /dev/null +++ b/spec/ci/support/gitlab_stubs/project_8.json @@ -0,0 +1,45 @@ +{ + "id":8, + "description":"ssh access and repository management app for GitLab", + "default_branch":"master", + "public":false, + "visibility_level":0, + "ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git", + "http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git", + "web_url":"http://demo.gitlab.com/gitlab/gitlab-shell", + "owner": { + "id":4, + "name":"GitLab", + "created_at":"2012-12-21T13:03:05Z" + }, + "name":"gitlab-shell", + "name_with_namespace":"GitLab / gitlab-shell", + "path":"gitlab-shell", + "path_with_namespace":"gitlab/gitlab-shell", + "issues_enabled":true, + "merge_requests_enabled":true, + "wall_enabled":false, + "wiki_enabled":true, + "snippets_enabled":false, + "created_at":"2013-03-20T13:28:53Z", + "last_activity_at":"2013-11-30T00:11:17Z", + "namespace":{ + "created_at":"2012-12-21T13:03:05Z", + "description":"Self hosted Git management software", + "id":4, + "name":"GitLab", + "owner_id":1, + "path":"gitlab", + "updated_at":"2013-03-20T13:29:13Z" + }, + "permissions":{ + "project_access": { + "access_level": 10, + "notification_level": 3 + }, + "group_access": { + "access_level": 50, + "notification_level": 3 + } + } +} \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/project_8_hooks.json b/spec/ci/support/gitlab_stubs/project_8_hooks.json new file mode 100644 index 00000000000..93d51406d63 --- /dev/null +++ b/spec/ci/support/gitlab_stubs/project_8_hooks.json @@ -0,0 +1 @@ +[{}] diff --git a/spec/ci/support/gitlab_stubs/projects.json b/spec/ci/support/gitlab_stubs/projects.json new file mode 100644 index 00000000000..ca42c14c5d8 --- /dev/null +++ b/spec/ci/support/gitlab_stubs/projects.json @@ -0,0 +1 @@ +[{"id":3,"description":"GitLab is open source software to collaborate on code. Create projects and repositories, manage access and do code reviews.","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlabhq.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlabhq.git","web_url":"http://demo.gitlab.com/gitlab/gitlabhq","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlabhq","name_with_namespace":"GitLab / gitlabhq","path":"gitlabhq","path_with_namespace":"gitlab/gitlabhq","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:34Z","last_activity_at":"2013-12-02T19:10:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":4,"description":"Component of GitLab CI. Web application","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-ci.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-ci.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-ci","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-ci","name_with_namespace":"GitLab / gitlab-ci","path":"gitlab-ci","path_with_namespace":"gitlab/gitlab-ci","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:50Z","last_activity_at":"2013-11-28T19:26:54Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":5,"description":"","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-recipes.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-recipes.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-recipes","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-recipes","name_with_namespace":"GitLab / gitlab-recipes","path":"gitlab-recipes","path_with_namespace":"gitlab/gitlab-recipes","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:07:02Z","last_activity_at":"2013-12-02T13:54:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":8,"description":"ssh access and repository management app for GitLab","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-shell","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-shell","name_with_namespace":"GitLab / gitlab-shell","path":"gitlab-shell","path_with_namespace":"gitlab/gitlab-shell","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-03-20T13:28:53Z","last_activity_at":"2013-11-30T00:11:17Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":9,"description":null,"default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab_git.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab_git.git","web_url":"http://demo.gitlab.com/gitlab/gitlab_git","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab_git","name_with_namespace":"GitLab / gitlab_git","path":"gitlab_git","path_with_namespace":"gitlab/gitlab_git","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-04-28T19:15:08Z","last_activity_at":"2013-12-02T13:07:13Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":10,"description":"ultra lite authorization library http://randx.github.com/six/\\r\\n ","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/six.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/six.git","web_url":"http://demo.gitlab.com/sandbox/six","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Six","name_with_namespace":"Sandbox / Six","path":"six","path_with_namespace":"sandbox/six","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:45:02Z","last_activity_at":"2013-11-29T11:30:56Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":11,"description":"Simple HTML5 Charts using the tag ","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/charts-js.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/charts-js.git","web_url":"http://demo.gitlab.com/sandbox/charts-js","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Charts.js","name_with_namespace":"Sandbox / Charts.js","path":"charts-js","path_with_namespace":"sandbox/charts-js","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:47:29Z","last_activity_at":"2013-12-02T15:18:11Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":13,"description":"","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/afro.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/afro.git","web_url":"http://demo.gitlab.com/sandbox/afro","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Afro","name_with_namespace":"Sandbox / Afro","path":"afro","path_with_namespace":"sandbox/afro","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-11-14T17:45:19Z","last_activity_at":"2013-12-02T17:41:45Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}}] \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/raw_project.yml b/spec/ci/support/gitlab_stubs/raw_project.yml new file mode 100644 index 00000000000..df2ce223d1f --- /dev/null +++ b/spec/ci/support/gitlab_stubs/raw_project.yml @@ -0,0 +1,36 @@ +--- !ruby/object:OpenStruct +table: + :id: 189 + :description: Website at http://api.gitlab.org/ + :default_branch: master + :public: false + :visibility_level: 0 + :ssh_url_to_repo: dzaporozhets@localhost:gitlab/api-gitlab-org.git + :http_url_to_repo: http://localhost:3000/gitlab/api-gitlab-org.git + :web_url: http://localhost:3000/gitlab/api-gitlab-org + :owner: + id: 1 + name: GitLab + created_at: '2012-10-03T09:59:57.000Z' + :name: api.gitlab.org + :name_with_namespace: GitLab / api.gitlab.org + :path: api-gitlab-org + :path_with_namespace: gitlab/api-gitlab-org + :issues_enabled: true + :merge_requests_enabled: true + :wall_enabled: false + :wiki_enabled: false + :snippets_enabled: false + :created_at: '2013-06-06T12:29:39.000Z' + :last_activity_at: '2013-12-06T20:29:42.000Z' + :namespace: + id: 1 + name: GitLab + path: gitlab + owner_id: 1 + created_at: '2012-10-03T09:59:57.000Z' + updated_at: '2014-01-28T08:49:53.000Z' + description: Self hosted Git management software + avatar: + url: /uploads/group/avatar/1/0-vader-profile.jpg + diff --git a/spec/ci/support/gitlab_stubs/session.json b/spec/ci/support/gitlab_stubs/session.json new file mode 100644 index 00000000000..ce8dfe5ae75 --- /dev/null +++ b/spec/ci/support/gitlab_stubs/session.json @@ -0,0 +1,20 @@ +{ + "id":2, + "username":"jsmith", + "email":"test@test.com", + "name":"John Smith", + "bio":"", + "skype":"aertert", + "linkedin":"", + "twitter":"", + "theme_id":2,"color_scheme_id":2, + "state":"active", + "created_at":"2012-12-21T13:02:20Z", + "extern_uid":null, + "provider":null, + "is_admin":false, + "can_create_group":false, + "can_create_project":false, + "private_token":"Wvjy2Krpb7y8xi93owUz", + "access_token":"Wvjy2Krpb7y8xi93owUz" +} \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/user.json b/spec/ci/support/gitlab_stubs/user.json new file mode 100644 index 00000000000..ce8dfe5ae75 --- /dev/null +++ b/spec/ci/support/gitlab_stubs/user.json @@ -0,0 +1,20 @@ +{ + "id":2, + "username":"jsmith", + "email":"test@test.com", + "name":"John Smith", + "bio":"", + "skype":"aertert", + "linkedin":"", + "twitter":"", + "theme_id":2,"color_scheme_id":2, + "state":"active", + "created_at":"2012-12-21T13:02:20Z", + "extern_uid":null, + "provider":null, + "is_admin":false, + "can_create_group":false, + "can_create_project":false, + "private_token":"Wvjy2Krpb7y8xi93owUz", + "access_token":"Wvjy2Krpb7y8xi93owUz" +} \ No newline at end of file diff --git a/spec/ci/support/login_helpers.rb b/spec/ci/support/login_helpers.rb new file mode 100644 index 00000000000..ebd9693f8a4 --- /dev/null +++ b/spec/ci/support/login_helpers.rb @@ -0,0 +1,22 @@ +module LoginHelpers + def login_as(role) + raise 'Only :user allowed' unless role == :user + stub_gitlab_calls + login_with(:user) + end + + # Internal: Login as the specified user + # + # user - User instance to login with + def login_with(user) + visit callback_user_sessions_path(code: "some_auth_code_here") + end + + def logout + click_link "Logout" rescue nil + end + + def skip_admin_auth + ApplicationController.any_instance.stub(authenticate_admin!: true) + end +end diff --git a/spec/ci/support/monkey_patches/oauth2.rb b/spec/ci/support/monkey_patches/oauth2.rb new file mode 100644 index 00000000000..dfd5e319f00 --- /dev/null +++ b/spec/ci/support/monkey_patches/oauth2.rb @@ -0,0 +1,7 @@ +module OAuth2 + class Client + def get_token(params, access_token_opts = {}, access_token_class = AccessToken) + OpenStruct.new(token: "some_token") + end + end +end \ No newline at end of file diff --git a/spec/ci/support/setup_builds_storage.rb b/spec/ci/support/setup_builds_storage.rb new file mode 100644 index 00000000000..cafc8dee918 --- /dev/null +++ b/spec/ci/support/setup_builds_storage.rb @@ -0,0 +1,16 @@ +RSpec.configure do |config| + def builds_path + Rails.root.join('tmp/builds_test') + end + + config.before(:each) do + FileUtils.mkdir_p(builds_path) + Ci::Settings.gitlab_ci['builds_path'] = builds_path + end + + config.after(:suite) do + Dir.chdir(builds_path) do + `ls | grep -v .gitkeep | xargs rm -r` + end + end +end diff --git a/spec/ci/support/stub_gitlab_calls.rb b/spec/ci/support/stub_gitlab_calls.rb new file mode 100644 index 00000000000..931ef963c0f --- /dev/null +++ b/spec/ci/support/stub_gitlab_calls.rb @@ -0,0 +1,77 @@ +module StubGitlabCalls + def stub_gitlab_calls + stub_session + stub_user + stub_project_8 + stub_project_8_hooks + stub_projects + stub_projects_owned + stub_ci_enable + end + + def stub_js_gitlab_calls + Network.any_instance.stub(:projects) { project_hash_array } + end + + private + + def gitlab_url + GitlabCi.config.gitlab_server.url + end + + def stub_session + f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json')) + + stub_request(:post, "#{gitlab_url}api/v3/session.json"). + with(:body => "{\"email\":\"test@test.com\",\"password\":\"123456\"}", + :headers => {'Content-Type'=>'application/json'}). + to_return(:status => 201, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_user + f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + + stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + + stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_project_8 + data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8.json')) + Network.any_instance.stub(:project).and_return(JSON.parse(data)) + end + + def stub_project_8_hooks + data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8_hooks.json')) + Network.any_instance.stub(:project_hooks).and_return(JSON.parse(data)) + end + + def stub_projects + f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) + + stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_projects_owned + stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => "", :headers => {}) + end + + def stub_ci_enable + stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => "", :headers => {}) + end + + def project_hash_array + f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) + return JSON.parse f + end +end diff --git a/spec/ci/support/stub_gitlab_data.rb b/spec/ci/support/stub_gitlab_data.rb new file mode 100644 index 00000000000..fa402f35b95 --- /dev/null +++ b/spec/ci/support/stub_gitlab_data.rb @@ -0,0 +1,5 @@ +module StubGitlabData + def gitlab_ci_yaml + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end +end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 9c115bbfc6a..48bc60eed16 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ExtractsPath do include ExtractsPath include RepoHelpers - include Rails.application.routes.url_helpers + include Gitlab::Application.routes.url_helpers let(:project) { double('project') } diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb index 755964e9a3d..203117aee70 100644 --- a/spec/support/filter_spec_helper.rb +++ b/spec/support/filter_spec_helper.rb @@ -72,6 +72,6 @@ module FilterSpecHelper # Shortcut to Rails' auto-generated routes helpers, to avoid including the # module def urls - Rails.application.routes.url_helpers + Gitlab::Application.routes.url_helpers end end -- cgit v1.2.1 From d0420c68fbb2fe84dee8538df246cdc7e7b85d28 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 26 Aug 2015 09:23:25 -0700 Subject: Simplify doc --- doc/reply_by_email/README.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 705cb08dd1a..6c3d191cc71 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -76,23 +76,11 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. :worker: EmailReceiverWorker ``` - -4. Copy `lib/support/init.d/gitlab.default.example` to `/etc/default/gitlab`, if that does not already exist: - - ```sh - [ -f /etc/default/gitlab ] || sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab - ``` - -5. Edit `/etc/default/gitlab` to enable `mail_room`: - - ```sh - sudo editor /etc/default/gitlab - ``` - - Either change `mail_room_enabled=false` to the below, or add it at the bottom of the file: +5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`: ```sh - mail_room_enabled=true + sudo mkdir -p /etc/default + echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab ``` 6. Restart GitLab: -- cgit v1.2.1 From f197785495e83c996150b05d9bc3ce1effcc5a68 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 26 Aug 2015 10:13:11 -0700 Subject: Use sudu -u git where appropriate --- doc/reply_by_email/README.md | 2 +- lib/tasks/gitlab/check.rake | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 6c3d191cc71..b33303c7be8 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -92,7 +92,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. 7. Check if everything is configured correctly: ```sh - sudo bundle exec rake gitlab:reply_by_email:check RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:reply_by_email:check RAILS_ENV=production ``` 8. Reply by email should now be working. diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 13f1cf58fca..f5a900d87c9 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -595,7 +595,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("RAILS_ENV=production sudo -u git -H bin/background_jobs start") + sudo_gitlab("bin/background_jobs start", "RAILS_ENV=production") ) for_more_information( see_installation_guide_section("Install Init Script"), @@ -726,7 +726,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("RAILS_ENV=production sudo -u git -H bin/mail_room start") + sudo_gitlab("bin/mail_room start", "RAILS_ENV=production") ) for_more_information( see_installation_guide_section("Install Init Script"), @@ -879,8 +879,10 @@ namespace :gitlab do "doc/install/installation.md in section \"#{section}\"" end - def sudo_gitlab(command) - "sudo -u #{gitlab_user} -H #{command}" + def sudo_gitlab(command, env = nil) + cmd = "sudo -u #{gitlab_user} -H #{command}" + cmd.prepend "#{env} " if env + cmd end def gitlab_user -- cgit v1.2.1 From 6afd69f4445cc0688aa1695389eb3f79033e3121 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 26 Aug 2015 17:47:18 -0700 Subject: Update gitignore, change literal DB table names, fix errors, fix fontawesome --- .gitignore | 3 +++ app/assets/javascripts/ci/application.js.coffee | 2 +- app/assets/javascripts/ci/build.coffee | 2 +- app/assets/stylesheets/ci/generic/common.scss | 2 +- app/controllers/ci/runners_controller.rb | 2 +- app/helpers/ci/icons_helper.rb | 4 ++-- app/helpers/ci/runners_helper.rb | 4 ++-- app/models/ci/user.rb | 2 +- app/views/ci/admin/projects/_project.html.haml | 8 ++++---- app/views/ci/builds/_build.html.haml | 4 ++-- app/views/ci/builds/show.html.haml | 8 ++++---- app/views/ci/commits/show.html.haml | 2 +- app/views/ci/helps/show.html.haml | 8 ++++---- app/views/ci/lints/_create.html.haml | 4 ++-- app/views/ci/lints/show.html.haml | 2 +- app/views/ci/projects/_project.html.haml | 6 +++--- app/views/ci/projects/_search.html.haml | 2 +- app/views/ci/projects/gitlab.html.haml | 4 ++-- app/views/ci/projects/index.html.haml | 2 +- app/views/layouts/ci/_nav.html.haml | 4 ++-- app/views/layouts/ci/_nav_admin.html.haml | 10 +++++----- app/views/layouts/ci/_nav_project.html.haml | 20 ++++++++++---------- app/views/layouts/ci/project.html.haml | 2 +- config/gitlab_ci.yml | 19 ------------------- config/secrets.yml | 3 --- lib/ci/charts.rb | 6 +++--- 26 files changed, 58 insertions(+), 77 deletions(-) delete mode 100644 config/gitlab_ci.yml delete mode 100644 config/secrets.yml diff --git a/.gitignore b/.gitignore index 8a68bb3e4f0..aff8ad9ecbe 100644 --- a/.gitignore +++ b/.gitignore @@ -20,12 +20,14 @@ backups/* config/aws.yml config/database.yml config/gitlab.yml +config/gitlab_ci.yml config/initializers/omniauth.rb config/initializers/rack_attack.rb config/initializers/smtp_settings.rb config/resque.yml config/unicorn.rb config/mail_room.yml +config/secrets.yml coverage/* db/*.sqlite3 db/*.sqlite3-journal @@ -41,3 +43,4 @@ rails_best_practices_output.html /tags tmp/ vendor/bundle/* +/ci/builds/* diff --git a/app/assets/javascripts/ci/application.js.coffee b/app/assets/javascripts/ci/application.js.coffee index 8a8aed1385c..c2f7bfe9776 100644 --- a/app/assets/javascripts/ci/application.js.coffee +++ b/app/assets/javascripts/ci/application.js.coffee @@ -41,7 +41,7 @@ $(document).on 'click', '.edit-runner-link', (event) -> descr.removeClass('hide') $(document).on 'click', '.assign-all-runner', -> - $(this).replaceWith(' Assign in progress..') + $(this).replaceWith(' Assign in progress..') window.unbindEvents = -> $(document).unbind('scroll') diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee index be4a3aa757a..c30859b484b 100644 --- a/app/assets/javascripts/ci/build.coffee +++ b/app/assets/javascripts/ci/build.coffee @@ -29,7 +29,7 @@ class CiBuild success: (build) => if build.status == "running" $('#build-trace code').html build.trace_html - $('#build-trace code').append '' + $('#build-trace code').append '' @checkAutoscroll() else Turbolinks.visit build_url diff --git a/app/assets/stylesheets/ci/generic/common.scss b/app/assets/stylesheets/ci/generic/common.scss index 58b7a93b0ad..8b0d59fdb73 100644 --- a/app/assets/stylesheets/ci/generic/common.scss +++ b/app/assets/stylesheets/ci/generic/common.scss @@ -122,7 +122,7 @@ ul.bordered-list { color: #888; text-shadow: 0 1px 1px #fff; } - i[class^="fa-"] { + i.fa { line-height: 14px; } } diff --git a/app/controllers/ci/runners_controller.rb b/app/controllers/ci/runners_controller.rb index 01eebf7e6a7..0ef32ce928f 100644 --- a/app/controllers/ci/runners_controller.rb +++ b/app/controllers/ci/runners_controller.rb @@ -11,7 +11,7 @@ module Ci def index @runners = @project.runners.order('id DESC') @specific_runners = current_user.authorized_runners. - where.not(id: @runners).order('runners.id DESC').page(params[:page]).per(20) + where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) end diff --git a/app/helpers/ci/icons_helper.rb b/app/helpers/ci/icons_helper.rb index ecb6ef7be45..be40f79e880 100644 --- a/app/helpers/ci/icons_helper.rb +++ b/app/helpers/ci/icons_helper.rb @@ -2,9 +2,9 @@ module Ci module IconsHelper def boolean_to_icon(value) if value.to_s == "true" - content_tag :i, nil, class: 'fa-circle cgreen' + content_tag :i, nil, class: 'fa fa-circle cgreen' else - content_tag :i, nil, class: 'fa-power-off clgray' + content_tag :i, nil, class: 'fa fa-power-off clgray' end end end diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index 782208ddfe4..03c9914641e 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -3,7 +3,7 @@ module Ci def runner_status_icon(runner) unless runner.contacted_at return content_tag :i, nil, - class: "fa-warning-sign", + class: "fa fa-warning-sign", title: "New runner. Has not connected yet" end @@ -15,7 +15,7 @@ module Ci end content_tag :i, nil, - class: "fa-circle runner-status-#{status}", + class: "fa fa-circle runner-status-#{status}", title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" end end diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb index 7456bd1a77b..49ec7126fc1 100644 --- a/app/models/ci/user.rb +++ b/app/models/ci/user.rb @@ -68,7 +68,7 @@ module Ci def authorized_runners Ci::Runner.specific.includes(:runner_projects). - where(runner_projects: { project_id: authorized_projects } ) + where(Ci::RunnerProject.table_name => { project_id: authorized_projects } ) end def authorized_projects diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml index e64bfe853d7..505dd4b3fdc 100644 --- a/app/views/ci/admin/projects/_project.html.haml +++ b/app/views/ci/admin/projects/_project.html.haml @@ -3,7 +3,7 @@ %td = project.id %td - = link_to project do + = link_to [:ci, project] do %strong= project.name %td - if last_commit @@ -15,14 +15,14 @@ No builds yet %td - if project.public - %i.fa-globe + %i.fa.fa-globe Public - else - %i.fa-lock + %i.fa.fa-lock Private %td = project.commits.count %td = link_to [:ci, :admin, project], method: :delete, class: 'btn btn-danger btn-sm' do - %i.fa-remove + %i.fa.fa-remove Remove diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml index ff9fdbbcb4e..54ca1102b5c 100644 --- a/app/views/ci/builds/_build.html.haml +++ b/app/views/ci/builds/_build.html.haml @@ -39,7 +39,7 @@ .pull-right - if build.active? = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do - %i.fa-remove.cred + %i.fa.fa-remove.cred - elsif build.commands.present? = link_to retry_ci_project_build_path(build.project, build, return_to: request.original_url), method: :post, title: 'Retry build' do - %i.fa-repeat + %i.fa.fa-repeat diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index fed30847e73..0bef67d8a20 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -25,7 +25,7 @@ %a Build ##{@build.id} · - %i.fa-warning-sign + %i.fa.fa-warning-sign This build was retried. .row @@ -46,7 +46,7 @@ - if @build.duration .pull-right %span - %i.fa-time + %i.fa.fa-time #{duration_in_words(@build.finished_at, @build.started_at)} .clearfix @@ -63,9 +63,9 @@ .clearfix .scroll-controls = link_to '#up-build-trace', class: 'btn' do - %i.fa-angle-up + %i.fa.fa-angle-up = link_to '#down-build-trace', class: 'btn' do - %i.fa-angle-down + %i.fa.fa-angle-down %pre.trace#build-trace %code.bash diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 4cf567c77e6..832cc6a1bae 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -60,7 +60,7 @@ Builds - if @commit.duration > 0 %small.pull-right - %i.fa-time + %i.fa.fa-time #{time_interval_in_words @commit.duration} %table.builds diff --git a/app/views/ci/helps/show.html.haml b/app/views/ci/helps/show.html.haml index 5acdf9fa98a..9b32d529c60 100644 --- a/app/views/ci/helps/show.html.haml +++ b/app/views/ci/helps/show.html.haml @@ -14,27 +14,27 @@ .bs-callout.bs-callout-success %h4 = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/api' do - %i.fa-cogs + %i.fa.fa-cogs API %p Explore how you can access GitLab CI via the API. .bs-callout.bs-callout-info %h4 = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/examples' do - %i.fa-info-sign + %i.fa.fa-info-sign Build script examples %p This includes the build script we use to test GitLab CE. .bs-callout.bs-callout-danger %h4 = link_to 'https://gitlab.com/gitlab-org/gitlab-ci/issues' do - %i.fa-bug + %i.fa.fa-bug Issue tracker %p Reports about recent bugs and problems.. .bs-callout.bs-callout-warning %h4 = link_to 'http://feedback.gitlab.com/forums/176466-general/category/64310-gitlab-ci' do - %i.fa-thumbs-up + %i.fa.fa-thumbs-up Feedback forum %p Suggest improvements or new features for GitLab CI. diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml index 903b92de689..e2179e60f3e 100644 --- a/app/views/ci/lints/_create.html.haml +++ b/app/views/ci/lints/_create.html.haml @@ -2,7 +2,7 @@ %p %b Status: syntax is correct - %i.fa-ok.correct-syntax + %i.fa.fa-ok.correct-syntax %table.table.table-bordered %thead @@ -32,7 +32,7 @@ %p %b Status: syntax is incorrect - %i.fa-remove.incorrect-syntax + %i.fa.fa-remove.incorrect-syntax %b Error: = @error diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml index b0fd5dd8e58..a9b954771c5 100644 --- a/app/views/ci/lints/show.html.haml +++ b/app/views/ci/lints/show.html.haml @@ -13,7 +13,7 @@ %p.text-center.loading - %i.fa-refresh.fa-spin + %i.fa.fa-refresh.fa-spin .results.prepend-top-20 diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index 3e893410df8..b3ad47ce432 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -1,7 +1,7 @@ - last_commit = project.last_commit %tr.alert{class: commit_status_alert_class(last_commit) } %td - = link_to project do + = link_to [:ci, project] do %strong= project.name %td - if last_commit @@ -13,10 +13,10 @@ No builds yet %td - if project.public - %i.fa-globe + %i.fa.fa-globe Public - else - %i.fa-lock + %i.fa.fa-lock Private %td = project.commits.count diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml index 37fb804d8d0..e65aaa3870d 100644 --- a/app/views/ci/projects/_search.html.haml +++ b/app/views/ci/projects/_search.html.haml @@ -4,7 +4,7 @@ .input-group = search_field_tag "search", params[:search], placeholder: "Search", class: "search-input form-control" .input-group-addon - %i.fa-search + %i.fa.fa-search :coffeescript diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml index dbc0ea0880f..bd55b1f12e7 100644 --- a/app/views/ci/projects/gitlab.html.haml +++ b/app/views/ci/projects/gitlab.html.haml @@ -6,7 +6,7 @@ by keyword: "#{params[:search]}", #{time_ago_in_words(current_user.sync_at)} ago. = link_to gitlab_ci_projects_path(reset_cache: true, search: params[:search]), class: 'sync-now btn btn-sm btn-default reset-cache' do - %i.fa-refresh + %i.fa.fa-refresh Sync now %br @@ -27,7 +27,7 @@ = render "gl_projects" %p.text-center.hide.loading - %i.fa-refresh.fa-spin + %i.fa.fa-refresh.fa-spin - else = render @projects diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 6243a28f9e2..69b6c8b4d6d 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -7,7 +7,7 @@ .projects %p.fetch-status.light - %i.fa-refresh.fa-spin + %i.fa.fa-refresh.fa-spin Please wait while we fetch from GitLab (#{GitlabCi.config.gitlab_server.url}) :coffeescript $.get '#{gitlab_ci_projects_path}', (data) -> diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml index 4e944d4d0d6..babd14ca2d3 100644 --- a/app/views/layouts/ci/_nav.html.haml +++ b/app/views/layouts/ci/_nav.html.haml @@ -3,7 +3,7 @@ .navbar-header %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} %span.sr-only Toggle navigation - %i.fa-reorder + %i.fa.fa-reorder = link_to 'GitLab CI', ci_root_path, class: "navbar-brand" @@ -25,7 +25,7 @@ %span= current_user.name %li = link_to ci_user_sessions_path, class: "logout", method: :delete do - %i.fa-signout + %i.fa.fa-signout Logout - else %li diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index 792a5f1e4dd..ae58f15554a 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -1,21 +1,21 @@ %ul.nav.nav-pills.nav-stacked.admin-menu = nav_link path: 'projects' do = link_to ci_admin_projects_path do - %i.fa-list-alt + %i.fa.fa-list-alt Projects = nav_link path: 'events' do = link_to ci_admin_events_path do - %i.fa-book + %i.fa.fa-book Events = nav_link path: 'runners#index' do = link_to ci_admin_runners_path do - %i.fa-cog + %i.fa.fa-cog Runners %small.pull-right = Ci::Runner.count(:all) = nav_link path: 'builds' do = link_to ci_admin_builds_path do - %i.fa-link + %i.fa.fa-link Builds %small.pull-right = Ci::Build.count(:all) @@ -23,6 +23,6 @@ %hr = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to ci_admin_application_settings_path do - %i.fa-cogs + %i.fa.fa-cogs %span Settings diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 24ee1609d25..d5b66b92fe8 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,40 +1,40 @@ %ul.nav.nav-pills.nav-stacked.project-menu = nav_link path: 'projects#show' do = link_to ci_project_path(@project) do - %i.fa-list-alt + %i.fa.fa-list-alt Commits %small.pull-right= @project.commits.count = nav_link path: 'charts#show' do = link_to ci_project_charts_path(@project) do - %i.fa-bar-chart + %i.fa.fa-bar-chart Charts = nav_link path: ['runners#index', 'runners#show'] do = link_to ci_project_runners_path(@project) do - %i.fa-cog + %i.fa.fa-cog Runners - = nav_link path: 'variables#index' do + = nav_link path: 'variables#show' do = link_to ci_project_variables_path(@project) do - %i.fa-code + %i.fa.fa-code Variables = nav_link path: 'web_hooks#index' do = link_to ci_project_web_hooks_path(@project) do - %i.fa-link + %i.fa.fa-link Web Hooks = nav_link path: 'triggers#index' do = link_to ci_project_triggers_path(@project) do - %i.fa-retweet + %i.fa.fa-retweet Triggers = nav_link path: 'services#index' do = link_to ci_project_services_path(@project) do - %i.fa-share + %i.fa.fa-share Services = nav_link path: 'events#index' do = link_to ci_project_events_path(@project) do - %i.fa-book + %i.fa.fa-book Events %li %hr = nav_link path: 'projects#edit' do = link_to edit_ci_project_path(@project) do - %i.fa-cogs + %i.fa.fa-cogs Settings diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index d0c0861669d..763a7fc0b02 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -9,7 +9,7 @@ = @project.name - if @project.public %small - %i.fa-globe + %i.fa.fa-globe Public .pull-right diff --git a/config/gitlab_ci.yml b/config/gitlab_ci.yml deleted file mode 100644 index 03a86307f40..00000000000 --- a/config/gitlab_ci.yml +++ /dev/null @@ -1,19 +0,0 @@ -development: - gitlab_server: - url: 'http://gitlab.dev' - app_id: 'cfda7ec2551af42d06acc6dbda9087dbdc8d45b7e2bc240f498fad3f84ff4044' - app_secret: 'd1802d55db9c1aedc950812a9489e2659fa1430dc488babde949bc9c409cc01b' - - gitlab_ci: - host: 'http://ci.gitlab.dev' - port: 80 - https: false -test: - gitlab_server: - url: 'http://demo.gitlab.com/' - app_id: '' - app_secret: '' - gitlab_ci: - host: localhost - port: 80 - https: false diff --git a/config/secrets.yml b/config/secrets.yml deleted file mode 100644 index f63c74d0688..00000000000 --- a/config/secrets.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -development: - db_key_base: 53ab5c413f37a5a87df3c7e55dc49924793c44b9a40834af258f75ce3cc71067478b7c1f999bf22d9cfb9e6dedffda989dc462684f8c869705f735a92b7230ed diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb index e50a7a59c27..915a4f526a6 100644 --- a/lib/ci/charts.rb +++ b/lib/ci/charts.rb @@ -17,10 +17,10 @@ module Ci def push(from, to, format) @labels << from.strftime(format) @total << project.builds. - where('? > builds.created_at AND builds.created_at > ?', to, from). + where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). count(:all) @success << project.builds. - where('? > builds.created_at AND builds.created_at > ?', to, from). + where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). success.count(:all) end end @@ -60,7 +60,7 @@ module Ci class BuildTime < Chart def collect - commits = project.commits.joins(:builds).where('builds.finished_at is NOT NULL AND builds.started_at is NOT NULL').last(30) + commits = project.commits.joins(:builds).where("#{Ci::Build.table_name}.finished_at is NOT NULL AND #{Ci::Build.table_name}.started_at is NOT NULL").last(30) commits.each do |commit| @labels << commit.short_sha @build_times << (commit.duration / 60) -- cgit v1.2.1 From 308e65c0d33583b7450f29822523051a4da2257e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 26 Aug 2015 17:58:16 -0700 Subject: Fix admin nav active state --- app/views/layouts/ci/_nav_admin.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index ae58f15554a..26eaf4db0e5 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-pills.nav-stacked.admin-menu - = nav_link path: 'projects' do + = nav_link path: 'projects#index' do = link_to ci_admin_projects_path do %i.fa.fa-list-alt Projects - = nav_link path: 'events' do + = nav_link path: 'events#index' do = link_to ci_admin_events_path do %i.fa.fa-book Events @@ -13,7 +13,7 @@ Runners %small.pull-right = Ci::Runner.count(:all) - = nav_link path: 'builds' do + = nav_link path: 'builds#index' do = link_to ci_admin_builds_path do %i.fa.fa-link Builds -- cgit v1.2.1 From 232422f828116d8b2551d63fbd511abc5148ec2e Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 27 Aug 2015 10:45:03 +0200 Subject: Add omnibus-gitlab configuration example. --- doc/reply_by_email/README.md | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index b33303c7be8..f1dcef5de2d 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -12,7 +12,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these ## Set it up -In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. +In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ### Installations from source @@ -27,7 +27,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ```sh sudo editor config/gitlab.yml ``` - + ```yaml reply_by_email: enabled: true @@ -37,7 +37,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: - + ```sh sudo cp config/mail_room.yml.example config/mail_room.yml ``` @@ -84,7 +84,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ``` 6. Restart GitLab: - + ```sh sudo service gitlab restart ``` @@ -99,14 +99,37 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ### Omnibus package installations -TODO +In `/etc/gitlab/gitlab.rb`: + +```ruby + +gitlab_rails['reply_by_email_enabled'] = true +gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" +gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host +gitlab_rails['reply_by_email_port'] = 993 # IMAP server port +gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL +gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. +gitlab_rails['reply_by_email_password'] = "password" # Email account password +gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. +``` + +and run `sudo gitlab-ctl reconfigure` for changes to take effect. + +After reconfigure run has been successfully completed you will have the following commands available: + +```bash +sudo gitlab-ctl status mailroom +sudo gitlab-ctl stop mailroom +sudo gitlab-ctl start mailroom +sudo gitlab-ctl restart mailroom +``` ### Development 1. Go to the GitLab installation directory. 1. Find the `reply_by_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `reply_key`: - + ```yaml reply_by_email: enabled: true @@ -116,7 +139,7 @@ TODO As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: - + ```sh sudo cp config/mail_room.yml.example config/mail_room.yml ``` @@ -158,7 +181,7 @@ TODO ``` 6. Restart GitLab: - + ```sh bundle exec foreman start ``` -- cgit v1.2.1 From 58c487e28b3f10c8a871dc82f7d79a45c7876f15 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 27 Aug 2015 11:23:23 -0700 Subject: Tweak check rake task. --- lib/tasks/gitlab/check.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index f5a900d87c9..9815582d02d 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -595,7 +595,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("bin/background_jobs start", "RAILS_ENV=production") + sudo_gitlab("RAILS_ENV=production bin/background_jobs start") ) for_more_information( see_installation_guide_section("Install Init Script"), @@ -726,7 +726,7 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("bin/mail_room start", "RAILS_ENV=production") + sudo_gitlab("RAILS_ENV=production bin/mail_room start") ) for_more_information( see_installation_guide_section("Install Init Script"), -- cgit v1.2.1 From c915e2c8237ddcae57ec48e700badd9d5bfd8c8c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 10:58:48 +0100 Subject: Allow configuration of LDAP attributes GitLab will use for the new user account. --- config/gitlab.yml.example | 15 ++++++++++++ lib/gitlab/ldap/auth_hash.rb | 40 ++++++++++++++++++++++++++++++++ lib/gitlab/ldap/user.rb | 4 ++++ lib/gitlab/o_auth/auth_hash.rb | 24 ++++++++++--------- spec/lib/gitlab/o_auth/auth_hash_spec.rb | 6 ++--- spec/lib/gitlab/o_auth/user_spec.rb | 2 +- 6 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 lib/gitlab/ldap/auth_hash.rb diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 9eb99dae456..cb697fad7ab 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -144,6 +144,21 @@ production: &base bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' + # LDAP attributes that GitLab will use to create an account for the LDAP user. + # Can be either the name of an attribute as a string (e.g. 'mail'), + # or an array of names of attributes to try in order (e.g. ['mail', 'email']). + # The default values are listed. + attributes: + # username: ['uid', 'userid', 'sAMAccountName'] + # name: 'cn' # Also falls back to a combination of first_name and last_name, see below + # email: ['mail', 'email', 'userPrincipalName'] + + # If no full name could be found at the attribute specified for `name`, + # the full name is determined as ` `, using the + # attributes specified below. + # first_name: 'givenName' + # last_name: 'sn' + # This setting specifies if LDAP server is Active Directory LDAP server. # For non AD servers it skips the AD specific queries. # If your LDAP server is not AD, set this to false. diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb new file mode 100644 index 00000000000..caca7bb3b57 --- /dev/null +++ b/lib/gitlab/ldap/auth_hash.rb @@ -0,0 +1,40 @@ +# Class to parse and transform the info provided by omniauth +# +module Gitlab + module LDAP + class AuthHash < Gitlab::OAuth::AuthHash + attr_accessor :config + + def initialize(auth_hash, config) + super(auth_hash) + @config = config + end + + private + + def get_info(key) + raw_key = config.attributes[key] + return super unless raw_key + + value = + case raw_key + when String + get_raw(raw_key) + when Array + raw_key.inject(nil) { |value, key| value || get_raw(key).presence } + else + nil + end + + return super unless value + + Gitlab::Utils.force_utf8(value) + value + end + + def get_raw(key) + auth_hash.extra[:raw_info][key] + end + end + end +end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 04a22237478..e568b0e3b31 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -71,6 +71,10 @@ module Gitlab def ldap_config Gitlab::LDAP::Config.new(auth_hash.provider) end + + def auth_hash=(auth_hash) + @auth_hash = Gitlab::LDAP::AuthHash.new(auth_hash, ldap_config) + end end end end diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb index 9b8e783d16c..76fbe698c74 100644 --- a/lib/gitlab/o_auth/auth_hash.rb +++ b/lib/gitlab/o_auth/auth_hash.rb @@ -16,16 +16,6 @@ module Gitlab @provider ||= Gitlab::Utils.force_utf8(auth_hash.provider.to_s) end - def info - auth_hash.info - end - - def get_info(key) - value = info.try(key) - Gitlab::Utils.force_utf8(value) if value - value - end - def name @name ||= get_info(:name) || "#{get_info(:first_name)} #{get_info(:last_name)}" end @@ -44,9 +34,21 @@ module Gitlab private + def info + auth_hash.info + end + + def get_info(key) + key = :nickname if key == :username + + value = info[key] + Gitlab::Utils.force_utf8(value) if value + value + end + def username_and_email @username_and_email ||= begin - username = get_info(:nickname) || get_info(:username) + username = get_info(:username) email = get_info(:email) username ||= generate_username(email) if email diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb index e4a6cd954cc..5632f2306ec 100644 --- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb +++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' describe Gitlab::OAuth::AuthHash do let(:auth_hash) do Gitlab::OAuth::AuthHash.new( - double({ + OmniAuth::AuthHash.new( provider: provider_ascii, uid: uid_ascii, - info: double(info_hash) - }) + info: info_hash + ) ) end diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index c6cca98a037..c0083fc85be 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::OAuth::User do let(:gl_user) { oauth_user.gl_user } let(:uid) { 'my-uid' } let(:provider) { 'my-provider' } - let(:auth_hash) { double(uid: uid, provider: provider, info: double(info_hash)) } + let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) } let(:info_hash) do { nickname: '-john+gitlab-ETC%.git@gmail.com', -- cgit v1.2.1 From 0e9ba0a4fa3a2e8335e7b902dd50710e2309773f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 17:34:18 +0100 Subject: Add attributes to LDAP::Config. --- config/initializers/1_settings.rb | 1 + lib/gitlab/ldap/config.rb | 4 ++++ lib/gitlab/ldap/user.rb | 7 ++++--- spec/lib/gitlab/ldap/user_spec.rb | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index c47e5dab27c..c23f00a6f05 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -97,6 +97,7 @@ if Settings.ldap['enabled'] || Rails.env.test? server['block_auto_created_users'] = false if server['block_auto_created_users'].nil? server['allow_username_or_email_login'] = false if server['allow_username_or_email_login'].nil? server['active_directory'] = true if server['active_directory'].nil? + server['attributes'] = {} if server['attributes'].nil? server['provider_name'] ||= "ldap#{key}".downcase server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name']) end diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb index d2ffa2e1fe8..101a3285f4b 100644 --- a/lib/gitlab/ldap/config.rb +++ b/lib/gitlab/ldap/config.rb @@ -84,6 +84,10 @@ module Gitlab options['block_auto_created_users'] end + def attributes + options['attributes'] + end + protected def base_config Gitlab.config.ldap diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index e568b0e3b31..e5023f5da11 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -68,12 +68,13 @@ module Gitlab Gitlab::LDAP::Access.allowed?(gl_user) end - def ldap_config - Gitlab::LDAP::Config.new(auth_hash.provider) + def ldap_config(provider = auth_hash.provider) + Gitlab::LDAP::Config.new(provider) end def auth_hash=(auth_hash) - @auth_hash = Gitlab::LDAP::AuthHash.new(auth_hash, ldap_config) + config = ldap_config(auth_hash.provider) + @auth_hash = Gitlab::LDAP::AuthHash.new(auth_hash, config) end end end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 84d9fb54b61..fd2e5f6d0e1 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -11,7 +11,7 @@ describe Gitlab::LDAP::User do } end let(:auth_hash) do - double(uid: 'my-uid', provider: 'ldapmain', info: double(info)) + OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info) end describe :changed? do -- cgit v1.2.1 From 3d6fed54f0dc551d8c7ba9a03f4dfbd2203552b5 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 9 Sep 2015 11:06:35 +0300 Subject: fix Gemfile --- Gemfile | 2 +- Gemfile.lock | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index c9d9d7f9e92..0cb3aedc108 100644 --- a/Gemfile +++ b/Gemfile @@ -111,7 +111,7 @@ gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' gem 'creole', '~>0.3.6' -gem 'wikicloth', '~> 0.8.1' +gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' # Diffs diff --git a/Gemfile.lock b/Gemfile.lock index 97e4c258615..1c64a622cd2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -345,7 +345,6 @@ GEM html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) - htmlentities (4.3.4) http-cookie (1.0.2) domain_name (~> 0.5) http_parser.rb (0.5.3) @@ -562,6 +561,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) + rinku (1.7.3) rotp (1.6.1) rouge (1.7.7) rqrcode (0.7.0) @@ -725,8 +725,6 @@ GEM eventmachine (>= 0.12.8) http_parser.rb (~> 0.5.1) simple_oauth (~> 0.1.4) - twitter-text (1.12.0) - unf (~> 0.1.0) tzinfo (1.2.2) thread_safe (~> 0.1) uglifier (2.3.3) @@ -762,12 +760,10 @@ GEM whenever (0.8.4) activesupport (>= 2.3.4) chronic (>= 0.6.3) - wikicloth (0.8.3) + wikicloth (0.8.1) builder expression_parser - htmlentities - nokogiri - twitter-text + rinku xpath (2.0.0) nokogiri (~> 1.3) @@ -924,7 +920,7 @@ DEPENDENCIES virtus (~> 1.0.1) webmock (~> 1.21.0) whenever (~> 0.8.4) - wikicloth (~> 0.8.1) + wikicloth (= 0.8.1) BUNDLED WITH 1.10.6 -- cgit v1.2.1 From 9c6ed296640ff06d5ae078024f112e44910af235 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 11:36:08 +0100 Subject: Expand explanation in config file --- config/gitlab.yml.example | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index cb697fad7ab..4d061dc93fb 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -143,21 +143,6 @@ production: &base method: 'plain' # "tls" or "ssl" or "plain" bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' - - # LDAP attributes that GitLab will use to create an account for the LDAP user. - # Can be either the name of an attribute as a string (e.g. 'mail'), - # or an array of names of attributes to try in order (e.g. ['mail', 'email']). - # The default values are listed. - attributes: - # username: ['uid', 'userid', 'sAMAccountName'] - # name: 'cn' # Also falls back to a combination of first_name and last_name, see below - # email: ['mail', 'email', 'userPrincipalName'] - - # If no full name could be found at the attribute specified for `name`, - # the full name is determined as ` `, using the - # attributes specified below. - # first_name: 'givenName' - # last_name: 'sn' # This setting specifies if LDAP server is Active Directory LDAP server. # For non AD servers it skips the AD specific queries. @@ -195,6 +180,26 @@ production: &base # user_filter: '' + # LDAP attributes that GitLab will use to create an account for the LDAP user. + # Can be either the name of an attribute as a string (e.g. 'mail'), + # or an array of names of attributes to try in order (e.g. ['mail', 'email']). + # Note that the user's LDAP login will always be the attribute specified as `uid` above. + attributes: + # The username will be used in paths for the user's own projects + # (like `gitlab.example.com/username/project`) and when mentioning + # them in issues, merge request and comments (like `@username`). + # If the attribute specified for `username` contains an email address, + # the GitLab username will be the part of the email address before the '@'. + username: ['uid', 'userid', 'sAMAccountName'] + email: ['mail', 'email', 'userPrincipalName'] + + # If no full name could be found at the attribute specified for `name`, + # the full name is determined using the attributes specified for + # `first_name` and `last_name`. + name: 'cn' + first_name: 'givenName' + last_name: 'sn' + # GitLab EE only: add more LDAP servers # Choose an ID made of a-z and 0-9 . This ID will be stored in the database # so that GitLab can remember which LDAP server a user belongs to. -- cgit v1.2.1 From 909a8443c63db423f031b8c7f810c13b3bc20b87 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 11:40:31 +0100 Subject: Shuffle config around a bit --- lib/gitlab/ldap/auth_hash.rb | 13 +++++-------- lib/gitlab/ldap/user.rb | 7 +++---- lib/gitlab/o_auth/auth_hash.rb | 4 +--- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb index caca7bb3b57..dc8a8fd41dd 100644 --- a/lib/gitlab/ldap/auth_hash.rb +++ b/lib/gitlab/ldap/auth_hash.rb @@ -3,17 +3,10 @@ module Gitlab module LDAP class AuthHash < Gitlab::OAuth::AuthHash - attr_accessor :config - - def initialize(auth_hash, config) - super(auth_hash) - @config = config - end - private def get_info(key) - raw_key = config.attributes[key] + raw_key = ldap_config.attributes[key] return super unless raw_key value = @@ -35,6 +28,10 @@ module Gitlab def get_raw(key) auth_hash.extra[:raw_info][key] end + + def ldap_config + @ldap_config ||= Gitlab::LDAP::Config.new(self.provider) + end end end end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index e5023f5da11..cb66fd500fe 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -68,13 +68,12 @@ module Gitlab Gitlab::LDAP::Access.allowed?(gl_user) end - def ldap_config(provider = auth_hash.provider) - Gitlab::LDAP::Config.new(provider) + def ldap_config + Gitlab::LDAP::Config.new(auth_hash.provider) end def auth_hash=(auth_hash) - config = ldap_config(auth_hash.provider) - @auth_hash = Gitlab::LDAP::AuthHash.new(auth_hash, config) + @auth_hash = Gitlab::LDAP::AuthHash.new(auth_hash) end end end diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb index 76fbe698c74..d94b104bbf8 100644 --- a/lib/gitlab/o_auth/auth_hash.rb +++ b/lib/gitlab/o_auth/auth_hash.rb @@ -39,8 +39,6 @@ module Gitlab end def get_info(key) - key = :nickname if key == :username - value = info[key] Gitlab::Utils.force_utf8(value) if value value @@ -48,7 +46,7 @@ module Gitlab def username_and_email @username_and_email ||= begin - username = get_info(:username) + username = get_info(:username) || get_info(:nickname) email = get_info(:email) username ||= generate_username(email) if email -- cgit v1.2.1 From c52fee70d06684035886f3d3c44cb581973b48c8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 11:51:40 +0100 Subject: Test overriding LDAP attributes --- spec/lib/gitlab/ldap/auth_hash_spec.rb | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 spec/lib/gitlab/ldap/auth_hash_spec.rb diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb new file mode 100644 index 00000000000..18c7924fea1 --- /dev/null +++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab::LDAP::AuthHash do + let(:auth_hash) do + Gitlab::LDAP::AuthHash.new( + OmniAuth::AuthHash.new( + uid: '123456', + provider: 'ldapmain', + info: info, + extra: { + raw_info: raw_info + } + ) + ) + end + + let(:info) do + { + name: 'Smith, J.', + email: 'johnsmith@example.com', + nickname: '123456' + } + end + + let(:raw_info) do + { + uid: '123456', + email: 'johnsmith@example.com', + cn: 'Smith, J.', + fullName: 'John Smith' + } + end + + context "without overridden attributes" do + + it "has the correct username" do + expect(auth_hash.username).to eq("123456") + end + + it "has the correct name" do + expect(auth_hash.name).to eq("Smith, J.") + end + end + + context "with overridden attributes" do + let(:attributes) do + { + username: ['mail', 'email'], + name: 'fullName' + } + end + + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive(:attributes).and_return(attributes) + end + + it "has the correct username" do + expect(auth_hash.username).to eq("johnsmith@example.com") + end + + it "has the correct name" do + expect(auth_hash.name).to eq("John Smith") + end + end +end -- cgit v1.2.1 From 00ecacf8ea2674afb043db9e900ad9e769e7929b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 11:54:48 +0100 Subject: Add to docs --- config/gitlab.yml.example | 4 ++-- doc/integration/ldap.md | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 4d061dc93fb..85461e51dd5 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -181,8 +181,8 @@ production: &base user_filter: '' # LDAP attributes that GitLab will use to create an account for the LDAP user. - # Can be either the name of an attribute as a string (e.g. 'mail'), - # or an array of names of attributes to try in order (e.g. ['mail', 'email']). + # The specified attribute can either be the attribute name as a string (e.g. 'mail'), + # or an array of attribute names to try in order (e.g. ['mail', 'email']). # Note that the user's LDAP login will always be the attribute specified as `uid` above. attributes: # The username will be used in paths for the user's own projects diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index 904d5d7fee2..3bc5df21ef4 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -78,6 +78,26 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # user_filter: '' + # LDAP attributes that GitLab will use to create an account for the LDAP user. + # The specified attribute can either be the attribute name as a string (e.g. 'mail'), + # or an array of attribute names to try in order (e.g. ['mail', 'email']). + # Note that the user's LDAP login will always be the attribute specified as `uid` above. + attributes: + # The username will be used in paths for the user's own projects + # (like `gitlab.example.com/username/project`) and when mentioning + # them in issues, merge request and comments (like `@username`). + # If the attribute specified for `username` contains an email address, + # the GitLab username will be the part of the email address before the '@'. + username: ['uid', 'userid', 'sAMAccountName'] + email: ['mail', 'email', 'userPrincipalName'] + + # If no full name could be found at the attribute specified for `name`, + # the full name is determined using the attributes specified for + # `first_name` and `last_name`. + name: 'cn' + first_name: 'givenName' + last_name: 'sn' + # GitLab EE only: add more LDAP servers # Choose an ID made of a-z and 0-9 . This ID will be stored in the database # so that GitLab can remember which LDAP server a user belongs to. -- cgit v1.2.1 From bed263f0fe3aa8485b3f07318c00a6164ca6f927 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 11:54:55 +0100 Subject: Add to changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 247eb1e3643..9555d913432 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -40,6 +40,7 @@ v 8.0.0 (unreleased) - Add ability to get user information by ID of an SSH key via the API - Fix bug which IE cannot show image at markdown when the image is raw file of gitlab - Add support for Crowd + - Allow configuration of LDAP attributes GitLab will use for the new user account. v 7.14.1 - Improve abuse reports management from admin area -- cgit v1.2.1 From d8795297fa7847d2dc672c30e1aabffd3687bdcc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 12:10:47 +0100 Subject: Restructure omnibus instructions to match those for source installations. --- doc/reply_by_email/README.md | 48 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 1f704e65bcf..1886650164a 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -34,7 +34,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. address: "gitlab-replies+%{reply_key}@gmail.com" ``` - As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-replies@gmail.com`. + As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: @@ -91,7 +91,7 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. sudo service gitlab restart ``` -7. Check if everything is configured correctly: +7. Verify that everything is configured correctly: ```sh sudo -u git -H bundle exec rake gitlab:reply_by_email:check RAILS_ENV=production @@ -101,30 +101,34 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. ### Omnibus package installations -In `/etc/gitlab/gitlab.rb`: +1. Find the `reply_by_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `reply_key` and fill in the details for your specific IMAP server and email account: + + ```ruby + gitlab_rails['reply_by_email_enabled'] = true + gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" + gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host + gitlab_rails['reply_by_email_port'] = 993 # IMAP server port + gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL + gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. + gitlab_rails['reply_by_email_password'] = "password" # Email account password + gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". + ``` -```ruby + As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. -gitlab_rails['reply_by_email_enabled'] = true -gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" -gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host -gitlab_rails['reply_by_email_port'] = 993 # IMAP server port -gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL -gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. -gitlab_rails['reply_by_email_password'] = "password" # Email account password -gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. -``` +1. Reconfigure GitLab for the changes to take effect: -and run `sudo gitlab-ctl reconfigure` for changes to take effect. + ```sh + sudo gitlab-ctl reconfigure + ``` -After reconfigure run has been successfully completed you will have the following commands available: +1. Verify that everything is configured correctly: + + ```sh + sudo gitlab-rake gitlab:reply_by_email:check + ``` -```bash -sudo gitlab-ctl status mailroom -sudo gitlab-ctl stop mailroom -sudo gitlab-ctl start mailroom -sudo gitlab-ctl restart mailroom -``` +1. Reply by email should now be working. ### Development @@ -190,7 +194,7 @@ sudo gitlab-ctl restart mailroom bundle exec foreman start ``` -7. Check if everything is configured correctly: +7. Verify that everything is configured correctly: ```sh bundle exec rake gitlab:reply_by_email:check RAILS_ENV=development -- cgit v1.2.1 From 3b89a6830261a7252899674ddaef49b6d19a66d9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 12:11:08 +0100 Subject: Move source section below omnibus section. --- doc/reply_by_email/README.md | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md index 1886650164a..e9187298d79 100644 --- a/doc/reply_by_email/README.md +++ b/doc/reply_by_email/README.md @@ -14,6 +14,37 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. +### Omnibus package installations + +1. Find the `reply_by_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `reply_key` and fill in the details for your specific IMAP server and email account: + + ```ruby + gitlab_rails['reply_by_email_enabled'] = true + gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" + gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host + gitlab_rails['reply_by_email_port'] = 993 # IMAP server port + gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL + gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. + gitlab_rails['reply_by_email_password'] = "password" # Email account password + gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". + ``` + + As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. + +1. Reconfigure GitLab for the changes to take effect: + + ```sh + sudo gitlab-ctl reconfigure + ``` + +1. Verify that everything is configured correctly: + + ```sh + sudo gitlab-rake gitlab:reply_by_email:check + ``` + +1. Reply by email should now be working. + ### Installations from source 1. Go to the GitLab installation directory: @@ -99,37 +130,6 @@ In this example, we'll use the Gmail address `gitlab-replies@gmail.com`. 8. Reply by email should now be working. -### Omnibus package installations - -1. Find the `reply_by_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `reply_key` and fill in the details for your specific IMAP server and email account: - - ```ruby - gitlab_rails['reply_by_email_enabled'] = true - gitlab_rails['reply_by_email_address'] = "gitlab-replies+%{reply_key}@gmail.com" - gitlab_rails['reply_by_email_host'] = "imap.gmail.com" # IMAP server host - gitlab_rails['reply_by_email_port'] = 993 # IMAP server port - gitlab_rails['reply_by_email_ssl'] = true # Whether the IMAP server uses SSL - gitlab_rails['reply_by_email_email'] = "gitlab-replies@gmail.com" # Email account username. Usually the full email address. - gitlab_rails['reply_by_email_password'] = "password" # Email account password - gitlab_rails['reply_by_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". - ``` - - As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-replies@gmail.com`. - -1. Reconfigure GitLab for the changes to take effect: - - ```sh - sudo gitlab-ctl reconfigure - ``` - -1. Verify that everything is configured correctly: - - ```sh - sudo gitlab-rake gitlab:reply_by_email:check - ``` - -1. Reply by email should now be working. - ### Development 1. Go to the GitLab installation directory. -- cgit v1.2.1 From 76c6aeb9bc9855e9a65bb08db862e92ac923255e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 13:37:50 +0200 Subject: Merge CI factories and CI spec/support with GitLab --- app/controllers/ci/application_controller.rb | 2 +- db/schema.rb | 144 ++++++---------------- spec/ci/controllers/commits_controller_spec.rb | 27 ---- spec/ci/controllers/projects_controller_spec.rb | 108 ---------------- spec/ci/factories/builds.rb | 45 ------- spec/ci/factories/commits.rb | 75 ----------- spec/ci/factories/events.rb | 24 ---- spec/ci/factories/projects.rb | 56 --------- spec/ci/factories/runner_projects.rb | 19 --- spec/ci/factories/runners.rb | 38 ------ spec/ci/factories/trigger_requests.rb | 13 -- spec/ci/factories/triggers.rb | 9 -- spec/ci/factories/users.rb | 6 - spec/ci/factories/web_hook.rb | 6 - spec/ci/spec_helper.rb | 60 --------- spec/ci/support/api_helpers.rb | 35 ------ spec/ci/support/db_cleaner.rb | 39 ------ spec/ci/support/gitlab_stubs/gitlab_ci.yml | 63 ---------- spec/ci/support/gitlab_stubs/project_8.json | 45 ------- spec/ci/support/gitlab_stubs/project_8_hooks.json | 1 - spec/ci/support/gitlab_stubs/projects.json | 1 - spec/ci/support/gitlab_stubs/raw_project.yml | 36 ------ spec/ci/support/gitlab_stubs/session.json | 20 --- spec/ci/support/gitlab_stubs/user.json | 20 --- spec/ci/support/login_helpers.rb | 22 ---- spec/ci/support/monkey_patches/oauth2.rb | 7 -- spec/ci/support/setup_builds_storage.rb | 16 --- spec/ci/support/stub_gitlab_calls.rb | 77 ------------ spec/ci/support/stub_gitlab_data.rb | 5 - spec/controllers/ci/commits_controller_spec.rb | 27 ++++ spec/controllers/ci/projects_controller_spec.rb | 108 ++++++++++++++++ spec/factories/ci/builds.rb | 45 +++++++ spec/factories/ci/commits.rb | 75 +++++++++++ spec/factories/ci/events.rb | 24 ++++ spec/factories/ci/projects.rb | 56 +++++++++ spec/factories/ci/runner_projects.rb | 19 +++ spec/factories/ci/runners.rb | 38 ++++++ spec/factories/ci/trigger_requests.rb | 13 ++ spec/factories/ci/triggers.rb | 9 ++ spec/factories/ci/web_hook.rb | 6 + spec/spec_helper.rb | 3 + spec/support/gitlab_stubs/gitlab_ci.yml | 63 ++++++++++ spec/support/gitlab_stubs/project_8.json | 45 +++++++ spec/support/gitlab_stubs/project_8_hooks.json | 1 + spec/support/gitlab_stubs/projects.json | 1 + spec/support/gitlab_stubs/raw_project.yml | 36 ++++++ spec/support/gitlab_stubs/session.json | 20 +++ spec/support/gitlab_stubs/user.json | 20 +++ spec/support/setup_builds_storage.rb | 16 +++ spec/support/stub_gitlab_calls.rb | 77 ++++++++++++ spec/support/stub_gitlab_data.rb | 5 + 51 files changed, 746 insertions(+), 980 deletions(-) delete mode 100644 spec/ci/controllers/commits_controller_spec.rb delete mode 100644 spec/ci/controllers/projects_controller_spec.rb delete mode 100644 spec/ci/factories/builds.rb delete mode 100644 spec/ci/factories/commits.rb delete mode 100644 spec/ci/factories/events.rb delete mode 100644 spec/ci/factories/projects.rb delete mode 100644 spec/ci/factories/runner_projects.rb delete mode 100644 spec/ci/factories/runners.rb delete mode 100644 spec/ci/factories/trigger_requests.rb delete mode 100644 spec/ci/factories/triggers.rb delete mode 100644 spec/ci/factories/users.rb delete mode 100644 spec/ci/factories/web_hook.rb delete mode 100644 spec/ci/spec_helper.rb delete mode 100644 spec/ci/support/api_helpers.rb delete mode 100644 spec/ci/support/db_cleaner.rb delete mode 100644 spec/ci/support/gitlab_stubs/gitlab_ci.yml delete mode 100644 spec/ci/support/gitlab_stubs/project_8.json delete mode 100644 spec/ci/support/gitlab_stubs/project_8_hooks.json delete mode 100644 spec/ci/support/gitlab_stubs/projects.json delete mode 100644 spec/ci/support/gitlab_stubs/raw_project.yml delete mode 100644 spec/ci/support/gitlab_stubs/session.json delete mode 100644 spec/ci/support/gitlab_stubs/user.json delete mode 100644 spec/ci/support/login_helpers.rb delete mode 100644 spec/ci/support/monkey_patches/oauth2.rb delete mode 100644 spec/ci/support/setup_builds_storage.rb delete mode 100644 spec/ci/support/stub_gitlab_calls.rb delete mode 100644 spec/ci/support/stub_gitlab_data.rb create mode 100644 spec/controllers/ci/commits_controller_spec.rb create mode 100644 spec/controllers/ci/projects_controller_spec.rb create mode 100644 spec/factories/ci/builds.rb create mode 100644 spec/factories/ci/commits.rb create mode 100644 spec/factories/ci/events.rb create mode 100644 spec/factories/ci/projects.rb create mode 100644 spec/factories/ci/runner_projects.rb create mode 100644 spec/factories/ci/runners.rb create mode 100644 spec/factories/ci/trigger_requests.rb create mode 100644 spec/factories/ci/triggers.rb create mode 100644 spec/factories/ci/web_hook.rb create mode 100644 spec/support/gitlab_stubs/gitlab_ci.yml create mode 100644 spec/support/gitlab_stubs/project_8.json create mode 100644 spec/support/gitlab_stubs/project_8_hooks.json create mode 100644 spec/support/gitlab_stubs/projects.json create mode 100644 spec/support/gitlab_stubs/raw_project.yml create mode 100644 spec/support/gitlab_stubs/session.json create mode 100644 spec/support/gitlab_stubs/user.json create mode 100644 spec/support/setup_builds_storage.rb create mode 100644 spec/support/stub_gitlab_calls.rb create mode 100644 spec/support/stub_gitlab_data.rb diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 726781cb30b..95390d09737 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -8,7 +8,7 @@ module Ci rescue_from Ci::Network::UnauthorizedError, with: :invalid_token before_filter :default_headers - before_filter :check_config + #before_filter :check_config protect_from_forgery diff --git a/db/schema.rb b/db/schema.rb index 77ced9caa3c..d7197d951a4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -24,17 +24,6 @@ ActiveRecord::Schema.define(version: 20150826001931) do t.datetime "updated_at" end - create_table "appearances", force: true do |t| - t.string "title" - t.text "description" - t.string "logo" - t.integer "updated_by" - t.datetime "created_at" - t.datetime "updated_at" - t.string "dark_logo" - t.string "light_logo" - end - create_table "application_settings", force: true do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" @@ -46,11 +35,10 @@ ActiveRecord::Schema.define(version: 20150826001931) do t.string "home_page_url" t.integer "default_branch_protection", default: 2 t.boolean "twitter_sharing_enabled", default: true - t.text "help_text" t.text "restricted_visibility_levels" + t.boolean "version_check_enabled", default: true t.integer "max_attachment_size", default: 10, null: false t.integer "default_project_visibility" - t.boolean "version_check_enabled", default: true t.integer "default_snippet_visibility" t.text "restricted_signup_domains" t.boolean "user_oauth_applications", default: true @@ -318,28 +306,6 @@ ActiveRecord::Schema.define(version: 20150826001931) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree - create_table "git_hooks", force: true do |t| - t.string "force_push_regex" - t.string "delete_branch_regex" - t.string "commit_message_regex" - t.boolean "deny_delete_tag" - t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" - t.string "author_email_regex" - t.boolean "member_check", default: false, null: false - t.string "file_name_regex" - t.boolean "is_sample", default: false - t.integer "max_file_size", default: 0 - end - - create_table "historical_data", force: true do |t| - t.date "date", null: false - t.integer "active_user_count" - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "identities", force: true do |t| t.string "extern_uid" t.string "provider" @@ -411,21 +377,6 @@ ActiveRecord::Schema.define(version: 20150826001931) do add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree - create_table "ldap_group_links", force: true do |t| - t.string "cn", null: false - t.integer "group_access", null: false - t.integer "group_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - t.string "provider" - end - - create_table "licenses", force: true do |t| - t.text "data", null: false - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "members", force: true do |t| t.integer "access_level", null: false t.integer "source_id", null: false @@ -507,19 +458,18 @@ ActiveRecord::Schema.define(version: 20150826001931) do add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree create_table "namespaces", force: true do |t| - t.string "name", null: false - t.string "path", null: false + t.string "name", null: false + t.string "path", null: false t.integer "owner_id" t.datetime "created_at" t.datetime "updated_at" t.string "type" - t.string "description", default: "", null: false + t.string "description", default: "", null: false t.string "avatar" - t.boolean "membership_lock", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree - add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree + add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree @@ -593,14 +543,6 @@ ActiveRecord::Schema.define(version: 20150826001931) do add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree - create_table "project_group_links", force: true do |t| - t.integer "project_id", null: false - t.integer "group_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - t.integer "group_access", default: 30, null: false - end - create_table "project_import_data", force: true do |t| t.integer "project_id" t.text "data" @@ -613,28 +555,25 @@ ActiveRecord::Schema.define(version: 20150826001931) do t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false + t.boolean "issues_enabled", default: true, null: false + t.boolean "wall_enabled", default: true, null: false + t.boolean "merge_requests_enabled", default: true, null: false + t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.string "issues_tracker", default: "gitlab", null: false + t.string "issues_tracker", default: "gitlab", null: false t.string "issues_tracker_id" - t.boolean "snippets_enabled", default: true, null: false + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" t.string "import_url" - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", default: false, null: false + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", default: false, null: false t.string "avatar" t.string "import_status" - t.float "repository_size", default: 0.0 - t.integer "star_count", default: 0, null: false + t.float "repository_size", default: 0.0 + t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.text "merge_requests_template" - t.boolean "merge_requests_rebase_enabled", default: false - t.boolean "merge_requests_rebase_default", default: true - t.integer "commit_count", default: 0 + t.integer "commit_count", default: 0 end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree @@ -734,19 +673,13 @@ ActiveRecord::Schema.define(version: 20150826001931) do add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree - create_table "test", id: false, force: true do |t| - t.integer "col" - end - - add_index "test", ["col"], name: "index_name", unique: true, using: :btree - create_table "users", force: true do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" @@ -754,22 +687,22 @@ ActiveRecord::Schema.define(version: 20150826001931) do t.datetime "created_at" t.datetime "updated_at" t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false t.string "authentication_token" - t.integer "theme_id", default: 1, null: false + t.integer "theme_id", default: 1, null: false t.string "bio" - t.integer "failed_attempts", default: 0 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" t.datetime "last_credential_check_at" @@ -778,21 +711,20 @@ ActiveRecord::Schema.define(version: 20150826001931) do t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", default: "", null: false t.string "notification_email" - t.boolean "hide_no_password", default: false - t.boolean "password_automatically_set", default: false - t.datetime "admin_email_unsubscribed_at" + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false t.string "location" - t.string "public_email", default: "", null: false t.string "encrypted_otp_secret" t.string "encrypted_otp_secret_iv" t.string "encrypted_otp_secret_salt" - t.boolean "otp_required_for_login", default: false, null: false + t.boolean "otp_required_for_login", default: false, null: false t.text "otp_backup_codes" - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.string "public_email", default: "", null: false + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/spec/ci/controllers/commits_controller_spec.rb b/spec/ci/controllers/commits_controller_spec.rb deleted file mode 100644 index f32d6f8c126..00000000000 --- a/spec/ci/controllers/commits_controller_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require "spec_helper" - -describe CommitsController do - before do - @project = FactoryGirl.create :project - end - - describe "GET /status" do - it "returns status of commit" do - commit = FactoryGirl.create :commit, project: @project - get :status, id: commit.sha, ref_id: commit.ref, project_id: @project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "pending" - end - - it "returns not_found status" do - commit = FactoryGirl.create :commit, project: @project - get :status, id: commit.sha, ref_id: "deploy", project_id: @project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "not_found" - end - end -end diff --git a/spec/ci/controllers/projects_controller_spec.rb b/spec/ci/controllers/projects_controller_spec.rb deleted file mode 100644 index 0069a782511..00000000000 --- a/spec/ci/controllers/projects_controller_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -require "spec_helper" - -describe ProjectsController do - before do - @project = FactoryGirl.create :project - end - - describe "POST #build" do - it 'should respond 200 if params is ok' do - post :build, id: @project.id, - ref: 'master', - before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', - after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', - token: @project.token, - ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] - - - expect(response).to be_success - expect(response.code).to eq('201') - end - - it 'should respond 400 if push about removed branch' do - post :build, id: @project.id, - ref: 'master', - before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', - after: '0000000000000000000000000000000000000000', - token: @project.token, - ci_yaml_file: gitlab_ci_yaml - - expect(response).not_to be_success - expect(response.code).to eq('400') - end - - it 'should respond 400 if some params missed' do - post :build, id: @project.id, token: @project.token, ci_yaml_file: gitlab_ci_yaml - expect(response).not_to be_success - expect(response.code).to eq('400') - end - - it 'should respond 403 if token is wrong' do - post :build, id: @project.id, token: 'invalid-token' - expect(response).not_to be_success - expect(response.code).to eq('403') - end - end - - describe "POST /projects" do - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - - let (:user_data) do - data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) - data.merge("url" => gitlab_url) - end - - let(:user) do - User.new(user_data) - end - - it "creates project" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - Network.any_instance.stub(:enable_ci).and_return(true) - Network.any_instance.stub(:project_hooks).and_return(true) - - post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access - - expect(response.code).to eq('302') - expect(assigns(:project)).not_to be_a_new(Project) - end - - it "shows error" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - User.any_instance.stub(:can_manage_project?).and_return(false) - - post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access - - expect(response.code).to eq('302') - expect(flash[:alert]).to include("You have to have at least master role to enable CI for this project") - end - end - - describe "GET /gitlab" do - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - - let (:user_data) do - data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) - data.merge("url" => gitlab_url) - end - - let(:user) do - User.new(user_data) - end - - it "searches projects" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - Network.any_instance.should_receive(:projects).with(hash_including(search: 'str'), :authorized) - - xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access - - expect(response).to be_success - expect(response.code).to eq('200') - end - end -end diff --git a/spec/ci/factories/builds.rb b/spec/ci/factories/builds.rb deleted file mode 100644 index 346e0002bf5..00000000000 --- a/spec/ci/factories/builds.rb +++ /dev/null @@ -1,45 +0,0 @@ -# == Schema Information -# -# Table name: builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# commit_id :integer -# coverage :float -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :build do - started_at 'Di 29. Okt 09:51:28 CET 2013' - finished_at 'Di 29. Okt 09:53:28 CET 2013' - commands 'ls -a' - options do - { - image: "ruby:2.1", - services: ["postgres"] - } - end - - factory :not_started_build do - started_at nil - finished_at nil - end - end -end diff --git a/spec/ci/factories/commits.rb b/spec/ci/factories/commits.rb deleted file mode 100644 index 6fdd46fa74b..00000000000 --- a/spec/ci/factories/commits.rb +++ /dev/null @@ -1,75 +0,0 @@ -# == Schema Information -# -# Table name: commits -# -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime -# - -# Read about factories at https://github.com/thoughtbot/factory_girl -FactoryGirl.define do - factory :commit do - ref 'master' - before_sha '76de212e80737a608d939f648d959671fb0a0142' - sha '97de212e80737a608d939f648d959671fb0a0142' - push_data do - { - ref: 'refs/heads/master', - before: '76de212e80737a608d939f648d959671fb0a0142', - after: '97de212e80737a608d939f648d959671fb0a0142', - user_name: 'Git User', - user_email: 'git@example.com', - repository: { - name: 'test-data', - url: 'ssh://git@gitlab.com/test/test-data.git', - description: '', - homepage: 'http://gitlab.com/test/test-data' - }, - commits: [ - { - id: '97de212e80737a608d939f648d959671fb0a0142', - message: 'Test commit message', - timestamp: '2014-09-23T13:12:25+02:00', - url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142', - author: { - name: 'Git User', - email: 'git@user.com' - } - } - ], - total_commits_count: 1, - ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - } - end - - factory :commit_without_jobs do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({}) - commit.save - end - end - - factory :commit_with_one_job do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }}) - commit.save - end - end - - factory :commit_with_two_jobs do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }, spinach: { script: "ls" }}) - commit.save - end - end - end -end diff --git a/spec/ci/factories/events.rb b/spec/ci/factories/events.rb deleted file mode 100644 index 1dfa52e3529..00000000000 --- a/spec/ci/factories/events.rb +++ /dev/null @@ -1,24 +0,0 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# is_admin :integer -# description :text -# created_at :datetime -# updated_at :datetime -# - -FactoryGirl.define do - factory :event, class: Event do - sequence :description do |n| - "updated project settings#{n}" - end - - factory :admin_event do - is_admin true - end - end -end diff --git a/spec/ci/factories/projects.rb b/spec/ci/factories/projects.rb deleted file mode 100644 index fb5b563f2f2..00000000000 --- a/spec/ci/factories/projects.rb +++ /dev/null @@ -1,56 +0,0 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) not null -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :project_without_token, class: Project do - sequence :name do |n| - "GitLab / gitlab-shell#{n}" - end - - default_ref 'master' - - sequence :path do |n| - "gitlab/gitlab-shell#{n}" - end - - sequence :ssh_url_to_repo do |n| - "git@demo.gitlab.com:gitlab/gitlab-shell#{n}.git" - end - - sequence :gitlab_id - - factory :project do - token 'iPWx6WM4lhHNedGfBpPJNP' - end - - factory :public_project do - public true - end - end -end diff --git a/spec/ci/factories/runner_projects.rb b/spec/ci/factories/runner_projects.rb deleted file mode 100644 index b27632b3429..00000000000 --- a/spec/ci/factories/runner_projects.rb +++ /dev/null @@ -1,19 +0,0 @@ -# == Schema Information -# -# Table name: runner_projects -# -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :runner_project do - runner_id 1 - project_id 1 - end -end diff --git a/spec/ci/factories/runners.rb b/spec/ci/factories/runners.rb deleted file mode 100644 index 20a80f03268..00000000000 --- a/spec/ci/factories/runners.rb +++ /dev/null @@ -1,38 +0,0 @@ -# == Schema Information -# -# Table name: runners -# -# id :integer not null, primary key -# token :string(255) -# created_at :datetime -# updated_at :datetime -# description :string(255) -# contacted_at :datetime -# active :boolean default(TRUE), not null -# is_shared :boolean default(FALSE) -# name :string(255) -# version :string(255) -# revision :string(255) -# platform :string(255) -# architecture :string(255) -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :runner do - sequence :description do |n| - "My runner#{n}" - end - - platform "darwin" - - factory :shared_runner do - is_shared true - end - - factory :specific_runner do - is_shared false - end - end -end diff --git a/spec/ci/factories/trigger_requests.rb b/spec/ci/factories/trigger_requests.rb deleted file mode 100644 index c85d1027ce6..00000000000 --- a/spec/ci/factories/trigger_requests.rb +++ /dev/null @@ -1,13 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :trigger_request do - factory :trigger_request_with_variables do - variables do - { - TRIGGER_KEY: 'TRIGGER_VALUE' - } - end - end - end -end diff --git a/spec/ci/factories/triggers.rb b/spec/ci/factories/triggers.rb deleted file mode 100644 index a5af47b7d7f..00000000000 --- a/spec/ci/factories/triggers.rb +++ /dev/null @@ -1,9 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :trigger_without_token, class: Trigger do - factory :trigger do - token 'token' - end - end -end diff --git a/spec/ci/factories/users.rb b/spec/ci/factories/users.rb deleted file mode 100644 index 26b30eff0e6..00000000000 --- a/spec/ci/factories/users.rb +++ /dev/null @@ -1,6 +0,0 @@ -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :user do - end -end diff --git a/spec/ci/factories/web_hook.rb b/spec/ci/factories/web_hook.rb deleted file mode 100644 index 3c027fb4861..00000000000 --- a/spec/ci/factories/web_hook.rb +++ /dev/null @@ -1,6 +0,0 @@ -FactoryGirl.define do - factory :web_hook do - sequence(:url) { Faker::Internet.uri('http') } - project - end -end diff --git a/spec/ci/spec_helper.rb b/spec/ci/spec_helper.rb deleted file mode 100644 index 54d3068845d..00000000000 --- a/spec/ci/spec_helper.rb +++ /dev/null @@ -1,60 +0,0 @@ -if ENV['SIMPLECOV'] - require 'simplecov' - SimpleCov.start -end - -if ENV['COVERALLS'] - require 'coveralls' - Coveralls.wear!('rails') -end - -ENV["RAILS_ENV"] ||= 'test' -require File.expand_path("../../config/environment", __FILE__) -require 'rspec/rails' -require 'rspec/autorun' -require 'sidekiq/testing/inline' -require 'capybara/poltergeist' - -Capybara.javascript_driver = :poltergeist -Capybara.default_wait_time = 10 - -# Requires supporting ruby files with custom matchers and macros, etc, -# in spec/support/ and its subdirectories. -Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} - -require 'webmock/rspec' -WebMock.disable_net_connect!(allow_localhost: true) - -RSpec.configure do |config| - config.include LoginHelpers, type: :feature - - config.include StubGitlabCalls - config.include StubGitlabData - - # ## Mock Framework - # - # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: - # - # config.mock_with :mocha - # config.mock_with :flexmock - # config.mock_with :rr - - # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_path = "#{::Rails.root}/spec/fixtures" - - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = false - - # If true, the base class of anonymous controllers will be inferred - # automatically. This will be the default behavior in future versions of - # rspec-rails. - config.infer_base_class_for_anonymous_controllers = false - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = "random" -end diff --git a/spec/ci/support/api_helpers.rb b/spec/ci/support/api_helpers.rb deleted file mode 100644 index 555980f2ea7..00000000000 --- a/spec/ci/support/api_helpers.rb +++ /dev/null @@ -1,35 +0,0 @@ -module ApiHelpers - # Public: Prepend a request path with the path to the API - # - # path - Path to append - # user - User object - If provided, automatically appends private_token query - # string for authenticated requests - # - # Examples - # - # >> api('/issues') - # => "/api/v2/issues" - # - # >> api('/issues', User.last) - # => "/api/v2/issues?private_token=..." - # - # >> api('/issues?foo=bar', User.last) - # => "/api/v2/issues?foo=bar&private_token=..." - # - # Returns the relative path to the requested API resource - def api(path, user = nil) - "/api/#{API::API.version}#{path}" + - - # Normalize query string - (path.index('?') ? '' : '?') + - - # Append private_token if given a User object - (user.respond_to?(:private_token) ? - "&private_token=#{user.private_token}" : "") - end - - def json_response - JSON.parse(response.body) - end - -end diff --git a/spec/ci/support/db_cleaner.rb b/spec/ci/support/db_cleaner.rb deleted file mode 100644 index d2d532d9738..00000000000 --- a/spec/ci/support/db_cleaner.rb +++ /dev/null @@ -1,39 +0,0 @@ -# RSpec.configure do |config| - -# config.around(:each) do |example| -# DatabaseCleaner.strategy = :transaction -# DatabaseCleaner.clean_with(:truncation) -# DatabaseCleaner.cleaning do -# example.run -# end -# end - -# config.around(:each, js: true) do |example| -# DatabaseCleaner.strategy = :truncation -# DatabaseCleaner.clean_with(:truncation) -# DatabaseCleaner.cleaning do -# example.run -# end -# end -# end -RSpec.configure do |config| - config.before(:suite) do - DatabaseCleaner.clean_with(:truncation) - end - - config.before(:each) do - DatabaseCleaner.strategy = :transaction - end - - config.before(:each, :js => true) do - DatabaseCleaner.strategy = :truncation - end - - config.before(:each) do - DatabaseCleaner.start - end - - config.after(:each) do - DatabaseCleaner.clean - end -end diff --git a/spec/ci/support/gitlab_stubs/gitlab_ci.yml b/spec/ci/support/gitlab_stubs/gitlab_ci.yml deleted file mode 100644 index 3482145404e..00000000000 --- a/spec/ci/support/gitlab_stubs/gitlab_ci.yml +++ /dev/null @@ -1,63 +0,0 @@ -image: ruby:2.1 -services: - - postgres - -before_script: - - gem install bundler - - bundle install - - bundle exec rake db:create - -variables: - DB_NAME: postgres - -types: - - test - - deploy - - notify - -rspec: - script: "rake spec" - tags: - - ruby - - postgres - only: - - branches - -spinach: - script: "rake spinach" - allow_failure: true - tags: - - ruby - - mysql - except: - - tags - -staging: - script: "cap deploy stating" - type: deploy - tags: - - capistrano - - debian - except: - - stable - -production: - type: deploy - script: - - cap deploy production - - cap notify - tags: - - capistrano - - debian - only: - - master - - /^deploy-.*$/ - -dockerhub: - type: notify - script: "curl http://dockerhub/URL" - tags: - - ruby - - postgres - only: - - branches diff --git a/spec/ci/support/gitlab_stubs/project_8.json b/spec/ci/support/gitlab_stubs/project_8.json deleted file mode 100644 index f0a9fce859c..00000000000 --- a/spec/ci/support/gitlab_stubs/project_8.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "id":8, - "description":"ssh access and repository management app for GitLab", - "default_branch":"master", - "public":false, - "visibility_level":0, - "ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git", - "http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git", - "web_url":"http://demo.gitlab.com/gitlab/gitlab-shell", - "owner": { - "id":4, - "name":"GitLab", - "created_at":"2012-12-21T13:03:05Z" - }, - "name":"gitlab-shell", - "name_with_namespace":"GitLab / gitlab-shell", - "path":"gitlab-shell", - "path_with_namespace":"gitlab/gitlab-shell", - "issues_enabled":true, - "merge_requests_enabled":true, - "wall_enabled":false, - "wiki_enabled":true, - "snippets_enabled":false, - "created_at":"2013-03-20T13:28:53Z", - "last_activity_at":"2013-11-30T00:11:17Z", - "namespace":{ - "created_at":"2012-12-21T13:03:05Z", - "description":"Self hosted Git management software", - "id":4, - "name":"GitLab", - "owner_id":1, - "path":"gitlab", - "updated_at":"2013-03-20T13:29:13Z" - }, - "permissions":{ - "project_access": { - "access_level": 10, - "notification_level": 3 - }, - "group_access": { - "access_level": 50, - "notification_level": 3 - } - } -} \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/project_8_hooks.json b/spec/ci/support/gitlab_stubs/project_8_hooks.json deleted file mode 100644 index 93d51406d63..00000000000 --- a/spec/ci/support/gitlab_stubs/project_8_hooks.json +++ /dev/null @@ -1 +0,0 @@ -[{}] diff --git a/spec/ci/support/gitlab_stubs/projects.json b/spec/ci/support/gitlab_stubs/projects.json deleted file mode 100644 index ca42c14c5d8..00000000000 --- a/spec/ci/support/gitlab_stubs/projects.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":3,"description":"GitLab is open source software to collaborate on code. Create projects and repositories, manage access and do code reviews.","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlabhq.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlabhq.git","web_url":"http://demo.gitlab.com/gitlab/gitlabhq","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlabhq","name_with_namespace":"GitLab / gitlabhq","path":"gitlabhq","path_with_namespace":"gitlab/gitlabhq","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:34Z","last_activity_at":"2013-12-02T19:10:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":4,"description":"Component of GitLab CI. Web application","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-ci.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-ci.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-ci","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-ci","name_with_namespace":"GitLab / gitlab-ci","path":"gitlab-ci","path_with_namespace":"gitlab/gitlab-ci","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:50Z","last_activity_at":"2013-11-28T19:26:54Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":5,"description":"","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-recipes.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-recipes.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-recipes","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-recipes","name_with_namespace":"GitLab / gitlab-recipes","path":"gitlab-recipes","path_with_namespace":"gitlab/gitlab-recipes","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:07:02Z","last_activity_at":"2013-12-02T13:54:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":8,"description":"ssh access and repository management app for GitLab","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-shell","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-shell","name_with_namespace":"GitLab / gitlab-shell","path":"gitlab-shell","path_with_namespace":"gitlab/gitlab-shell","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-03-20T13:28:53Z","last_activity_at":"2013-11-30T00:11:17Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":9,"description":null,"default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab_git.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab_git.git","web_url":"http://demo.gitlab.com/gitlab/gitlab_git","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab_git","name_with_namespace":"GitLab / gitlab_git","path":"gitlab_git","path_with_namespace":"gitlab/gitlab_git","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-04-28T19:15:08Z","last_activity_at":"2013-12-02T13:07:13Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":10,"description":"ultra lite authorization library http://randx.github.com/six/\\r\\n ","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/six.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/six.git","web_url":"http://demo.gitlab.com/sandbox/six","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Six","name_with_namespace":"Sandbox / Six","path":"six","path_with_namespace":"sandbox/six","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:45:02Z","last_activity_at":"2013-11-29T11:30:56Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":11,"description":"Simple HTML5 Charts using the tag ","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/charts-js.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/charts-js.git","web_url":"http://demo.gitlab.com/sandbox/charts-js","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Charts.js","name_with_namespace":"Sandbox / Charts.js","path":"charts-js","path_with_namespace":"sandbox/charts-js","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:47:29Z","last_activity_at":"2013-12-02T15:18:11Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":13,"description":"","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/afro.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/afro.git","web_url":"http://demo.gitlab.com/sandbox/afro","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Afro","name_with_namespace":"Sandbox / Afro","path":"afro","path_with_namespace":"sandbox/afro","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-11-14T17:45:19Z","last_activity_at":"2013-12-02T17:41:45Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}}] \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/raw_project.yml b/spec/ci/support/gitlab_stubs/raw_project.yml deleted file mode 100644 index df2ce223d1f..00000000000 --- a/spec/ci/support/gitlab_stubs/raw_project.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- !ruby/object:OpenStruct -table: - :id: 189 - :description: Website at http://api.gitlab.org/ - :default_branch: master - :public: false - :visibility_level: 0 - :ssh_url_to_repo: dzaporozhets@localhost:gitlab/api-gitlab-org.git - :http_url_to_repo: http://localhost:3000/gitlab/api-gitlab-org.git - :web_url: http://localhost:3000/gitlab/api-gitlab-org - :owner: - id: 1 - name: GitLab - created_at: '2012-10-03T09:59:57.000Z' - :name: api.gitlab.org - :name_with_namespace: GitLab / api.gitlab.org - :path: api-gitlab-org - :path_with_namespace: gitlab/api-gitlab-org - :issues_enabled: true - :merge_requests_enabled: true - :wall_enabled: false - :wiki_enabled: false - :snippets_enabled: false - :created_at: '2013-06-06T12:29:39.000Z' - :last_activity_at: '2013-12-06T20:29:42.000Z' - :namespace: - id: 1 - name: GitLab - path: gitlab - owner_id: 1 - created_at: '2012-10-03T09:59:57.000Z' - updated_at: '2014-01-28T08:49:53.000Z' - description: Self hosted Git management software - avatar: - url: /uploads/group/avatar/1/0-vader-profile.jpg - diff --git a/spec/ci/support/gitlab_stubs/session.json b/spec/ci/support/gitlab_stubs/session.json deleted file mode 100644 index ce8dfe5ae75..00000000000 --- a/spec/ci/support/gitlab_stubs/session.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "id":2, - "username":"jsmith", - "email":"test@test.com", - "name":"John Smith", - "bio":"", - "skype":"aertert", - "linkedin":"", - "twitter":"", - "theme_id":2,"color_scheme_id":2, - "state":"active", - "created_at":"2012-12-21T13:02:20Z", - "extern_uid":null, - "provider":null, - "is_admin":false, - "can_create_group":false, - "can_create_project":false, - "private_token":"Wvjy2Krpb7y8xi93owUz", - "access_token":"Wvjy2Krpb7y8xi93owUz" -} \ No newline at end of file diff --git a/spec/ci/support/gitlab_stubs/user.json b/spec/ci/support/gitlab_stubs/user.json deleted file mode 100644 index ce8dfe5ae75..00000000000 --- a/spec/ci/support/gitlab_stubs/user.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "id":2, - "username":"jsmith", - "email":"test@test.com", - "name":"John Smith", - "bio":"", - "skype":"aertert", - "linkedin":"", - "twitter":"", - "theme_id":2,"color_scheme_id":2, - "state":"active", - "created_at":"2012-12-21T13:02:20Z", - "extern_uid":null, - "provider":null, - "is_admin":false, - "can_create_group":false, - "can_create_project":false, - "private_token":"Wvjy2Krpb7y8xi93owUz", - "access_token":"Wvjy2Krpb7y8xi93owUz" -} \ No newline at end of file diff --git a/spec/ci/support/login_helpers.rb b/spec/ci/support/login_helpers.rb deleted file mode 100644 index ebd9693f8a4..00000000000 --- a/spec/ci/support/login_helpers.rb +++ /dev/null @@ -1,22 +0,0 @@ -module LoginHelpers - def login_as(role) - raise 'Only :user allowed' unless role == :user - stub_gitlab_calls - login_with(:user) - end - - # Internal: Login as the specified user - # - # user - User instance to login with - def login_with(user) - visit callback_user_sessions_path(code: "some_auth_code_here") - end - - def logout - click_link "Logout" rescue nil - end - - def skip_admin_auth - ApplicationController.any_instance.stub(authenticate_admin!: true) - end -end diff --git a/spec/ci/support/monkey_patches/oauth2.rb b/spec/ci/support/monkey_patches/oauth2.rb deleted file mode 100644 index dfd5e319f00..00000000000 --- a/spec/ci/support/monkey_patches/oauth2.rb +++ /dev/null @@ -1,7 +0,0 @@ -module OAuth2 - class Client - def get_token(params, access_token_opts = {}, access_token_class = AccessToken) - OpenStruct.new(token: "some_token") - end - end -end \ No newline at end of file diff --git a/spec/ci/support/setup_builds_storage.rb b/spec/ci/support/setup_builds_storage.rb deleted file mode 100644 index cafc8dee918..00000000000 --- a/spec/ci/support/setup_builds_storage.rb +++ /dev/null @@ -1,16 +0,0 @@ -RSpec.configure do |config| - def builds_path - Rails.root.join('tmp/builds_test') - end - - config.before(:each) do - FileUtils.mkdir_p(builds_path) - Ci::Settings.gitlab_ci['builds_path'] = builds_path - end - - config.after(:suite) do - Dir.chdir(builds_path) do - `ls | grep -v .gitkeep | xargs rm -r` - end - end -end diff --git a/spec/ci/support/stub_gitlab_calls.rb b/spec/ci/support/stub_gitlab_calls.rb deleted file mode 100644 index 931ef963c0f..00000000000 --- a/spec/ci/support/stub_gitlab_calls.rb +++ /dev/null @@ -1,77 +0,0 @@ -module StubGitlabCalls - def stub_gitlab_calls - stub_session - stub_user - stub_project_8 - stub_project_8_hooks - stub_projects - stub_projects_owned - stub_ci_enable - end - - def stub_js_gitlab_calls - Network.any_instance.stub(:projects) { project_hash_array } - end - - private - - def gitlab_url - GitlabCi.config.gitlab_server.url - end - - def stub_session - f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json')) - - stub_request(:post, "#{gitlab_url}api/v3/session.json"). - with(:body => "{\"email\":\"test@test.com\",\"password\":\"123456\"}", - :headers => {'Content-Type'=>'application/json'}). - to_return(:status => 201, :body => f, :headers => {'Content-Type'=>'application/json'}) - end - - def stub_user - f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) - - stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) - - stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) - end - - def stub_project_8 - data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8.json')) - Network.any_instance.stub(:project).and_return(JSON.parse(data)) - end - - def stub_project_8_hooks - data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8_hooks.json')) - Network.any_instance.stub(:project_hooks).and_return(JSON.parse(data)) - end - - def stub_projects - f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) - - stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) - end - - def stub_projects_owned - stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => "", :headers => {}) - end - - def stub_ci_enable - stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => "", :headers => {}) - end - - def project_hash_array - f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) - return JSON.parse f - end -end diff --git a/spec/ci/support/stub_gitlab_data.rb b/spec/ci/support/stub_gitlab_data.rb deleted file mode 100644 index fa402f35b95..00000000000 --- a/spec/ci/support/stub_gitlab_data.rb +++ /dev/null @@ -1,5 +0,0 @@ -module StubGitlabData - def gitlab_ci_yaml - File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - end -end diff --git a/spec/controllers/ci/commits_controller_spec.rb b/spec/controllers/ci/commits_controller_spec.rb new file mode 100644 index 00000000000..b71e7505731 --- /dev/null +++ b/spec/controllers/ci/commits_controller_spec.rb @@ -0,0 +1,27 @@ +require "spec_helper" + +describe Ci::CommitsController do + before do + @project = FactoryGirl.create :ci_project + end + + describe "GET /status" do + it "returns status of commit" do + commit = FactoryGirl.create :ci_commit, project: @project + get :status, id: commit.sha, ref_id: commit.ref, project_id: @project.id + + expect(response).to be_success + expect(response.code).to eq('200') + JSON.parse(response.body)["status"] == "pending" + end + + it "returns not_found status" do + commit = FactoryGirl.create :ci_commit, project: @project + get :status, id: commit.sha, ref_id: "deploy", project_id: @project.id + + expect(response).to be_success + expect(response.code).to eq('200') + JSON.parse(response.body)["status"] == "not_found" + end + end +end diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb new file mode 100644 index 00000000000..0069a782511 --- /dev/null +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -0,0 +1,108 @@ +require "spec_helper" + +describe ProjectsController do + before do + @project = FactoryGirl.create :project + end + + describe "POST #build" do + it 'should respond 200 if params is ok' do + post :build, id: @project.id, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', + token: @project.token, + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + + + expect(response).to be_success + expect(response.code).to eq('201') + end + + it 'should respond 400 if push about removed branch' do + post :build, id: @project.id, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '0000000000000000000000000000000000000000', + token: @project.token, + ci_yaml_file: gitlab_ci_yaml + + expect(response).not_to be_success + expect(response.code).to eq('400') + end + + it 'should respond 400 if some params missed' do + post :build, id: @project.id, token: @project.token, ci_yaml_file: gitlab_ci_yaml + expect(response).not_to be_success + expect(response.code).to eq('400') + end + + it 'should respond 403 if token is wrong' do + post :build, id: @project.id, token: 'invalid-token' + expect(response).not_to be_success + expect(response.code).to eq('403') + end + end + + describe "POST /projects" do + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + + let (:user_data) do + data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + data.merge("url" => gitlab_url) + end + + let(:user) do + User.new(user_data) + end + + it "creates project" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + Network.any_instance.stub(:enable_ci).and_return(true) + Network.any_instance.stub(:project_hooks).and_return(true) + + post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access + + expect(response.code).to eq('302') + expect(assigns(:project)).not_to be_a_new(Project) + end + + it "shows error" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + User.any_instance.stub(:can_manage_project?).and_return(false) + + post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access + + expect(response.code).to eq('302') + expect(flash[:alert]).to include("You have to have at least master role to enable CI for this project") + end + end + + describe "GET /gitlab" do + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + + let (:user_data) do + data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + data.merge("url" => gitlab_url) + end + + let(:user) do + User.new(user_data) + end + + it "searches projects" do + allow(controller).to receive(:reset_cache) { true } + allow(controller).to receive(:current_user) { user } + Network.any_instance.should_receive(:projects).with(hash_including(search: 'str'), :authorized) + + xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access + + expect(response).to be_success + expect(response.code).to eq('200') + end + end +end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb new file mode 100644 index 00000000000..35a84b1e6eb --- /dev/null +++ b/spec/factories/ci/builds.rb @@ -0,0 +1,45 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_build, class: Ci::Build do + started_at 'Di 29. Okt 09:51:28 CET 2013' + finished_at 'Di 29. Okt 09:53:28 CET 2013' + commands 'ls -a' + options do + { + image: "ruby:2.1", + services: ["postgres"] + } + end + + factory :not_started_build do + started_at nil + finished_at nil + end + end +end diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb new file mode 100644 index 00000000000..c1d42b607c3 --- /dev/null +++ b/spec/factories/ci/commits.rb @@ -0,0 +1,75 @@ +# == Schema Information +# +# Table name: commits +# +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# + +# Read about factories at https://github.com/thoughtbot/factory_girl +FactoryGirl.define do + factory :ci_commit, class: Ci::Commit do + ref 'master' + before_sha '76de212e80737a608d939f648d959671fb0a0142' + sha '97de212e80737a608d939f648d959671fb0a0142' + push_data do + { + ref: 'refs/heads/master', + before: '76de212e80737a608d939f648d959671fb0a0142', + after: '97de212e80737a608d939f648d959671fb0a0142', + user_name: 'Git User', + user_email: 'git@example.com', + repository: { + name: 'test-data', + url: 'ssh://git@gitlab.com/test/test-data.git', + description: '', + homepage: 'http://gitlab.com/test/test-data' + }, + commits: [ + { + id: '97de212e80737a608d939f648d959671fb0a0142', + message: 'Test commit message', + timestamp: '2014-09-23T13:12:25+02:00', + url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142', + author: { + name: 'Git User', + email: 'git@user.com' + } + } + ], + total_commits_count: 1, + ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + } + end + + factory :ci_commit_without_jobs do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({}) + commit.save + end + end + + factory :ci_commit_with_one_job do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }}) + commit.save + end + end + + factory :ci_commit_with_two_jobs do + after(:create) do |commit, evaluator| + commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }, spinach: { script: "ls" }}) + commit.save + end + end + end +end diff --git a/spec/factories/ci/events.rb b/spec/factories/ci/events.rb new file mode 100644 index 00000000000..03450751596 --- /dev/null +++ b/spec/factories/ci/events.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: events +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# is_admin :integer +# description :text +# created_at :datetime +# updated_at :datetime +# + +FactoryGirl.define do + factory :ci_event, class: Ci::Event do + sequence :description do |n| + "updated project settings#{n}" + end + + factory :admin_event do + is_admin true + end + end +end diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb new file mode 100644 index 00000000000..e6be88fa585 --- /dev/null +++ b/spec/factories/ci/projects.rb @@ -0,0 +1,56 @@ +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) not null +# timeout :integer default(3600), not null +# created_at :datetime +# updated_at :datetime +# token :string(255) +# default_ref :string(255) +# path :string(255) +# always_build :boolean default(FALSE), not null +# polling_interval :integer +# public :boolean default(FALSE), not null +# ssh_url_to_repo :string(255) +# gitlab_id :integer +# allow_git_fetch :boolean default(TRUE), not null +# email_recipients :string(255) default(""), not null +# email_add_pusher :boolean default(TRUE), not null +# email_only_broken_builds :boolean default(TRUE), not null +# skip_refs :string(255) +# coverage_regex :string(255) +# shared_runners_enabled :boolean default(FALSE) +# generated_yaml_config :text +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_project_without_token, class: Ci::Project do + sequence :name do |n| + "GitLab / gitlab-shell#{n}" + end + + default_ref 'master' + + sequence :path do |n| + "gitlab/gitlab-shell#{n}" + end + + sequence :ssh_url_to_repo do |n| + "git@demo.gitlab.com:gitlab/gitlab-shell#{n}.git" + end + + sequence :gitlab_id + + factory :ci_project do + token 'iPWx6WM4lhHNedGfBpPJNP' + end + + factory :ci_public_project do + public true + end + end +end diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb new file mode 100644 index 00000000000..3aa14ca434d --- /dev/null +++ b/spec/factories/ci/runner_projects.rb @@ -0,0 +1,19 @@ +# == Schema Information +# +# Table name: runner_projects +# +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_runner_project, class: Ci::RunnerProject do + runner_id 1 + project_id 1 + end +end diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb new file mode 100644 index 00000000000..fec56b438fa --- /dev/null +++ b/spec/factories/ci/runners.rb @@ -0,0 +1,38 @@ +# == Schema Information +# +# Table name: runners +# +# id :integer not null, primary key +# token :string(255) +# created_at :datetime +# updated_at :datetime +# description :string(255) +# contacted_at :datetime +# active :boolean default(TRUE), not null +# is_shared :boolean default(FALSE) +# name :string(255) +# version :string(255) +# revision :string(255) +# platform :string(255) +# architecture :string(255) +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_runner, class: Ci::Runner do + sequence :description do |n| + "My runner#{n}" + end + + platform "darwin" + + factory :shared_runner do + is_shared true + end + + factory :specific_runner do + is_shared false + end + end +end diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb new file mode 100644 index 00000000000..c85d1027ce6 --- /dev/null +++ b/spec/factories/ci/trigger_requests.rb @@ -0,0 +1,13 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :trigger_request do + factory :trigger_request_with_variables do + variables do + { + TRIGGER_KEY: 'TRIGGER_VALUE' + } + end + end + end +end diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb new file mode 100644 index 00000000000..38cd3cbceb6 --- /dev/null +++ b/spec/factories/ci/triggers.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_trigger_without_token, class: Ci::Trigger do + factory :trigger do + token 'token' + end + end +end diff --git a/spec/factories/ci/web_hook.rb b/spec/factories/ci/web_hook.rb new file mode 100644 index 00000000000..1fde5805c94 --- /dev/null +++ b/spec/factories/ci/web_hook.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :ci_web_hook, class: Ci::WebHook do + sequence(:url) { Faker::Internet.uri('http') } + project + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d0f1873ee2d..8442d3f4445 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -29,6 +29,9 @@ RSpec.configure do |config| config.include LoginHelpers, type: :request config.include StubConfiguration config.include TestEnv + config.include StubGitlabCalls + config.include StubGitlabData + config.infer_spec_type_from_file_location! config.raise_errors_for_deprecations! diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml new file mode 100644 index 00000000000..3482145404e --- /dev/null +++ b/spec/support/gitlab_stubs/gitlab_ci.yml @@ -0,0 +1,63 @@ +image: ruby:2.1 +services: + - postgres + +before_script: + - gem install bundler + - bundle install + - bundle exec rake db:create + +variables: + DB_NAME: postgres + +types: + - test + - deploy + - notify + +rspec: + script: "rake spec" + tags: + - ruby + - postgres + only: + - branches + +spinach: + script: "rake spinach" + allow_failure: true + tags: + - ruby + - mysql + except: + - tags + +staging: + script: "cap deploy stating" + type: deploy + tags: + - capistrano + - debian + except: + - stable + +production: + type: deploy + script: + - cap deploy production + - cap notify + tags: + - capistrano + - debian + only: + - master + - /^deploy-.*$/ + +dockerhub: + type: notify + script: "curl http://dockerhub/URL" + tags: + - ruby + - postgres + only: + - branches diff --git a/spec/support/gitlab_stubs/project_8.json b/spec/support/gitlab_stubs/project_8.json new file mode 100644 index 00000000000..f0a9fce859c --- /dev/null +++ b/spec/support/gitlab_stubs/project_8.json @@ -0,0 +1,45 @@ +{ + "id":8, + "description":"ssh access and repository management app for GitLab", + "default_branch":"master", + "public":false, + "visibility_level":0, + "ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git", + "http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git", + "web_url":"http://demo.gitlab.com/gitlab/gitlab-shell", + "owner": { + "id":4, + "name":"GitLab", + "created_at":"2012-12-21T13:03:05Z" + }, + "name":"gitlab-shell", + "name_with_namespace":"GitLab / gitlab-shell", + "path":"gitlab-shell", + "path_with_namespace":"gitlab/gitlab-shell", + "issues_enabled":true, + "merge_requests_enabled":true, + "wall_enabled":false, + "wiki_enabled":true, + "snippets_enabled":false, + "created_at":"2013-03-20T13:28:53Z", + "last_activity_at":"2013-11-30T00:11:17Z", + "namespace":{ + "created_at":"2012-12-21T13:03:05Z", + "description":"Self hosted Git management software", + "id":4, + "name":"GitLab", + "owner_id":1, + "path":"gitlab", + "updated_at":"2013-03-20T13:29:13Z" + }, + "permissions":{ + "project_access": { + "access_level": 10, + "notification_level": 3 + }, + "group_access": { + "access_level": 50, + "notification_level": 3 + } + } +} \ No newline at end of file diff --git a/spec/support/gitlab_stubs/project_8_hooks.json b/spec/support/gitlab_stubs/project_8_hooks.json new file mode 100644 index 00000000000..93d51406d63 --- /dev/null +++ b/spec/support/gitlab_stubs/project_8_hooks.json @@ -0,0 +1 @@ +[{}] diff --git a/spec/support/gitlab_stubs/projects.json b/spec/support/gitlab_stubs/projects.json new file mode 100644 index 00000000000..ca42c14c5d8 --- /dev/null +++ b/spec/support/gitlab_stubs/projects.json @@ -0,0 +1 @@ +[{"id":3,"description":"GitLab is open source software to collaborate on code. Create projects and repositories, manage access and do code reviews.","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlabhq.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlabhq.git","web_url":"http://demo.gitlab.com/gitlab/gitlabhq","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlabhq","name_with_namespace":"GitLab / gitlabhq","path":"gitlabhq","path_with_namespace":"gitlab/gitlabhq","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:34Z","last_activity_at":"2013-12-02T19:10:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":4,"description":"Component of GitLab CI. Web application","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-ci.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-ci.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-ci","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-ci","name_with_namespace":"GitLab / gitlab-ci","path":"gitlab-ci","path_with_namespace":"gitlab/gitlab-ci","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:06:50Z","last_activity_at":"2013-11-28T19:26:54Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":5,"description":"","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-recipes.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-recipes.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-recipes","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-recipes","name_with_namespace":"GitLab / gitlab-recipes","path":"gitlab-recipes","path_with_namespace":"gitlab/gitlab-recipes","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":true,"wiki_enabled":true,"snippets_enabled":true,"created_at":"2012-12-21T13:07:02Z","last_activity_at":"2013-12-02T13:54:10Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":8,"description":"ssh access and repository management app for GitLab","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab-shell.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab-shell.git","web_url":"http://demo.gitlab.com/gitlab/gitlab-shell","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab-shell","name_with_namespace":"GitLab / gitlab-shell","path":"gitlab-shell","path_with_namespace":"gitlab/gitlab-shell","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-03-20T13:28:53Z","last_activity_at":"2013-11-30T00:11:17Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":9,"description":null,"default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:gitlab/gitlab_git.git","http_url_to_repo":"http://demo.gitlab.com/gitlab/gitlab_git.git","web_url":"http://demo.gitlab.com/gitlab/gitlab_git","owner":{"id":4,"name":"GitLab","created_at":"2012-12-21T13:03:05Z"},"name":"gitlab_git","name_with_namespace":"GitLab / gitlab_git","path":"gitlab_git","path_with_namespace":"gitlab/gitlab_git","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-04-28T19:15:08Z","last_activity_at":"2013-12-02T13:07:13Z","namespace":{"created_at":"2012-12-21T13:03:05Z","description":"Self hosted Git management software","id":4,"name":"GitLab","owner_id":1,"path":"gitlab","updated_at":"2013-03-20T13:29:13Z"}},{"id":10,"description":"ultra lite authorization library http://randx.github.com/six/\\r\\n ","default_branch":"master","public":true,"visibility_level":20,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/six.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/six.git","web_url":"http://demo.gitlab.com/sandbox/six","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Six","name_with_namespace":"Sandbox / Six","path":"six","path_with_namespace":"sandbox/six","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:45:02Z","last_activity_at":"2013-11-29T11:30:56Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":11,"description":"Simple HTML5 Charts using the tag ","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/charts-js.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/charts-js.git","web_url":"http://demo.gitlab.com/sandbox/charts-js","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Charts.js","name_with_namespace":"Sandbox / Charts.js","path":"charts-js","path_with_namespace":"sandbox/charts-js","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-08-01T16:47:29Z","last_activity_at":"2013-12-02T15:18:11Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}},{"id":13,"description":"","default_branch":"master","public":false,"visibility_level":0,"ssh_url_to_repo":"git@demo.gitlab.com:sandbox/afro.git","http_url_to_repo":"http://demo.gitlab.com/sandbox/afro.git","web_url":"http://demo.gitlab.com/sandbox/afro","owner":{"id":8,"name":"Sandbox","created_at":"2013-08-01T16:44:17Z"},"name":"Afro","name_with_namespace":"Sandbox / Afro","path":"afro","path_with_namespace":"sandbox/afro","issues_enabled":true,"merge_requests_enabled":true,"wall_enabled":false,"wiki_enabled":true,"snippets_enabled":false,"created_at":"2013-11-14T17:45:19Z","last_activity_at":"2013-12-02T17:41:45Z","namespace":{"created_at":"2013-08-01T16:44:17Z","description":"","id":8,"name":"Sandbox","owner_id":1,"path":"sandbox","updated_at":"2013-08-01T16:44:17Z"}}] \ No newline at end of file diff --git a/spec/support/gitlab_stubs/raw_project.yml b/spec/support/gitlab_stubs/raw_project.yml new file mode 100644 index 00000000000..df2ce223d1f --- /dev/null +++ b/spec/support/gitlab_stubs/raw_project.yml @@ -0,0 +1,36 @@ +--- !ruby/object:OpenStruct +table: + :id: 189 + :description: Website at http://api.gitlab.org/ + :default_branch: master + :public: false + :visibility_level: 0 + :ssh_url_to_repo: dzaporozhets@localhost:gitlab/api-gitlab-org.git + :http_url_to_repo: http://localhost:3000/gitlab/api-gitlab-org.git + :web_url: http://localhost:3000/gitlab/api-gitlab-org + :owner: + id: 1 + name: GitLab + created_at: '2012-10-03T09:59:57.000Z' + :name: api.gitlab.org + :name_with_namespace: GitLab / api.gitlab.org + :path: api-gitlab-org + :path_with_namespace: gitlab/api-gitlab-org + :issues_enabled: true + :merge_requests_enabled: true + :wall_enabled: false + :wiki_enabled: false + :snippets_enabled: false + :created_at: '2013-06-06T12:29:39.000Z' + :last_activity_at: '2013-12-06T20:29:42.000Z' + :namespace: + id: 1 + name: GitLab + path: gitlab + owner_id: 1 + created_at: '2012-10-03T09:59:57.000Z' + updated_at: '2014-01-28T08:49:53.000Z' + description: Self hosted Git management software + avatar: + url: /uploads/group/avatar/1/0-vader-profile.jpg + diff --git a/spec/support/gitlab_stubs/session.json b/spec/support/gitlab_stubs/session.json new file mode 100644 index 00000000000..ce8dfe5ae75 --- /dev/null +++ b/spec/support/gitlab_stubs/session.json @@ -0,0 +1,20 @@ +{ + "id":2, + "username":"jsmith", + "email":"test@test.com", + "name":"John Smith", + "bio":"", + "skype":"aertert", + "linkedin":"", + "twitter":"", + "theme_id":2,"color_scheme_id":2, + "state":"active", + "created_at":"2012-12-21T13:02:20Z", + "extern_uid":null, + "provider":null, + "is_admin":false, + "can_create_group":false, + "can_create_project":false, + "private_token":"Wvjy2Krpb7y8xi93owUz", + "access_token":"Wvjy2Krpb7y8xi93owUz" +} \ No newline at end of file diff --git a/spec/support/gitlab_stubs/user.json b/spec/support/gitlab_stubs/user.json new file mode 100644 index 00000000000..ce8dfe5ae75 --- /dev/null +++ b/spec/support/gitlab_stubs/user.json @@ -0,0 +1,20 @@ +{ + "id":2, + "username":"jsmith", + "email":"test@test.com", + "name":"John Smith", + "bio":"", + "skype":"aertert", + "linkedin":"", + "twitter":"", + "theme_id":2,"color_scheme_id":2, + "state":"active", + "created_at":"2012-12-21T13:02:20Z", + "extern_uid":null, + "provider":null, + "is_admin":false, + "can_create_group":false, + "can_create_project":false, + "private_token":"Wvjy2Krpb7y8xi93owUz", + "access_token":"Wvjy2Krpb7y8xi93owUz" +} \ No newline at end of file diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb new file mode 100644 index 00000000000..cafc8dee918 --- /dev/null +++ b/spec/support/setup_builds_storage.rb @@ -0,0 +1,16 @@ +RSpec.configure do |config| + def builds_path + Rails.root.join('tmp/builds_test') + end + + config.before(:each) do + FileUtils.mkdir_p(builds_path) + Ci::Settings.gitlab_ci['builds_path'] = builds_path + end + + config.after(:suite) do + Dir.chdir(builds_path) do + `ls | grep -v .gitkeep | xargs rm -r` + end + end +end diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb new file mode 100644 index 00000000000..931ef963c0f --- /dev/null +++ b/spec/support/stub_gitlab_calls.rb @@ -0,0 +1,77 @@ +module StubGitlabCalls + def stub_gitlab_calls + stub_session + stub_user + stub_project_8 + stub_project_8_hooks + stub_projects + stub_projects_owned + stub_ci_enable + end + + def stub_js_gitlab_calls + Network.any_instance.stub(:projects) { project_hash_array } + end + + private + + def gitlab_url + GitlabCi.config.gitlab_server.url + end + + def stub_session + f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json')) + + stub_request(:post, "#{gitlab_url}api/v3/session.json"). + with(:body => "{\"email\":\"test@test.com\",\"password\":\"123456\"}", + :headers => {'Content-Type'=>'application/json'}). + to_return(:status => 201, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_user + f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) + + stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + + stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_project_8 + data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8.json')) + Network.any_instance.stub(:project).and_return(JSON.parse(data)) + end + + def stub_project_8_hooks + data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8_hooks.json')) + Network.any_instance.stub(:project_hooks).and_return(JSON.parse(data)) + end + + def stub_projects + f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) + + stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + end + + def stub_projects_owned + stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => "", :headers => {}) + end + + def stub_ci_enable + stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz"). + with(:headers => {'Content-Type'=>'application/json'}). + to_return(:status => 200, :body => "", :headers => {}) + end + + def project_hash_array + f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) + return JSON.parse f + end +end diff --git a/spec/support/stub_gitlab_data.rb b/spec/support/stub_gitlab_data.rb new file mode 100644 index 00000000000..fa402f35b95 --- /dev/null +++ b/spec/support/stub_gitlab_data.rb @@ -0,0 +1,5 @@ +module StubGitlabData + def gitlab_ci_yaml + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end +end -- cgit v1.2.1 From cc4ef4da11d46761ab0ce4fbd6b032a7e01baba9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 14:17:16 +0200 Subject: Refactor CI tests --- spec/ci/features/admin/builds_spec.rb | 71 ----- spec/ci/features/admin/events_spec.rb | 20 -- spec/ci/features/admin/projects_spec.rb | 19 -- spec/ci/features/admin/runners_spec.rb | 63 ---- spec/ci/features/builds_spec.rb | 57 ---- spec/ci/features/commits_spec.rb | 66 ---- spec/ci/features/events_spec.rb | 20 -- spec/ci/features/lint_spec.rb | 28 -- spec/ci/features/projects_spec.rb | 57 ---- spec/ci/features/runners_spec.rb | 98 ------ spec/ci/features/triggers_spec.rb | 26 -- spec/ci/features/variables_spec.rb | 26 -- spec/ci/helpers/application_helper_spec.rb | 37 --- spec/ci/helpers/runners_helper_spec.rb | 18 -- spec/ci/helpers/user_helper_spec.rb | 49 --- spec/ci/helpers/user_sessions_helper_spec.rb | 69 ---- spec/ci/lib/ansi2html_spec.rb | 133 -------- spec/ci/lib/charts_spec.rb | 17 - spec/ci/lib/gitlab_ci_yaml_processor_spec.rb | 311 ------------------ spec/ci/lib/upgrader_spec.rb | 39 --- spec/ci/mailers/notify_spec.rb | 36 --- spec/ci/models/build_spec.rb | 350 --------------------- spec/ci/models/commit_spec.rb | 264 ---------------- spec/ci/models/mail_service_spec.rb | 184 ----------- spec/ci/models/network_spec.rb | 54 ---- .../project_services/hip_chat_message_spec.rb | 74 ----- .../project_services/hip_chat_service_spec.rb | 75 ----- .../models/project_services/slack_message_spec.rb | 84 ----- .../models/project_services/slack_service_spec.rb | 58 ---- spec/ci/models/project_spec.rb | 185 ----------- spec/ci/models/runner_project_spec.rb | 16 - spec/ci/models/runner_spec.rb | 70 ----- spec/ci/models/service_spec.rb | 49 --- spec/ci/models/trigger_spec.rb | 17 - spec/ci/models/user_spec.rb | 100 ------ spec/ci/models/variable_spec.rb | 44 --- spec/ci/models/web_hook_spec.rb | 64 ---- spec/ci/requests/api/builds_spec.rb | 115 ------- spec/ci/requests/api/commits_spec.rb | 65 ---- spec/ci/requests/api/forks_spec.rb | 60 ---- spec/ci/requests/api/projects_spec.rb | 251 --------------- spec/ci/requests/api/runners_spec.rb | 83 ----- spec/ci/requests/api/triggers_spec.rb | 78 ----- spec/ci/requests/builds_spec.rb | 18 -- spec/ci/requests/commits_spec.rb | 17 - spec/ci/services/create_commit_service_spec.rb | 130 -------- spec/ci/services/create_project_service_spec.rb | 40 --- .../create_trigger_request_service_spec.rb | 52 --- spec/ci/services/event_service_spec.rb | 34 -- spec/ci/services/image_for_build_service_spec.rb | 46 --- spec/ci/services/register_build_service_spec.rb | 89 ------ spec/ci/services/web_hook_service_spec.rb | 36 --- spec/ci/six.tar.gz | Bin 61937 -> 0 bytes spec/controllers/ci/projects_controller_spec.rb | 18 +- spec/factories/ci/trigger_requests.rb | 4 +- spec/factories/ci/triggers.rb | 2 +- spec/features/ci/admin/builds_spec.rb | 71 +++++ spec/features/ci/admin/events_spec.rb | 20 ++ spec/features/ci/admin/projects_spec.rb | 19 ++ spec/features/ci/admin/runners_spec.rb | 63 ++++ spec/features/ci/builds_spec.rb | 57 ++++ spec/features/ci/commits_spec.rb | 66 ++++ spec/features/ci/events_spec.rb | 20 ++ spec/features/ci/lint_spec.rb | 28 ++ spec/features/ci/projects_spec.rb | 57 ++++ spec/features/ci/runners_spec.rb | 98 ++++++ spec/features/ci/triggers_spec.rb | 26 ++ spec/features/ci/variables_spec.rb | 26 ++ spec/helpers/ci/application_helper_spec.rb | 37 +++ spec/helpers/ci/runners_helper_spec.rb | 18 ++ spec/helpers/ci/user_helper_spec.rb | 49 +++ spec/helpers/ci/user_sessions_helper_spec.rb | 69 ++++ spec/lib/ci/ansi2html_spec.rb | 133 ++++++++ spec/lib/ci/charts_spec.rb | 17 + spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 311 ++++++++++++++++++ spec/lib/ci/upgrader_spec.rb | 39 +++ spec/mailers/ci/notify_spec.rb | 36 +++ spec/models/ci/build_spec.rb | 350 +++++++++++++++++++++ spec/models/ci/commit_spec.rb | 264 ++++++++++++++++ spec/models/ci/mail_service_spec.rb | 184 +++++++++++ spec/models/ci/network_spec.rb | 54 ++++ .../ci/project_services/hip_chat_message_spec.rb | 74 +++++ .../ci/project_services/hip_chat_service_spec.rb | 75 +++++ .../ci/project_services/slack_message_spec.rb | 84 +++++ .../ci/project_services/slack_service_spec.rb | 58 ++++ spec/models/ci/project_spec.rb | 185 +++++++++++ spec/models/ci/runner_project_spec.rb | 16 + spec/models/ci/runner_spec.rb | 70 +++++ spec/models/ci/service_spec.rb | 49 +++ spec/models/ci/trigger_spec.rb | 17 + spec/models/ci/user_spec.rb | 100 ++++++ spec/models/ci/variable_spec.rb | 44 +++ spec/models/ci/web_hook_spec.rb | 64 ++++ spec/requests/ci/api/builds_spec.rb | 115 +++++++ spec/requests/ci/api/commits_spec.rb | 65 ++++ spec/requests/ci/api/forks_spec.rb | 60 ++++ spec/requests/ci/api/projects_spec.rb | 251 +++++++++++++++ spec/requests/ci/api/runners_spec.rb | 83 +++++ spec/requests/ci/api/triggers_spec.rb | 78 +++++ spec/requests/ci/builds_spec.rb | 18 ++ spec/requests/ci/commits_spec.rb | 17 + spec/services/ci/create_commit_service_spec.rb | 130 ++++++++ spec/services/ci/create_project_service_spec.rb | 40 +++ .../ci/create_trigger_request_service_spec.rb | 52 +++ spec/services/ci/event_service_spec.rb | 34 ++ spec/services/ci/image_for_build_service_spec.rb | 46 +++ spec/services/ci/register_build_service_spec.rb | 89 ++++++ spec/services/ci/web_hook_service_spec.rb | 36 +++ 108 files changed, 4074 insertions(+), 4074 deletions(-) delete mode 100644 spec/ci/features/admin/builds_spec.rb delete mode 100644 spec/ci/features/admin/events_spec.rb delete mode 100644 spec/ci/features/admin/projects_spec.rb delete mode 100644 spec/ci/features/admin/runners_spec.rb delete mode 100644 spec/ci/features/builds_spec.rb delete mode 100644 spec/ci/features/commits_spec.rb delete mode 100644 spec/ci/features/events_spec.rb delete mode 100644 spec/ci/features/lint_spec.rb delete mode 100644 spec/ci/features/projects_spec.rb delete mode 100644 spec/ci/features/runners_spec.rb delete mode 100644 spec/ci/features/triggers_spec.rb delete mode 100644 spec/ci/features/variables_spec.rb delete mode 100644 spec/ci/helpers/application_helper_spec.rb delete mode 100644 spec/ci/helpers/runners_helper_spec.rb delete mode 100644 spec/ci/helpers/user_helper_spec.rb delete mode 100644 spec/ci/helpers/user_sessions_helper_spec.rb delete mode 100644 spec/ci/lib/ansi2html_spec.rb delete mode 100644 spec/ci/lib/charts_spec.rb delete mode 100644 spec/ci/lib/gitlab_ci_yaml_processor_spec.rb delete mode 100644 spec/ci/lib/upgrader_spec.rb delete mode 100644 spec/ci/mailers/notify_spec.rb delete mode 100644 spec/ci/models/build_spec.rb delete mode 100644 spec/ci/models/commit_spec.rb delete mode 100644 spec/ci/models/mail_service_spec.rb delete mode 100644 spec/ci/models/network_spec.rb delete mode 100644 spec/ci/models/project_services/hip_chat_message_spec.rb delete mode 100644 spec/ci/models/project_services/hip_chat_service_spec.rb delete mode 100644 spec/ci/models/project_services/slack_message_spec.rb delete mode 100644 spec/ci/models/project_services/slack_service_spec.rb delete mode 100644 spec/ci/models/project_spec.rb delete mode 100644 spec/ci/models/runner_project_spec.rb delete mode 100644 spec/ci/models/runner_spec.rb delete mode 100644 spec/ci/models/service_spec.rb delete mode 100644 spec/ci/models/trigger_spec.rb delete mode 100644 spec/ci/models/user_spec.rb delete mode 100644 spec/ci/models/variable_spec.rb delete mode 100644 spec/ci/models/web_hook_spec.rb delete mode 100644 spec/ci/requests/api/builds_spec.rb delete mode 100644 spec/ci/requests/api/commits_spec.rb delete mode 100644 spec/ci/requests/api/forks_spec.rb delete mode 100644 spec/ci/requests/api/projects_spec.rb delete mode 100644 spec/ci/requests/api/runners_spec.rb delete mode 100644 spec/ci/requests/api/triggers_spec.rb delete mode 100644 spec/ci/requests/builds_spec.rb delete mode 100644 spec/ci/requests/commits_spec.rb delete mode 100644 spec/ci/services/create_commit_service_spec.rb delete mode 100644 spec/ci/services/create_project_service_spec.rb delete mode 100644 spec/ci/services/create_trigger_request_service_spec.rb delete mode 100644 spec/ci/services/event_service_spec.rb delete mode 100644 spec/ci/services/image_for_build_service_spec.rb delete mode 100644 spec/ci/services/register_build_service_spec.rb delete mode 100644 spec/ci/services/web_hook_service_spec.rb delete mode 100644 spec/ci/six.tar.gz create mode 100644 spec/features/ci/admin/builds_spec.rb create mode 100644 spec/features/ci/admin/events_spec.rb create mode 100644 spec/features/ci/admin/projects_spec.rb create mode 100644 spec/features/ci/admin/runners_spec.rb create mode 100644 spec/features/ci/builds_spec.rb create mode 100644 spec/features/ci/commits_spec.rb create mode 100644 spec/features/ci/events_spec.rb create mode 100644 spec/features/ci/lint_spec.rb create mode 100644 spec/features/ci/projects_spec.rb create mode 100644 spec/features/ci/runners_spec.rb create mode 100644 spec/features/ci/triggers_spec.rb create mode 100644 spec/features/ci/variables_spec.rb create mode 100644 spec/helpers/ci/application_helper_spec.rb create mode 100644 spec/helpers/ci/runners_helper_spec.rb create mode 100644 spec/helpers/ci/user_helper_spec.rb create mode 100644 spec/helpers/ci/user_sessions_helper_spec.rb create mode 100644 spec/lib/ci/ansi2html_spec.rb create mode 100644 spec/lib/ci/charts_spec.rb create mode 100644 spec/lib/ci/gitlab_ci_yaml_processor_spec.rb create mode 100644 spec/lib/ci/upgrader_spec.rb create mode 100644 spec/mailers/ci/notify_spec.rb create mode 100644 spec/models/ci/build_spec.rb create mode 100644 spec/models/ci/commit_spec.rb create mode 100644 spec/models/ci/mail_service_spec.rb create mode 100644 spec/models/ci/network_spec.rb create mode 100644 spec/models/ci/project_services/hip_chat_message_spec.rb create mode 100644 spec/models/ci/project_services/hip_chat_service_spec.rb create mode 100644 spec/models/ci/project_services/slack_message_spec.rb create mode 100644 spec/models/ci/project_services/slack_service_spec.rb create mode 100644 spec/models/ci/project_spec.rb create mode 100644 spec/models/ci/runner_project_spec.rb create mode 100644 spec/models/ci/runner_spec.rb create mode 100644 spec/models/ci/service_spec.rb create mode 100644 spec/models/ci/trigger_spec.rb create mode 100644 spec/models/ci/user_spec.rb create mode 100644 spec/models/ci/variable_spec.rb create mode 100644 spec/models/ci/web_hook_spec.rb create mode 100644 spec/requests/ci/api/builds_spec.rb create mode 100644 spec/requests/ci/api/commits_spec.rb create mode 100644 spec/requests/ci/api/forks_spec.rb create mode 100644 spec/requests/ci/api/projects_spec.rb create mode 100644 spec/requests/ci/api/runners_spec.rb create mode 100644 spec/requests/ci/api/triggers_spec.rb create mode 100644 spec/requests/ci/builds_spec.rb create mode 100644 spec/requests/ci/commits_spec.rb create mode 100644 spec/services/ci/create_commit_service_spec.rb create mode 100644 spec/services/ci/create_project_service_spec.rb create mode 100644 spec/services/ci/create_trigger_request_service_spec.rb create mode 100644 spec/services/ci/event_service_spec.rb create mode 100644 spec/services/ci/image_for_build_service_spec.rb create mode 100644 spec/services/ci/register_build_service_spec.rb create mode 100644 spec/services/ci/web_hook_service_spec.rb diff --git a/spec/ci/features/admin/builds_spec.rb b/spec/ci/features/admin/builds_spec.rb deleted file mode 100644 index e62e83692da..00000000000 --- a/spec/ci/features/admin/builds_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'spec_helper' - -describe "Admin Builds" do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit } - - before do - skip_admin_auth - login_as :user - end - - describe "GET /admin/builds" do - before do - build - visit admin_builds_path - end - - it { page.should have_content "All builds" } - it { page.should have_content build.short_sha } - end - - describe "Tabs" do - it "shows all builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" - - visit admin_builds_path - - page.all(".build-link").size.should == 4 - end - - it "shows pending builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" - - visit admin_builds_path - - within ".nav.nav-tabs" do - click_on "Pending" - end - - page.find(".build-link").should have_content(build.id) - page.find(".build-link").should_not have_content(build1.id) - page.find(".build-link").should_not have_content(build2.id) - page.find(".build-link").should_not have_content(build3.id) - end - - it "shows running builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" - - visit admin_builds_path - - within ".nav.nav-tabs" do - click_on "Running" - end - - page.find(".build-link").should have_content(build1.id) - page.find(".build-link").should_not have_content(build.id) - page.find(".build-link").should_not have_content(build2.id) - page.find(".build-link").should_not have_content(build3.id) - end - end -end diff --git a/spec/ci/features/admin/events_spec.rb b/spec/ci/features/admin/events_spec.rb deleted file mode 100644 index 469c6ed102d..00000000000 --- a/spec/ci/features/admin/events_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Admin Events" do - let(:event) { FactoryGirl.create :admin_event } - - before do - skip_admin_auth - login_as :user - end - - describe "GET /admin/events" do - before do - event - visit admin_events_path - end - - it { page.should have_content "Events" } - it { page.should have_content event.description } - end -end diff --git a/spec/ci/features/admin/projects_spec.rb b/spec/ci/features/admin/projects_spec.rb deleted file mode 100644 index 6f87e368deb..00000000000 --- a/spec/ci/features/admin/projects_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe "Admin Projects" do - let(:project) { FactoryGirl.create :project } - - before do - skip_admin_auth - login_as :user - end - - describe "GET /admin/projects" do - before do - project - visit admin_projects_path - end - - it { page.should have_content "Projects" } - end -end diff --git a/spec/ci/features/admin/runners_spec.rb b/spec/ci/features/admin/runners_spec.rb deleted file mode 100644 index 2827a7fc6e5..00000000000 --- a/spec/ci/features/admin/runners_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'spec_helper' - -describe "Admin Runners" do - before do - skip_admin_auth - login_as :user - end - - describe "Runners page" do - before do - runner = FactoryGirl.create(:runner) - commit = FactoryGirl.create(:commit) - FactoryGirl.create(:build, commit: commit, runner_id: runner.id) - visit admin_runners_path - end - - it { page.has_text? "Manage Runners" } - it { page.has_text? "To register a new runner" } - it { page.has_text? "Runners with last contact less than a minute ago: 1" } - - describe 'search' do - before do - FactoryGirl.create :runner, description: 'foo' - FactoryGirl.create :runner, description: 'bar' - - fill_in 'search', with: 'foo' - click_button 'Search' - end - - it { page.should have_content("foo") } - it { page.should_not have_content("bar") } - end - end - - describe "Runner show page" do - let(:runner) { FactoryGirl.create :runner } - - before do - FactoryGirl.create(:project, name: "foo") - FactoryGirl.create(:project, name: "bar") - visit admin_runner_path(runner) - end - - describe 'runner info' do - it { find_field('runner_token').value.should eq runner.token } - end - - describe 'projects' do - it { page.should have_content("foo") } - it { page.should have_content("bar") } - end - - describe 'search' do - before do - fill_in 'search', with: 'foo' - click_button 'Search' - end - - it { page.should have_content("foo") } - it { page.should_not have_content("bar") } - end - end -end diff --git a/spec/ci/features/builds_spec.rb b/spec/ci/features/builds_spec.rb deleted file mode 100644 index fcd7996efd7..00000000000 --- a/spec/ci/features/builds_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe "GET /:project/builds/:id" do - before do - login_as :user - visit project_build_path(@project, @build) - end - - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } - end - - describe "GET /:project/builds/:id/cancel" do - before do - login_as :user - @build.run! - visit cancel_project_build_path(@project, @build) - end - - it { page.should have_content 'canceled' } - it { page.should have_content 'Retry' } - end - - describe "POST /:project/builds/:id/retry" do - before do - login_as :user - @build.cancel! - visit project_build_path(@project, @build) - click_link 'Retry' - end - - it { page.should have_content 'pending' } - it { page.should have_content 'Cancel' } - end - - describe "Show page public accessible" do - before do - @project = FactoryGirl.create :public_project - @commit = FactoryGirl.create :commit, project: @project - @runner = FactoryGirl.create :specific_runner - @build = FactoryGirl.create :build, commit: @commit, runner: @runner - - stub_gitlab_calls - visit project_build_path(@project, @build) - end - - it { page.should have_content @commit.sha[0..7] } - end -end diff --git a/spec/ci/features/commits_spec.rb b/spec/ci/features/commits_spec.rb deleted file mode 100644 index 202f05c516f..00000000000 --- a/spec/ci/features/commits_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'spec_helper' - -describe "Commits" do - context "Authenticated user" do - before do - login_as :user - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe "GET /:project/commits/:sha" do - before do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) - end - - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } - end - - describe "Cancel commit" do - it "cancels commit" do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) - click_on "Cancel" - - page.should have_content "canceled" - end - end - - describe ".gitlab-ci.yml not found warning" do - it "does not show warning" do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) - - page.should_not have_content ".gitlab-ci.yml not found in this commit" - end - - it "shows warning" do - @commit.push_data[:ci_yaml_file] = nil - @commit.save - - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) - - page.should have_content ".gitlab-ci.yml not found in this commit" - end - end - end - - context "Public pages" do - before do - @project = FactoryGirl.create :public_project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe "GET /:project/commits/:sha" do - before do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) - end - - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } - end - end -end diff --git a/spec/ci/features/events_spec.rb b/spec/ci/features/events_spec.rb deleted file mode 100644 index 77d1fba5769..00000000000 --- a/spec/ci/features/events_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Events" do - let(:project) { FactoryGirl.create :project } - let(:event) { FactoryGirl.create :admin_event, project: project } - - before do - login_as :user - end - - describe "GET /project/:id/events" do - before do - event - visit project_events_path(project) - end - - it { page.should have_content "Events" } - it { page.should have_content event.description } - end -end diff --git a/spec/ci/features/lint_spec.rb b/spec/ci/features/lint_spec.rb deleted file mode 100644 index 0b3d4e099fb..00000000000 --- a/spec/ci/features/lint_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe "Lint" do - before do - login_as :user - end - - it "Yaml parsing", js: true do - content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - visit lint_path - fill_in "content", with: content - click_on "Validate" - within "table" do - page.should have_content("Job - rspec") - page.should have_content("Job - spinach") - page.should have_content("Deploy Job - staging") - page.should have_content("Deploy Job - production") - end - end - - it "Yaml parsing with error", js: true do - visit lint_path - fill_in "content", with: "" - click_on "Validate" - page.should have_content("Status: syntax is incorrect") - page.should have_content("Error: Please provide content of .gitlab-ci.yml") - end -end diff --git a/spec/ci/features/projects_spec.rb b/spec/ci/features/projects_spec.rb deleted file mode 100644 index 3f21af92a2b..00000000000 --- a/spec/ci/features/projects_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'spec_helper' - -describe "Projects" do - before do - login_as :user - @project = FactoryGirl.create :project, name: "GitLab / gitlab-shell" - end - - describe "GET /projects", js: true do - before do - stub_js_gitlab_calls - visit projects_path - end - - it { page.should have_content "GitLab / gitlab-shell" } - it { page.should have_selector ".search input#search" } - end - - describe "GET /projects/:id" do - before do - visit project_path(@project) - end - - it { page.should have_content @project.name } - it { page.should have_content 'All commits' } - end - - describe "GET /projects/:id/edit" do - before do - visit edit_project_path(@project) - end - - it { page.should have_content @project.name } - it { page.should have_content 'Build Schedule' } - - it "updates configuration" do - fill_in 'Timeout', with: '70' - click_button 'Save changes' - - page.should have_content 'was successfully updated' - - find_field('Timeout').value.should eq '70' - end - end - - describe "GET /projects/:id/charts" do - before do - visit project_charts_path(@project) - end - - it { page.should have_content 'Overall' } - it { page.should have_content 'Builds chart for last week' } - it { page.should have_content 'Builds chart for last month' } - it { page.should have_content 'Builds chart for last year' } - it { page.should have_content 'Commit duration in minutes for last 30 commits' } - end -end diff --git a/spec/ci/features/runners_spec.rb b/spec/ci/features/runners_spec.rb deleted file mode 100644 index c41dc5b2e2e..00000000000 --- a/spec/ci/features/runners_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'spec_helper' - -describe "Runners" do - before do - login_as :user - end - - describe "specific runners" do - before do - @project = FactoryGirl.create :project - @project2 = FactoryGirl.create :project - stub_js_gitlab_calls - - # all projects should be authorized for user - Network.any_instance.stub(:projects).and_return([ - OpenStruct.new({id: @project.gitlab_id}), - OpenStruct.new({id: @project2.gitlab_id}) - ]) - - @shared_runner = FactoryGirl.create :shared_runner - @specific_runner = FactoryGirl.create :specific_runner - @specific_runner2 = FactoryGirl.create :specific_runner - @project.runners << @specific_runner - @project2.runners << @specific_runner2 - end - - it "places runners in right places" do - visit project_runners_path(@project) - page.find(".available-specific-runners").should have_content(@specific_runner2.display_name) - page.find(".activated-specific-runners").should have_content(@specific_runner.display_name) - page.find(".available-shared-runners").should have_content(@shared_runner.display_name) - end - - it "enables specific runner for project" do - visit project_runners_path(@project) - - within ".available-specific-runners" do - click_on "Enable for this project" - end - - page.find(".activated-specific-runners").should have_content(@specific_runner2.display_name) - end - - it "disables specific runner for project" do - @project2.runners << @specific_runner - - visit project_runners_path(@project) - - within ".activated-specific-runners" do - click_on "Disable for this project" - end - - page.find(".available-specific-runners").should have_content(@specific_runner.display_name) - end - - it "removes specific runner for project if this is last project for that runners" do - visit project_runners_path(@project) - - within ".activated-specific-runners" do - click_on "Remove runner" - end - - Runner.exists?(id: @specific_runner).should be_false - end - end - - describe "shared runners" do - before do - @project = FactoryGirl.create :project - stub_js_gitlab_calls - end - - it "enables shared runners" do - visit project_runners_path(@project) - - click_on "Enable shared runners" - - @project.reload.shared_runners_enabled.should be_true - end - end - - describe "show page" do - before do - @project = FactoryGirl.create :project - stub_js_gitlab_calls - @specific_runner = FactoryGirl.create :specific_runner - @project.runners << @specific_runner - end - - it "shows runner information" do - visit project_runners_path(@project) - - click_on @specific_runner.short_sha - - page.should have_content(@specific_runner.platform) - end - end -end diff --git a/spec/ci/features/triggers_spec.rb b/spec/ci/features/triggers_spec.rb deleted file mode 100644 index 2076429383d..00000000000 --- a/spec/ci/features/triggers_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'spec_helper' - -describe 'Variables' do - before do - login_as :user - @project = FactoryGirl.create :project - stub_js_gitlab_calls - visit project_triggers_path(@project) - end - - context 'create a trigger' do - before do - click_on 'Add Trigger' - @project.triggers.count.should == 1 - end - - it 'contains trigger token' do - page.should have_content(@project.triggers.first.token) - end - - it 'revokes the trigger' do - click_on 'Revoke' - @project.triggers.count.should == 0 - end - end -end diff --git a/spec/ci/features/variables_spec.rb b/spec/ci/features/variables_spec.rb deleted file mode 100644 index 2bb0d9dedde..00000000000 --- a/spec/ci/features/variables_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'spec_helper' - -describe "Variables" do - before do - login_as :user - end - - describe "specific runners" do - before do - @project = FactoryGirl.create :project - stub_js_gitlab_calls - end - - it "creates variable", js: true do - visit project_variables_path(@project) - click_on "Add a variable" - fill_in "Key", with: "SECRET_KEY" - fill_in "Value", with: "SECRET_VALUE" - click_on "Save changes" - - page.should have_content("Variables were successfully updated.") - @project.variables.count.should == 1 - end - - end -end diff --git a/spec/ci/helpers/application_helper_spec.rb b/spec/ci/helpers/application_helper_spec.rb deleted file mode 100644 index c2b1058a8fa..00000000000 --- a/spec/ci/helpers/application_helper_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'spec_helper' - -describe ApplicationHelper do - describe "#duration_in_words" do - it "returns minutes and seconds" do - intervals_in_words = { - 100 => "1 minute 40 seconds", - 121 => "2 minutes 1 second", - 3721 => "62 minutes 1 second", - 0 => "0 seconds" - } - - intervals_in_words.each do |interval, expectation| - duration_in_words(Time.now + interval, Time.now).should == expectation - end - end - - it "calculates interval from now if there is no finished_at" do - duration_in_words(nil, Time.now - 5).should == "5 seconds" - end - end - - describe "#time_interval_in_words" do - it "returns minutes and seconds" do - intervals_in_words = { - 100 => "1 minute 40 seconds", - 121 => "2 minutes 1 second", - 3721 => "62 minutes 1 second", - 0 => "0 seconds" - } - - intervals_in_words.each do |interval, expectation| - time_interval_in_words(interval).should == expectation - end - end - end -end diff --git a/spec/ci/helpers/runners_helper_spec.rb b/spec/ci/helpers/runners_helper_spec.rb deleted file mode 100644 index 02d497b40d2..00000000000 --- a/spec/ci/helpers/runners_helper_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'spec_helper' - -describe RunnersHelper do - it "returns - not contacted yet" do - runner = FactoryGirl.build :runner - runner_status_icon(runner).should include("not connected yet") - end - - it "returns offline text" do - runner = FactoryGirl.build(:runner, contacted_at: 1.day.ago, active: true) - runner_status_icon(runner).should include("Runner is offline") - end - - it "returns online text" do - runner = FactoryGirl.build(:runner, contacted_at: 1.hour.ago, active: true) - runner_status_icon(runner).should include("Runner is online") - end -end diff --git a/spec/ci/helpers/user_helper_spec.rb b/spec/ci/helpers/user_helper_spec.rb deleted file mode 100644 index 7215dc41a85..00000000000 --- a/spec/ci/helpers/user_helper_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe UserHelper do - describe :user_avatar_url do - let (:user) { User.new({'avatar_url' => avatar_url}) } - - context 'no avatar' do - let (:avatar_url) { nil } - - it 'should return a generic avatar' do - user_avatar_url(user).should == 'ci/no_avatar.png' - end - end - - context 'plain gravatar' do - let (:base_url) { 'http://www.gravatar.com/avatar/abcdefgh' } - let (:avatar_url) { "#{base_url}?s=40&d=mm" } - - it 'should return gravatar with default size' do - user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" - end - - it 'should return gravatar with custom size' do - user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" - end - end - - context 'secure gravatar' do - let (:base_url) { 'https://secure.gravatar.com/avatar/abcdefgh' } - let (:avatar_url) { "#{base_url}?s=40&d=mm" } - - it 'should return gravatar with default size' do - user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" - end - - it 'should return gravatar with custom size' do - user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" - end - end - - context 'custom avatar' do - let (:avatar_url) { 'http://example.local/avatar.png' } - - it 'should return custom avatar' do - user_avatar_url(user).should == avatar_url - end - end - end -end diff --git a/spec/ci/helpers/user_sessions_helper_spec.rb b/spec/ci/helpers/user_sessions_helper_spec.rb deleted file mode 100644 index a2ab1f1e023..00000000000 --- a/spec/ci/helpers/user_sessions_helper_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'spec_helper' - -describe UserSessionsHelper do - describe :generate_oauth_hmac do - let (:salt) { 'a' } - let (:salt2) { 'b' } - let (:return_to) { 'b' } - - it 'should return null if return_to is also null' do - generate_oauth_hmac(salt, nil).should be_nil - end - - it 'should return not null if return_to is also not null' do - generate_oauth_hmac(salt, return_to).should_not be_nil - end - - it 'should return different hmacs for different salts' do - secret1 = generate_oauth_hmac(salt, return_to) - secret2 = generate_oauth_hmac(salt2, return_to) - secret1.should_not eq(secret2) - end - end - - describe :generate_oauth_state do - let (:return_to) { 'b' } - - it 'should return null if return_to is also null' do - generate_oauth_state(nil).should be_nil - end - - it 'should return two different states for same return_to' do - state1 = generate_oauth_state(return_to) - state2 = generate_oauth_state(return_to) - state1.should_not eq(state2) - end - end - - describe :get_ouath_state_return_to do - let (:return_to) { 'a' } - let (:state) { generate_oauth_state(return_to) } - - it 'should return return_to' do - get_ouath_state_return_to(state).should eq(return_to) - end - end - - describe :is_oauth_state_valid? do - let (:return_to) { 'a' } - let (:state) { generate_oauth_state(return_to) } - let (:forged) { "forged#{state}" } - let (:invalid) { 'aa' } - let (:invalid2) { 'aa:bb' } - let (:invalid3) { 'aa:bb:' } - - it 'should validate oauth state' do - is_oauth_state_valid?(state).should be_true - end - - it 'should not validate forged state' do - is_oauth_state_valid?(forged).should be_false - end - - it 'should not validate invalid state' do - is_oauth_state_valid?(invalid).should be_false - is_oauth_state_valid?(invalid2).should be_false - is_oauth_state_valid?(invalid3).should be_false - end - end -end diff --git a/spec/ci/lib/ansi2html_spec.rb b/spec/ci/lib/ansi2html_spec.rb deleted file mode 100644 index aa60011685b..00000000000 --- a/spec/ci/lib/ansi2html_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'spec_helper' - -describe Ansi2html do - - it "prints non-ansi as-is" do - Ansi2html::convert("Hello").should == 'Hello' - end - - it "strips non-color-changing controll sequences" do - Ansi2html::convert("Hello \e[2Kworld").should == 'Hello world' - end - - it "prints simply red" do - Ansi2html::convert("\e[31mHello\e[0m").should == 'Hello' - end - - it "prints simply red without trailing reset" do - Ansi2html::convert("\e[31mHello").should == 'Hello' - end - - it "prints simply yellow" do - Ansi2html::convert("\e[33mHello\e[0m").should == 'Hello' - end - - it "prints default on blue" do - Ansi2html::convert("\e[39;44mHello").should == 'Hello' - end - - it "prints red on blue" do - Ansi2html::convert("\e[31;44mHello").should == 'Hello' - end - - it "resets colors after red on blue" do - Ansi2html::convert("\e[31;44mHello\e[0m world").should == 'Hello world' - end - - it "performs color change from red/blue to yellow/blue" do - Ansi2html::convert("\e[31;44mHello \e[33mworld").should == 'Hello world' - end - - it "performs color change from red/blue to yellow/green" do - Ansi2html::convert("\e[31;44mHello \e[33;42mworld").should == 'Hello world' - end - - it "performs color change from red/blue to reset to yellow/green" do - Ansi2html::convert("\e[31;44mHello\e[0m \e[33;42mworld").should == 'Hello world' - end - - it "ignores unsupported codes" do - Ansi2html::convert("\e[51mHello\e[0m").should == 'Hello' - end - - it "prints light red" do - Ansi2html::convert("\e[91mHello\e[0m").should == 'Hello' - end - - it "prints default on light red" do - Ansi2html::convert("\e[101mHello\e[0m").should == 'Hello' - end - - it "performs color change from red/blue to default/blue" do - Ansi2html::convert("\e[31;44mHello \e[39mworld").should == 'Hello world' - end - - it "performs color change from light red/blue to default/blue" do - Ansi2html::convert("\e[91;44mHello \e[39mworld").should == 'Hello world' - end - - it "prints bold text" do - Ansi2html::convert("\e[1mHello").should == 'Hello' - end - - it "resets bold text" do - Ansi2html::convert("\e[1mHello\e[21m world").should == 'Hello world' - Ansi2html::convert("\e[1mHello\e[22m world").should == 'Hello world' - end - - it "prints italic text" do - Ansi2html::convert("\e[3mHello").should == 'Hello' - end - - it "resets italic text" do - Ansi2html::convert("\e[3mHello\e[23m world").should == 'Hello world' - end - - it "prints underlined text" do - Ansi2html::convert("\e[4mHello").should == 'Hello' - end - - it "resets underlined text" do - Ansi2html::convert("\e[4mHello\e[24m world").should == 'Hello world' - end - - it "prints concealed text" do - Ansi2html::convert("\e[8mHello").should == 'Hello' - end - - it "resets concealed text" do - Ansi2html::convert("\e[8mHello\e[28m world").should == 'Hello world' - end - - it "prints crossed-out text" do - Ansi2html::convert("\e[9mHello").should == 'Hello' - end - - it "resets crossed-out text" do - Ansi2html::convert("\e[9mHello\e[29m world").should == 'Hello world' - end - - it "can print 256 xterm fg colors" do - Ansi2html::convert("\e[38;5;16mHello").should == 'Hello' - end - - it "can print 256 xterm fg colors on normal magenta background" do - Ansi2html::convert("\e[38;5;16;45mHello").should == 'Hello' - end - - it "can print 256 xterm bg colors" do - Ansi2html::convert("\e[48;5;240mHello").should == 'Hello' - end - - it "can print 256 xterm bg colors on normal magenta foreground" do - Ansi2html::convert("\e[48;5;16;35mHello").should == 'Hello' - end - - it "prints bold colored text vividly" do - Ansi2html::convert("\e[1;31mHello\e[0m").should == 'Hello' - end - - it "prints bold light colored text correctly" do - Ansi2html::convert("\e[1;91mHello\e[0m").should == 'Hello' - end -end diff --git a/spec/ci/lib/charts_spec.rb b/spec/ci/lib/charts_spec.rb deleted file mode 100644 index 236cfc2a1f6..00000000000 --- a/spec/ci/lib/charts_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe "Charts" do - - context "build_times" do - before do - @project = FactoryGirl.create(:project) - @commit = FactoryGirl.create(:commit, project: @project) - FactoryGirl.create(:build, commit: @commit) - end - - it 'should return build times in minutes' do - chart = Charts::BuildTime.new(@project) - chart.build_times.should == [2] - end - end -end diff --git a/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb b/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb deleted file mode 100644 index ed3d4e84054..00000000000 --- a/spec/ci/lib/gitlab_ci_yaml_processor_spec.rb +++ /dev/null @@ -1,311 +0,0 @@ -require 'spec_helper' - -describe GitlabCiYamlProcessor do - - describe "#builds_for_ref" do - let (:type) { 'test' } - - it "returns builds if no branch specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec"} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 - config_processor.builds_for_stage_and_ref(type, "master").first.should == { - stage: "test", - except: nil, - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: {}, - allow_failure: false - } - end - - it "does not return builds if only has another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec", only: ["deploy"]} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 - end - - it "does not return builds if only has regexp with another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec", only: ["/^deploy$/"]} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 - end - - it "returns builds if only has specified this branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec", only: ["master"]} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 - end - - it "does not build tags" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec", except: ["tags"]} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "0-1", true).size.should == 0 - end - - it "returns builds if only has a list of branches including specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: {script: "rspec", type: type, only: ["master", "deploy"]} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 - end - - it "returns build only for specified type" do - - config = YAML.dump({ - before_script: ["pwd"], - build: {script: "build", type: "build", only: ["master", "deploy"]}, - rspec: {script: "rspec", type: type, only: ["master", "deploy"]}, - staging: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, - production: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("production", "deploy").size.should == 0 - config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 - config_processor.builds_for_stage_and_ref("deploy", "deploy").size.should == 2 - end - end - - describe "Image and service handling" do - it "returns image and service when defined" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: {script: "rspec"} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 - config_processor.builds_for_stage_and_ref("test", "master").first.should == { - except: nil, - stage: "test", - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: { - image: "ruby:2.1", - services: ["mysql"] - }, - allow_failure: false - } - end - - it "returns image and service when overridden for job" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: {image: "ruby:2.5", services: ["postgresql"], script: "rspec"} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 - config_processor.builds_for_stage_and_ref("test", "master").first.should == { - except: nil, - stage: "test", - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: { - image: "ruby:2.5", - services: ["postgresql"] - }, - allow_failure: false - } - end - end - - describe "Variables" do - it "returns variables when defined" do - variables = { - var1: "value1", - var2: "value2", - } - config = YAML.dump({ - variables: variables, - before_script: ["pwd"], - rspec: {script: "rspec"} - }) - - config_processor = GitlabCiYamlProcessor.new(config) - config_processor.variables.should == variables - end - end - - describe "Error handling" do - it "indicates that object is invalid" do - expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) - end - - it "returns errors if tags parameter is invalid" do - config = YAML.dump({rspec: {script: "test", tags: "mysql"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") - end - - it "returns errors if before_script parameter is invalid" do - config = YAML.dump({before_script: "bundle update", rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") - end - - it "returns errors if image parameter is invalid" do - config = YAML.dump({image: ["test"], rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") - end - - it "returns errors if job image parameter is invalid" do - config = YAML.dump({rspec: {script: "test", image: ["test"]}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") - end - - it "returns errors if services parameter is not an array" do - config = YAML.dump({services: "test", rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") - end - - it "returns errors if services parameter is not an array of strings" do - config = YAML.dump({services: [10, "test"], rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") - end - - it "returns errors if job services parameter is not an array" do - config = YAML.dump({rspec: {script: "test", services: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") - end - - it "returns errors if job services parameter is not an array of strings" do - config = YAML.dump({rspec: {script: "test", services: [10, "test"]}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") - end - - it "returns errors if there are unknown parameters" do - config = YAML.dump({extra: "bundle update"}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") - end - - it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do - config = YAML.dump({extra: {services: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") - end - - it "returns errors if there is no any jobs defined" do - config = YAML.dump({before_script: ["bundle update"]}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") - end - - it "returns errors if job allow_failure parameter is not an boolean" do - config = YAML.dump({rspec: {script: "test", allow_failure: "string"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") - end - - it "returns errors if job stage is not a string" do - config = YAML.dump({rspec: {script: "test", type: 1, allow_failure: "string"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") - end - - it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") - end - - it "returns errors if job stage is not a defined stage" do - config = YAML.dump({types: ["build", "test"], rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") - end - - it "returns errors if stages is not an array" do - config = YAML.dump({types: "test", rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") - end - - it "returns errors if stages is not an array of strings" do - config = YAML.dump({types: [true, "test"], rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") - end - - it "returns errors if variables is not a map" do - config = YAML.dump({variables: "test", rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") - end - - it "returns errors if variables is not a map of key-valued strings" do - config = YAML.dump({variables: {test: false}, rspec: {script: "test"}}) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") - end - end -end diff --git a/spec/ci/lib/upgrader_spec.rb b/spec/ci/lib/upgrader_spec.rb deleted file mode 100644 index 40a98307ad2..00000000000 --- a/spec/ci/lib/upgrader_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe Upgrader do - let(:upgrader) { Upgrader.new } - let(:current_version) { GitlabCi::VERSION } - - describe 'current_version_raw' do - it { upgrader.current_version_raw.should == current_version } - end - - describe 'latest_version?' do - it 'should be true if newest version' do - upgrader.stub(latest_version_raw: current_version) - upgrader.latest_version?.should be_true - end - end - - describe 'latest_version_raw' do - it 'should be latest version for GitlabCI 3' do - allow(upgrader).to receive(:current_version_raw).and_return('3.0.0') - expect(upgrader.latest_version_raw).to eq('v3.2.0') - end - - it 'should get the latest version from tags' do - allow(upgrader).to receive(:fetch_git_tags).and_return([ - '1b5bee25b51724214c7a3307ef94027ab93ec982 refs/tags/v7.8.1', - '424cb42e35947fa304ef83eb211ffc657e31aef3 refs/tags/v7.8.1^{}', - '498e5ba63be1bb99e30c6e720902d864aac4413c refs/tags/v7.9.0.rc1', - '96aaf45ae93bd43e8b3f5d4d353d64d3cbe1e63b refs/tags/v7.9.0.rc1^{}', - '94aaf45ae93bd43e8b3fad4a353d64d3cbe1e62b refs/tags/v7.1.0', - '96aaf45ae93ba13e8b3f5d4d353d64d3cbe1e251 refs/tags/v7.1.0^{}', - '29359d64442bf54b4ca1d8b439fd9e5f9cd83252 refs/tags/v7.10.0', - '4d9213a6378bff43a69ae099702fb81e29335e7a refs/tags/v7.10.0^{}', - '1d93e1626bda93622ca7a2ae2825e2e94dabf3c6 refs/tags/v7.12.0', - '0188a9d1c2efdc52bfad36ad303686be997de713 refs/tags/v7.12.0^{}']) - expect(upgrader.latest_version_raw).to eq("v7.12.0") - end - end -end diff --git a/spec/ci/mailers/notify_spec.rb b/spec/ci/mailers/notify_spec.rb deleted file mode 100644 index 6a2c845cd0e..00000000000 --- a/spec/ci/mailers/notify_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'spec_helper' - -describe Notify do - include EmailSpec::Helpers - include EmailSpec::Matchers - - before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe 'build success' do - subject { Notify.build_success_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build success for/ - end - - it 'contains name of project' do - should have_body_text /build successful/ - end - end - - describe 'build fail' do - subject { Notify.build_fail_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build failed for/ - end - - it 'contains name of project' do - should have_body_text /build failed/ - end - end -end diff --git a/spec/ci/models/build_spec.rb b/spec/ci/models/build_spec.rb deleted file mode 100644 index 733398176bf..00000000000 --- a/spec/ci/models/build_spec.rb +++ /dev/null @@ -1,350 +0,0 @@ -# == Schema Information -# -# Table name: builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# commit_id :integer -# coverage :float -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# - -require 'spec_helper' - -describe Build do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit } - - it { should belong_to(:commit) } - it { should validate_presence_of :status } - - it { should respond_to :success? } - it { should respond_to :failed? } - it { should respond_to :running? } - it { should respond_to :pending? } - it { should respond_to :trace_html } - - describe :first_pending do - let(:first) { FactoryGirl.create :build, commit: commit, status: 'pending', created_at: Date.yesterday } - let(:second) { FactoryGirl.create :build, commit: commit, status: 'pending' } - before { first; second } - subject { Build.first_pending } - - it { should be_a(Build) } - it('returns with the first pending build') { should eq(first) } - end - - describe :create_from do - before do - build.status = 'success' - build.save - end - let(:create_from_build) { Build.create_from build } - - it ('there should be a pending task') do - expect(Build.pending.count(:all)).to eq 0 - create_from_build - expect(Build.pending.count(:all)).to be > 0 - end - end - - describe :started? do - subject { build.started? } - - context 'without started_at' do - before { build.started_at = nil } - - it { should be_false } - end - - %w(running success failed).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { should be_true } - end - end - - %w(pending canceled).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { should be_false } - end - end - end - - describe :active? do - subject { build.active? } - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { should be_true } - end - end - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { should be_false } - end - end - end - - describe :complete? do - subject { build.complete? } - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { should be_true } - end - end - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { should be_false } - end - end - end - - describe :ignored? do - subject { build.ignored? } - - context 'if build is not allowed to fail' do - before { build.allow_failure = false } - - context 'and build.status is success' do - before { build.status = 'success' } - - it { should be_false } - end - - context 'and build.status is failed' do - before { build.status = 'failed' } - - it { should be_false } - end - end - - context 'if build is allowed to fail' do - before { build.allow_failure = true } - - context 'and build.status is success' do - before { build.status = 'success' } - - it { should be_false } - end - - context 'and build.status is failed' do - before { build.status = 'failed' } - - it { should be_true } - end - end - end - - describe :trace do - subject { build.trace_html } - - it { should be_empty } - - context 'if build.trace contains text' do - let(:text) { 'example output' } - before { build.trace = text } - - it { should include(text) } - it { should have_at_least(text.length).items } - end - end - - describe :timeout do - subject { build.timeout } - - it { should eq(commit.project.timeout) } - end - - describe :duration do - subject { build.duration } - - it { should eq(120.0) } - - context 'if the building process has not started yet' do - before do - build.started_at = nil - build.finished_at = nil - end - - it { should be_nil } - end - - context 'if the building process has started' do - before do - build.started_at = Time.now - 1.minute - build.finished_at = nil - end - - it { should be_a(Float) } - it { should > 0.0 } - end - end - - describe :options do - let(:options) { - { - :image => "ruby:2.1", - :services => [ - "postgres" - ] - } - } - - subject { build.options } - it { should eq(options) } - end - - describe :ref do - subject { build.ref } - - it { should eq(commit.ref) } - end - - describe :sha do - subject { build.sha } - - it { should eq(commit.sha) } - end - - describe :short_sha do - subject { build.short_sha } - - it { should eq(commit.short_sha) } - end - - describe :before_sha do - subject { build.before_sha } - - it { should eq(commit.before_sha) } - end - - describe :allow_git_fetch do - subject { build.allow_git_fetch } - - it { should eq(project.allow_git_fetch) } - end - - describe :project do - subject { build.project } - - it { should eq(commit.project) } - end - - describe :project_id do - subject { build.project_id } - - it { should eq(commit.project_id) } - end - - describe :project_name do - subject { build.project_name } - - it { should eq(project.name) } - end - - describe :repo_url do - subject { build.repo_url } - - it { should eq(project.repo_url_with_auth) } - end - - describe :extract_coverage do - context 'valid content & regex' do - subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } - - it { should eq(98.29) } - end - - context 'valid content & bad regex' do - subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } - - it { should be_nil } - end - - context 'no coverage content & regex' do - subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } - - it { should be_nil } - end - - context 'multiple results in content & regex' do - subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } - - it { should eq(98.29) } - end - end - - describe :variables do - context 'returns variables' do - subject { build.variables } - - let(:variables) { - [ - {key: :DB_NAME, value: 'postgres', public: true} - ] - } - - it { should eq(variables) } - - context 'and secure variables' do - let(:secure_variables) { - [ - {key: 'SECRET_KEY', value: 'secret_value', public: false} - ] - } - - before do - build.project.variables << Variable.new(key: 'SECRET_KEY', value: 'secret_value') - end - - it { should eq(variables + secure_variables) } - - context 'and trigger variables' do - let(:trigger) { FactoryGirl.create :trigger, project: project } - let(:trigger_request) { FactoryGirl.create :trigger_request_with_variables, commit: commit, trigger: trigger } - let(:trigger_variables) { - [ - {key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false} - ] - } - - before do - build.trigger_request = trigger_request - end - - it { should eq(variables + secure_variables + trigger_variables) } - end - end - end - end -end diff --git a/spec/ci/models/commit_spec.rb b/spec/ci/models/commit_spec.rb deleted file mode 100644 index 6f644d20aaf..00000000000 --- a/spec/ci/models/commit_spec.rb +++ /dev/null @@ -1,264 +0,0 @@ -# == Schema Information -# -# Table name: commits -# -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime -# - -require 'spec_helper' - -describe Commit do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:commit_with_project) { FactoryGirl.create :commit, project: project } - let(:config_processor) { GitlabCiYamlProcessor.new(gitlab_ci_yaml) } - - it { should belong_to(:project) } - it { should have_many(:builds) } - it { should validate_presence_of :before_sha } - it { should validate_presence_of :sha } - it { should validate_presence_of :ref } - it { should validate_presence_of :push_data } - - it { should respond_to :git_author_name } - it { should respond_to :git_author_email } - it { should respond_to :short_sha } - - describe :last_build do - subject { commit.last_build } - before do - @first = FactoryGirl.create :build, commit: commit, created_at: Date.yesterday - @second = FactoryGirl.create :build, commit: commit - end - - it { should be_a(Build) } - it('returns with the most recently created build') { should eq(@second) } - end - - describe :retry do - before do - @first = FactoryGirl.create :build, commit: commit, created_at: Date.yesterday - @second = FactoryGirl.create :build, commit: commit - end - - it "creates new build" do - expect(commit.builds.count(:all)).to eq 2 - commit.retry - expect(commit.builds.count(:all)).to eq 3 - end - end - - describe :project_recipients do - - context 'always sending notification' do - it 'should return commit_pusher_email as only recipient when no additional recipients are given' do - project = FactoryGirl.create :project, - email_add_pusher: true, - email_recipients: '' - commit = FactoryGirl.create :commit, project: project - expected = 'commit_pusher_email' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == [expected] - end - - it 'should return commit_pusher_email and additional recipients' do - project = FactoryGirl.create :project, - email_add_pusher: true, - email_recipients: 'rec1 rec2' - commit = FactoryGirl.create :commit, project: project - expected = 'commit_pusher_email' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == ['rec1', 'rec2', expected] - end - - it 'should return recipients' do - project = FactoryGirl.create :project, - email_add_pusher: false, - email_recipients: 'rec1 rec2' - commit = FactoryGirl.create :commit, project: project - commit.project_recipients.should == ['rec1', 'rec2'] - end - - it 'should return unique recipients only' do - project = FactoryGirl.create :project, - email_add_pusher: true, - email_recipients: 'rec1 rec1 rec2' - commit = FactoryGirl.create :commit, project: project - expected = 'rec2' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == ['rec1', 'rec2'] - end - end - end - - describe :valid_commit_sha do - context 'commit.sha can not start with 00000000' do - before do - commit.sha = '0' * 40 - commit.valid_commit_sha - end - - it('commit errors should not be empty') { commit.errors.should_not be_empty } - end - end - - describe :compare? do - subject { commit_with_project.compare? } - - context 'if commit.before_sha are not nil' do - it { should be_true } - end - end - - describe :short_sha do - subject { commit.short_before_sha } - - it { should have(8).items } - it { commit.before_sha.should start_with(subject) } - end - - describe :short_sha do - subject { commit.short_sha } - - it { should have(8).items } - it { commit.sha.should start_with(subject) } - end - - describe :create_next_builds do - before do - commit.stub(:config_processor).and_return(config_processor) - end - - it "creates builds for next type" do - commit.create_builds.should be_true - commit.builds.reload - commit.builds.size.should == 2 - - commit.create_next_builds(nil).should be_true - commit.builds.reload - commit.builds.size.should == 4 - - commit.create_next_builds(nil).should be_true - commit.builds.reload - commit.builds.size.should == 5 - - commit.create_next_builds(nil).should be_false - end - end - - describe :create_builds do - before do - commit.stub(:config_processor).and_return(config_processor) - end - - it 'creates builds' do - commit.create_builds.should be_true - commit.builds.reload - commit.builds.size.should == 2 - end - - context 'for build triggers' do - let(:trigger) { FactoryGirl.create :trigger, project: project } - let(:trigger_request) { FactoryGirl.create :trigger_request, commit: commit, trigger: trigger } - - it 'creates builds' do - commit.create_builds(trigger_request).should be_true - commit.builds.reload - commit.builds.size.should == 2 - end - - it 'rebuilds commit' do - commit.create_builds.should be_true - commit.builds.reload - commit.builds.size.should == 2 - - commit.create_builds(trigger_request).should be_true - commit.builds.reload - commit.builds.size.should == 4 - end - - it 'creates next builds' do - commit.create_builds(trigger_request).should be_true - commit.builds.reload - commit.builds.size.should == 2 - - commit.create_next_builds(trigger_request).should be_true - commit.builds.reload - commit.builds.size.should == 4 - end - - context 'for [ci skip]' do - before do - commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]' - commit.save - end - - it 'rebuilds commit' do - commit.status.should == 'skipped' - commit.create_builds(trigger_request).should be_true - commit.builds.reload - commit.builds.size.should == 2 - commit.status.should == 'pending' - end - end - end - end - - describe "#finished_at" do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - - it "returns finished_at of latest build" do - build = FactoryGirl.create :build, commit: commit, finished_at: Time.now - 60 - build1 = FactoryGirl.create :build, commit: commit, finished_at: Time.now - 120 - - commit.finished_at.to_i.should == build.finished_at.to_i - end - - it "returns nil if there is no finished build" do - build = FactoryGirl.create :not_started_build, commit: commit - - commit.finished_at.should be_nil - end - end - - describe "coverage" do - let(:project) { FactoryGirl.create :project, coverage_regex: "/.*/" } - let(:commit) { FactoryGirl.create :commit, project: project } - - it "calculates average when there are two builds with coverage" do - FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit - FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit - commit.coverage.should == "35.00" - end - - it "calculates average when there are two builds with coverage and one with nil" do - FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit - FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit - FactoryGirl.create :build, commit: commit - commit.coverage.should == "35.00" - end - - it "calculates average when there are two builds with coverage and one is retried" do - FactoryGirl.create :build, name: "rspec", coverage: 30, commit: commit - FactoryGirl.create :build, name: "rubocop", coverage: 30, commit: commit - FactoryGirl.create :build, name: "rubocop", coverage: 40, commit: commit - commit.coverage.should == "35.00" - end - - it "calculates average when there is one build without coverage" do - FactoryGirl.create :build, commit: commit - commit.coverage.should be_nil - end - end -end diff --git a/spec/ci/models/mail_service_spec.rb b/spec/ci/models/mail_service_spec.rb deleted file mode 100644 index d66a6591f8f..00000000000 --- a/spec/ci/models/mail_service_spec.rb +++ /dev/null @@ -1,184 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe MailService do - describe "Associations" do - it { should belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - end - end - - describe 'Sends email for' do - let(:mail) { MailService.new } - - describe 'failed build' do - let(:project) { FactoryGirl.create(:project, email_add_pusher: true) } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :failed, commit: commit) } - - before do - mail.stub( - project: project - ) - end - - it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - Notify.should_receive(:build_fail_email).with(build.id, email) - Notify.should_not_receive(:build_success_email).with(build.id, email) - end - end - - describe 'successfull build' do - let(:project) { FactoryGirl.create(:project, email_add_pusher: true, email_only_broken_builds: false) } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } - - before do - mail.stub( - project: project - ) - end - - it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - Notify.should_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) - end - end - - describe 'successfull build and project has email_recipients' do - let(:project) { - FactoryGirl.create(:project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } - - before do - mail.stub( - project: project - ) - end - - it do - should_email("git@example.com") - should_email("jeroen@example.com") - mail.execute(build) - end - - def should_email(email) - Notify.should_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) - end - end - - describe 'successful build and notify only broken builds' do - let(:project) { - FactoryGirl.create(:project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } - - before do - mail.stub( - project: project - ) - end - - it do - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - Notify.should_not_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) - end - end - - describe 'successful build and can test service' do - let(:project) { - FactoryGirl.create(:project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :success, commit: commit) } - - before do - mail.stub( - project: project - ) - build - end - - it do - mail.can_test?.should == true - end - end - - describe 'retried build should not receive email' do - let(:project) { - FactoryGirl.create(:project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - } - let(:commit) { FactoryGirl.create(:commit, project: project) } - let(:build) { FactoryGirl.create(:build, status: :failed, commit: commit) } - - before do - mail.stub( - project: project - ) - end - - it do - Build.retry(build) - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - Notify.should_not_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) - end - end - end -end diff --git a/spec/ci/models/network_spec.rb b/spec/ci/models/network_spec.rb deleted file mode 100644 index b80adba5b08..00000000000 --- a/spec/ci/models/network_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'spec_helper' - -describe Network do - let(:network) { Network.new } - - describe :enable_ci do - subject { network.enable_ci '', '', '' } - - context 'on success' do - before do - response = double - response.stub(:code) { 200 } - network.class.stub(:put) { response } - end - - it { should be_true } - end - - context 'on failure' do - before do - response = double - response.stub(:code) { 404 } - network.class.stub(:put) { response } - end - - it { should be_nil } - end - end - - describe :disable_ci do - let(:response) { double } - subject { network.disable_ci '', '' } - - context 'on success' do - let(:parsed_response) { 'parsed' } - before do - response.stub(:code) { 200 } - response.stub(:parsed_response) { parsed_response } - network.class.stub(:delete) { response } - end - - it { should equal(parsed_response) } - end - - context 'on failure' do - before do - response.stub(:code) { 404 } - network.class.stub(:delete) { response } - end - - it { should be_nil } - end - end -end diff --git a/spec/ci/models/project_services/hip_chat_message_spec.rb b/spec/ci/models/project_services/hip_chat_message_spec.rb deleted file mode 100644 index f1ad875ebcf..00000000000 --- a/spec/ci/models/project_services/hip_chat_message_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'spec_helper' - -describe HipChatMessage do - subject { HipChatMessage.new(build) } - - let(:project) { FactoryGirl.create(:project) } - - context "One build" do - let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } - - let(:build) do - commit.create_builds - commit.builds.first - end - - context 'when build succeeds' do - it 'returns a successful message' do - build.update(status: "success") - - expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_false - expect( subject.to_s ).to match(/Build '[^']+' #\d+/) - expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) - end - end - - context 'when build fails' do - it 'returns a failure message' do - build.update(status: "failed") - - expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_true - expect( subject.to_s ).to match(/Build '[^']+' #\d+/) - expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) - end - end - end - - context "Several builds" do - let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } - - let(:build) do - commit.builds.first - end - - context 'when all matrix builds succeed' do - it 'returns a successful message' do - commit.create_builds - commit.builds.update_all(status: "success") - commit.reload - - expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_false - expect( subject.to_s ).to match(/Commit #\d+/) - expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) - end - end - - context 'when at least one matrix build fails' do - it 'returns a failure message' do - commit.create_builds - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_true - expect( subject.to_s ).to match(/Commit #\d+/) - expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) - end - end - end -end diff --git a/spec/ci/models/project_services/hip_chat_service_spec.rb b/spec/ci/models/project_services/hip_chat_service_spec.rb deleted file mode 100644 index 37ce4905af8..00000000000 --- a/spec/ci/models/project_services/hip_chat_service_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - - -require 'spec_helper' - -describe HipChatService do - - describe "Validations" do - - context "active" do - before do - subject.active = true - end - - it { should validate_presence_of :hipchat_room } - it { should validate_presence_of :hipchat_token } - - end - end - - describe "Execute" do - - let(:service) { HipChatService.new } - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } - let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } - - before do - service.stub( - project: project, - project_id: project.id, - notify_only_broken_builds: false, - hipchat_room: 123, - hipchat_token: 'a1b2c3d4e5f6' - ) - - WebMock.stub_request(:post, api_url) - end - - - it "should call the HipChat API" do - service.execute(build) - HipChatNotifierWorker.drain - - expect( WebMock ).to have_requested(:post, api_url).once - end - - it "calls the worker with expected arguments" do - expect( HipChatNotifierWorker ).to receive(:perform_async) \ - .with(an_instance_of(String), hash_including( - token: 'a1b2c3d4e5f6', - room: 123, - server: 'https://api.hipchat.com', - color: 'red', - notify: true - )) - - service.execute(build) - end - end -end - diff --git a/spec/ci/models/project_services/slack_message_spec.rb b/spec/ci/models/project_services/slack_message_spec.rb deleted file mode 100644 index 88e0f373206..00000000000 --- a/spec/ci/models/project_services/slack_message_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'spec_helper' - -describe SlackMessage do - subject { SlackMessage.new(commit) } - - let(:project) { FactoryGirl.create :project } - - context "One build" do - let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } - - let(:build) do - commit.create_builds - commit.builds.first - end - - context 'when build succeeded' do - let(:color) { 'good' } - - it 'returns a message with succeeded build' do - build.update(status: "success") - - subject.color.should == color - subject.fallback.should include('Build') - subject.fallback.should include("\##{build.id}") - subject.fallback.should include('succeeded') - subject.attachments.first[:fields].should be_empty - end - end - - context 'when build failed' do - let(:color) { 'danger' } - - it 'returns a message with failed build' do - build.update(status: "failed") - - subject.color.should == color - subject.fallback.should include('Build') - subject.fallback.should include("\##{build.id}") - subject.fallback.should include('failed') - subject.attachments.first[:fields].should be_empty - end - end - end - - context "Several builds" do - let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } - - context 'when all matrix builds succeeded' do - let(:color) { 'good' } - - it 'returns a message with success' do - commit.create_builds - commit.builds.update_all(status: "success") - commit.reload - - subject.color.should == color - subject.fallback.should include('Commit') - subject.fallback.should include("\##{commit.id}") - subject.fallback.should include('succeeded') - subject.attachments.first[:fields].should be_empty - end - end - - context 'when one of matrix builds failed' do - let(:color) { 'danger' } - - it 'returns a message with information about failed build' do - commit.create_builds - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - subject.color.should == color - subject.fallback.should include('Commit') - subject.fallback.should include("\##{commit.id}") - subject.fallback.should include('failed') - subject.attachments.first[:fields].size.should == 1 - subject.attachments.first[:fields].first[:title].should == second_build.name - subject.attachments.first[:fields].first[:value].should include("\##{second_build.id}") - end - end - end -end diff --git a/spec/ci/models/project_services/slack_service_spec.rb b/spec/ci/models/project_services/slack_service_spec.rb deleted file mode 100644 index e1c14281274..00000000000 --- a/spec/ci/models/project_services/slack_service_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe SlackService do - describe "Associations" do - it { should belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - - it { should validate_presence_of :webhook } - end - end - - describe "Execute" do - let(:slack) { SlackService.new } - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } - let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } - let(:notify_only_broken_builds) { false } - - before do - slack.stub( - project: project, - project_id: project.id, - webhook: webhook_url, - notify_only_broken_builds: notify_only_broken_builds - ) - - WebMock.stub_request(:post, webhook_url) - end - - it "should call Slack API" do - slack.execute(build) - SlackNotifierWorker.drain - - WebMock.should have_requested(:post, webhook_url).once - end - end -end diff --git a/spec/ci/models/project_spec.rb b/spec/ci/models/project_spec.rb deleted file mode 100644 index aa76b99154b..00000000000 --- a/spec/ci/models/project_spec.rb +++ /dev/null @@ -1,185 +0,0 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) not null -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -require 'spec_helper' - -describe Project do - subject { FactoryGirl.build :project } - - it { should have_many(:commits) } - - it { should validate_presence_of :name } - it { should validate_presence_of :timeout } - it { should validate_presence_of :default_ref } - - describe 'before_validation' do - it 'should set an random token if none provided' do - project = FactoryGirl.create :project_without_token - project.token.should_not == "" - end - - it 'should not set an random toke if one provided' do - project = FactoryGirl.create :project - project.token.should == "iPWx6WM4lhHNedGfBpPJNP" - end - end - - describe "ordered_by_last_commit_date" do - it "returns ordered projects" do - newest_project = FactoryGirl.create :project - oldest_project = FactoryGirl.create :project - project_without_commits = FactoryGirl.create :project - - FactoryGirl.create :commit, committed_at: 1.hour.ago, project: newest_project - FactoryGirl.create :commit, committed_at: 2.hour.ago, project: oldest_project - - Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits] - end - end - - context :valid_project do - let(:project) { FactoryGirl.create :project } - - context :project_with_commit_and_builds do - before do - commit = FactoryGirl.create(:commit, project: project) - FactoryGirl.create(:build, commit: commit) - end - - it { project.status.should == 'pending' } - it { project.last_commit.should be_kind_of(Commit) } - it { project.human_status.should == 'pending' } - end - end - - describe '#email_notification?' do - it do - project = FactoryGirl.create :project, email_add_pusher: true - project.email_notification?.should == true - end - - it do - project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: 'test tesft' - project.email_notification?.should == true - end - - it do - project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: '' - project.email_notification?.should == false - end - end - - describe '#broken_or_success?' do - it { - project = FactoryGirl.create :project, email_add_pusher: true - project.stub(:broken?).and_return(true) - project.stub(:success?).and_return(true) - project.broken_or_success?.should == true - } - - it { - project = FactoryGirl.create :project, email_add_pusher: true - project.stub(:broken?).and_return(true) - project.stub(:success?).and_return(false) - project.broken_or_success?.should == true - } - - it { - project = FactoryGirl.create :project, email_add_pusher: true - project.stub(:broken?).and_return(false) - project.stub(:success?).and_return(true) - project.broken_or_success?.should == true - } - - it { - project = FactoryGirl.create :project, email_add_pusher: true - project.stub(:broken?).and_return(false) - project.stub(:success?).and_return(false) - project.broken_or_success?.should == false - } - end - - describe 'Project.parse' do - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - let(:parsed_project) { Project.parse(project_dump) } - - - it { parsed_project.should be_valid } - it { parsed_project.should be_kind_of(Project) } - it { parsed_project.name.should eq("GitLab / api.gitlab.org") } - it { parsed_project.gitlab_id.should eq(189) } - it { parsed_project.gitlab_url.should eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } - - it "parses plain hash" do - Project.parse(project_dump).name.should eq("GitLab / api.gitlab.org") - end - end - - describe :repo_url_with_auth do - let(:project) { FactoryGirl.create :project } - subject { project.repo_url_with_auth } - - it { should be_a(String) } - it { should end_with(".git") } - it { should start_with(project.gitlab_url[0..6]) } - it { should include(project.token) } - it { should include('gitlab-ci-token') } - it { should include(project.gitlab_url[7..-1]) } - end - - describe :search do - let!(:project) { FactoryGirl.create(:project, name: "foo") } - - it { Project.search('fo').should include(project) } - it { Project.search('bar').should be_empty } - end - - describe :any_runners do - it "there are no runners available" do - project = FactoryGirl.create(:project) - project.any_runners?.should be_false - end - - it "there is a specific runner" do - project = FactoryGirl.create(:project) - project.runners << FactoryGirl.create(:specific_runner) - project.any_runners?.should be_true - end - - it "there is a shared runner" do - project = FactoryGirl.create(:project, shared_runners_enabled: true) - FactoryGirl.create(:shared_runner) - project.any_runners?.should be_true - end - - it "there is a shared runner, but they are prohibited to use" do - project = FactoryGirl.create(:project) - FactoryGirl.create(:shared_runner) - project.any_runners?.should be_false - end - end -end diff --git a/spec/ci/models/runner_project_spec.rb b/spec/ci/models/runner_project_spec.rb deleted file mode 100644 index cbefb24705a..00000000000 --- a/spec/ci/models/runner_project_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -# == Schema Information -# -# Table name: runner_projects -# -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -require 'spec_helper' - -describe RunnerProject do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/ci/models/runner_spec.rb b/spec/ci/models/runner_spec.rb deleted file mode 100644 index 6902c0a94e6..00000000000 --- a/spec/ci/models/runner_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -# == Schema Information -# -# Table name: runners -# -# id :integer not null, primary key -# token :string(255) -# created_at :datetime -# updated_at :datetime -# description :string(255) -# contacted_at :datetime -# active :boolean default(TRUE), not null -# is_shared :boolean default(FALSE) -# name :string(255) -# version :string(255) -# revision :string(255) -# platform :string(255) -# architecture :string(255) -# - -require 'spec_helper' - -describe Runner do - describe '#display_name' do - it 'should return the description if it has a value' do - runner = FactoryGirl.build(:runner, description: 'Linux/Ruby-1.9.3-p448') - expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448' - end - - it 'should return the token if it does not have a description' do - runner = FactoryGirl.create(:runner) - expect(runner.display_name).to eq runner.description - end - - it 'should return the token if the description is an empty string' do - runner = FactoryGirl.build(:runner, description: '') - expect(runner.display_name).to eq runner.token - end - end - - describe :assign_to do - let!(:project) { FactoryGirl.create :project } - let!(:shared_runner) { FactoryGirl.create(:shared_runner) } - - before { shared_runner.assign_to(project) } - - it { shared_runner.should be_specific } - it { shared_runner.projects.should == [project] } - it { shared_runner.only_for?(project).should be_true } - end - - describe "belongs_to_one_project?" do - it "returns false if there are two projects runner assigned to" do - runner = FactoryGirl.create(:specific_runner) - project = FactoryGirl.create(:project) - project1 = FactoryGirl.create(:project) - project.runners << runner - project1.runners << runner - - runner.belongs_to_one_project?.should be_false - end - - it "returns true" do - runner = FactoryGirl.create(:specific_runner) - project = FactoryGirl.create(:project) - project.runners << runner - - runner.belongs_to_one_project?.should be_true - end - end -end diff --git a/spec/ci/models/service_spec.rb b/spec/ci/models/service_spec.rb deleted file mode 100644 index 22a49e10a6c..00000000000 --- a/spec/ci/models/service_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Service do - - describe "Associations" do - it { should belong_to :project } - end - - describe "Mass assignment" do - end - - describe "Test Button" do - before do - @service = Service.new - end - - describe "Testable" do - let (:project) { FactoryGirl.create :project } - let (:commit) { FactoryGirl.create :commit, project: project } - let (:build) { FactoryGirl.create :build, commit: commit } - - before do - @service.stub( - project: project - ) - build - @testable = @service.can_test? - end - - describe :can_test do - it { @testable.should == true } - end - end - end -end diff --git a/spec/ci/models/trigger_spec.rb b/spec/ci/models/trigger_spec.rb deleted file mode 100644 index bba638e7817..00000000000 --- a/spec/ci/models/trigger_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe Trigger do - let(:project) { FactoryGirl.create :project } - - describe 'before_validation' do - it 'should set an random token if none provided' do - trigger = FactoryGirl.create :trigger_without_token, project: project - trigger.token.should_not be_nil - end - - it 'should not set an random token if one provided' do - trigger = FactoryGirl.create :trigger, project: project - trigger.token.should == 'token' - end - end -end diff --git a/spec/ci/models/user_spec.rb b/spec/ci/models/user_spec.rb deleted file mode 100644 index 73a7a7d5fbc..00000000000 --- a/spec/ci/models/user_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'spec_helper' - -describe User do - - describe "has_developer_access?" do - before do - @user = User.new({}) - end - - let(:project_with_owner_access) do - { - "name" => "gitlab-shell", - "permissions" => { - "project_access" => { - "access_level"=> 10, - "notification_level" => 3 - }, - "group_access" => { - "access_level" => 50, - "notification_level" => 3 - } - } - } - end - - let(:project_with_reporter_access) do - { - "name" => "gitlab-shell", - "permissions" => { - "project_access" => { - "access_level" => 20, - "notification_level" => 3 - }, - "group_access" => { - "access_level" => 10, - "notification_level" => 3 - } - } - } - end - - it "returns false for reporter" do - @user.stub(:project_info).and_return(project_with_reporter_access) - - @user.has_developer_access?(1).should be_false - end - - it "returns true for owner" do - @user.stub(:project_info).and_return(project_with_owner_access) - - @user.has_developer_access?(1).should be_true - end - end - - describe "authorized_projects" do - let (:user) { User.new({}) } - - before do - FactoryGirl.create :project, gitlab_id: 1 - FactoryGirl.create :project, gitlab_id: 2 - gitlab_project = OpenStruct.new({id: 1}) - gitlab_project1 = OpenStruct.new({id: 2}) - User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) - end - - it "returns projects" do - User.any_instance.stub(:can_manage_project?).and_return(true) - - user.authorized_projects.count.should == 2 - end - - it "empty list if user miss manage permission" do - User.any_instance.stub(:can_manage_project?).and_return(false) - - user.authorized_projects.count.should == 0 - end - end - - describe "authorized_runners" do - it "returns authorized runners" do - project = FactoryGirl.create :project, gitlab_id: 1 - project1 = FactoryGirl.create :project, gitlab_id: 2 - gitlab_project = OpenStruct.new({id: 1}) - gitlab_project1 = OpenStruct.new({id: 2}) - User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) - User.any_instance.stub(:can_manage_project?).and_return(true) - user = User.new({}) - - runner = FactoryGirl.create :specific_runner - runner1 = FactoryGirl.create :specific_runner - runner2 = FactoryGirl.create :specific_runner - - project.runners << runner - project1.runners << runner1 - - user.authorized_runners.should include(runner, runner1) - user.authorized_runners.should_not include(runner2) - end - end -end diff --git a/spec/ci/models/variable_spec.rb b/spec/ci/models/variable_spec.rb deleted file mode 100644 index 4575115ccfb..00000000000 --- a/spec/ci/models/variable_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# == Schema Information -# -# Table name: variables -# -# id :integer not null, primary key -# project_id :integer not null -# key :string(255) -# value :text -# encrypted_value :text -# encrypted_value_salt :string(255) -# encrypted_value_iv :string(255) -# - -require 'spec_helper' - -describe Variable do - subject { Variable.new } - - let(:secret_value) { 'secret' } - - before :each do - subject.value = secret_value - end - - describe :value do - it 'stores the encrypted value' do - subject.encrypted_value.should_not be_nil - end - - it 'stores an iv for value' do - subject.encrypted_value_iv.should_not be_nil - end - - it 'stores a salt for value' do - subject.encrypted_value_salt.should_not be_nil - end - - it 'fails to decrypt if iv is incorrect' do - subject.encrypted_value_iv = nil - subject.instance_variable_set(:@value, nil) - expect { subject.value }.to raise_error - end - end -end diff --git a/spec/ci/models/web_hook_spec.rb b/spec/ci/models/web_hook_spec.rb deleted file mode 100644 index 0f0f175a7a3..00000000000 --- a/spec/ci/models/web_hook_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -# == Schema Information -# -# Table name: web_hooks -# -# id :integer not null, primary key -# url :string(255) not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -require 'spec_helper' - -describe WebHook do - describe "Associations" do - it { should belong_to :project } - end - - describe "Validations" do - it { should validate_presence_of(:url) } - - context "url format" do - it { should allow_value("http://example.com").for(:url) } - it { should allow_value("https://excample.com").for(:url) } - it { should allow_value("http://test.com/api").for(:url) } - it { should allow_value("http://test.com/api?key=abc").for(:url) } - it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } - - it { should_not allow_value("example.com").for(:url) } - it { should_not allow_value("ftp://example.com").for(:url) } - it { should_not allow_value("herp-and-derp").for(:url) } - end - end - - describe "execute" do - before(:each) do - @web_hook = FactoryGirl.create(:web_hook) - @project = @web_hook.project - @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} - - WebMock.stub_request(:post, @web_hook.url) - end - - it "POSTs to the web hook URL" do - @web_hook.execute(@data) - WebMock.should have_requested(:post, @web_hook.url).once - end - - it "POSTs the data as JSON" do - json = @data.to_json - - @web_hook.execute(@data) - WebMock.should have_requested(:post, @web_hook.url).with(body: json).once - end - - it "catches exceptions" do - WebHook.should_receive(:post).and_raise("Some HTTP Post error") - - lambda { - @web_hook.execute(@data) - }.should raise_error - end - end -end diff --git a/spec/ci/requests/api/builds_spec.rb b/spec/ci/requests/api/builds_spec.rb deleted file mode 100644 index be55e9ff479..00000000000 --- a/spec/ci/requests/api/builds_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'spec_helper' - -describe API::API do - include ApiHelpers - - let(:runner) { FactoryGirl.create(:runner, tag_list: ["mysql", "ruby"]) } - let(:project) { FactoryGirl.create(:project) } - - describe "Builds API for runners" do - let(:shared_runner) { FactoryGirl.create(:runner, token: "SharedRunner") } - let(:shared_project) { FactoryGirl.create(:project, name: "SharedProject") } - - before do - FactoryGirl.create :runner_project, project_id: project.id, runner_id: runner.id - end - - describe "POST /builds/register" do - it "should start a build" do - commit = FactoryGirl.create(:commit, project: project) - commit.create_builds - build = commit.builds.first - - post api("/builds/register"), token: runner.token, info: {platform: :darwin} - - response.status.should == 201 - json_response['sha'].should == build.sha - runner.reload.platform.should == "darwin" - end - - it "should return 404 error if no pending build found" do - post api("/builds/register"), token: runner.token - - response.status.should == 404 - end - - it "should return 404 error if no builds for specific runner" do - commit = FactoryGirl.create(:commit, project: shared_project) - FactoryGirl.create(:build, commit: commit, status: 'pending' ) - - post api("/builds/register"), token: runner.token - - response.status.should == 404 - end - - it "should return 404 error if no builds for shared runner" do - commit = FactoryGirl.create(:commit, project: project) - FactoryGirl.create(:build, commit: commit, status: 'pending' ) - - post api("/builds/register"), token: shared_runner.token - - response.status.should == 404 - end - - it "returns options" do - commit = FactoryGirl.create(:commit, project: project) - commit.create_builds - - post api("/builds/register"), token: runner.token, info: {platform: :darwin} - - response.status.should == 201 - json_response["options"].should == {"image" => "ruby:2.1", "services" => ["postgres"]} - end - - it "returns variables" do - commit = FactoryGirl.create(:commit, project: project) - commit.create_builds - project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") - - post api("/builds/register"), token: runner.token, info: {platform: :darwin} - - response.status.should == 201 - json_response["variables"].should == [ - {"key" => "DB_NAME", "value" => "postgres", "public" => true}, - {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, - ] - end - - it "returns variables for triggers" do - trigger = FactoryGirl.create(:trigger, project: project) - commit = FactoryGirl.create(:commit, project: project) - - trigger_request = FactoryGirl.create(:trigger_request_with_variables, commit: commit, trigger: trigger) - commit.create_builds(trigger_request) - project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") - - post api("/builds/register"), token: runner.token, info: {platform: :darwin} - - response.status.should == 201 - json_response["variables"].should == [ - {"key" => "DB_NAME", "value" => "postgres", "public" => true}, - {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, - {"key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false}, - ] - end - end - - describe "PUT /builds/:id" do - let(:commit) { FactoryGirl.create(:commit, project: project)} - let(:build) { FactoryGirl.create(:build, commit: commit, runner_id: runner.id) } - - it "should update a running build" do - build.run! - put api("/builds/#{build.id}"), token: runner.token - response.status.should == 200 - end - - it 'Should not override trace information when no trace is given' do - build.run! - build.update!(trace: 'hello_world') - put api("/builds/#{build.id}"), token: runner.token - expect(build.reload.trace).to eq 'hello_world' - end - end - end -end diff --git a/spec/ci/requests/api/commits_spec.rb b/spec/ci/requests/api/commits_spec.rb deleted file mode 100644 index 190df70c1a5..00000000000 --- a/spec/ci/requests/api/commits_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'spec_helper' - -describe API::API, 'Commits' do - include ApiHelpers - - let(:project) { FactoryGirl.create(:project) } - let(:commit) { FactoryGirl.create(:commit, project: project) } - - let(:options) { - { - project_token: project.token, - project_id: project.id - } - } - - describe "GET /commits" do - before { commit } - - it "should return commits per project" do - get api("/commits"), options - - response.status.should == 200 - json_response.count.should == 1 - json_response.first["project_id"].should == project.id - json_response.first["sha"].should == commit.sha - end - end - - describe "POST /commits" do - let(:data) { - { - "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", - "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "ref" => "refs/heads/master", - "commits" => [ - { - "id" => "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "message" => "Update Catalan translation to e38cb41.", - "timestamp" => "2011-12-12T14:27:31+02:00", - "url" => "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "author" => { - "name" => "Jordi Mallach", - "email" => "jordi@softcatala.org", - } - } - ], - ci_yaml_file: gitlab_ci_yaml - } - } - - it "should create a build" do - post api("/commits"), options.merge(data: data) - - response.status.should == 201 - json_response['sha'].should == "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" - end - - it "should return 400 error if no data passed" do - post api("/commits"), options - - response.status.should == 400 - json_response['message'].should == "400 (Bad request) \"data\" not given" - end - end -end diff --git a/spec/ci/requests/api/forks_spec.rb b/spec/ci/requests/api/forks_spec.rb deleted file mode 100644 index af523421c65..00000000000 --- a/spec/ci/requests/api/forks_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'spec_helper' - -describe API::API do - include ApiHelpers - - let(:project) { FactoryGirl.create(:project) } - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - - let(:options) { - { - private_token: private_token, - url: gitlab_url - } - } - - before { - stub_gitlab_calls - } - - - describe "POST /forks" do - let(:project_info) { - { - project_id: project.gitlab_id, - project_token: project.token, - data: { - id: 2, - name_with_namespace: "Gitlab.org / Underscore", - path_with_namespace: "gitlab-org/underscore", - default_branch: "master", - ssh_url_to_repo: "git@example.com:gitlab-org/underscore" - } - } - } - - context "with valid info" do - before do - options.merge!(project_info) - end - - it "should create a project with valid data" do - post api("/forks"), options - response.status.should == 201 - json_response['name'].should == "Gitlab.org / Underscore" - end - end - - context "with invalid project info" do - before do - options.merge!({}) - end - - it "should error with invalid data" do - post api("/forks"), options - response.status.should == 400 - end - end - end -end diff --git a/spec/ci/requests/api/projects_spec.rb b/spec/ci/requests/api/projects_spec.rb deleted file mode 100644 index 014a9efc617..00000000000 --- a/spec/ci/requests/api/projects_spec.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'spec_helper' - -describe API::API do - include ApiHelpers - - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - - let(:options) { - { - private_token: private_token, - url: gitlab_url - } - } - - before { - stub_gitlab_calls - } - - context "requests for scoped projects" do - # NOTE: These ids are tied to the actual projects on demo.gitlab.com - describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:project, name: "gitlab-ci", gitlab_id: 4) } - - it "should return all projects on the CI instance" do - get api("/projects"), options - response.status.should == 200 - json_response.count.should == 2 - json_response.first["id"].should == project1.id - json_response.last["id"].should == project2.id - end - end - - describe "GET /projects/owned" do - # NOTE: This user doesn't own any of these projects on demo.gitlab.com - let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:project, name: "random-project", gitlab_id: 9898) } - - it "should return all projects on the CI instance" do - get api("/projects/owned"), options - - response.status.should == 200 - json_response.count.should == 0 - end - end - end - - describe "POST /projects/:project_id/webhooks" do - let!(:project) { FactoryGirl.create(:project) } - - context "Valid Webhook URL" do - let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "should create webhook for specified project" do - post api("/projects/#{project.id}/webhooks"), options - response.status.should == 201 - json_response["url"].should == webhook[:web_hook] - end - - it "fails to create webhook for non existsing project" do - post api("/projects/non-existant-id/webhooks"), options - response.status.should == 404 - end - - it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/webhooks"), options - response.status.should == 401 - end - end - - context "Invalid Webhook URL" do - let!(:webhook) { {web_hook: "ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "fails to create webhook for not valid url" do - post api("/projects/#{project.id}/webhooks"), options - response.status.should == 400 - end - end - - context "Missed web_hook parameter" do - it "fails to create webhook for not provided url" do - post api("/projects/#{project.id}/webhooks"), options - response.status.should == 400 - end - end - end - - describe "GET /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } - - context "with an existing project" do - it "should retrieve the project info" do - get api("/projects/#{project.id}"), options - response.status.should == 200 - json_response['id'].should == project.id - end - end - - context "with a non-existing project" do - it "should return 404 error if project not found" do - get api("/projects/non_existent_id"), options - response.status.should == 404 - end - end - end - - describe "PUT /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } - let!(:project_info) { {name: "An updated name!" } } - - before do - options.merge!(project_info) - end - - it "should update a specific project's information" do - put api("/projects/#{project.id}"), options - response.status.should == 200 - json_response["name"].should == project_info[:name] - end - - it "fails to update a non-existing project" do - put api("/projects/non-existant-id"), options - response.status.should == 404 - end - - it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) - put api("/projects/#{project.id}"), options - response.status.should == 401 - end - end - - describe "DELETE /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } - - it "should delete a specific project" do - delete api("/projects/#{project.id}"), options - response.status.should == 200 - - expect { project.reload }.to raise_error - end - - it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) - delete api("/projects/#{project.id}"), options - response.status.should == 401 - end - - it "is getting not found error" do - delete api("/projects/not-existing_id"), options - response.status.should == 404 - end - end - - describe "POST /projects" do - let(:project_info) { - { - name: "My project", - gitlab_id: 1, - path: "testing/testing", - ssh_url_to_repo: "ssh://example.com/testing/testing.git" - } - } - - let(:invalid_project_info) { {} } - - context "with valid project info" do - before do - options.merge!(project_info) - end - - it "should create a project with valid data" do - post api("/projects"), options - response.status.should == 201 - json_response['name'].should == project_info[:name] - end - end - - context "with invalid project info" do - before do - options.merge!(invalid_project_info) - end - - it "should error with invalid data" do - post api("/projects"), options - response.status.should == 400 - end - end - - describe "POST /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:project) } - let(:runner) { FactoryGirl.create(:runner) } - - it "should add the project to the runner" do - post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 201 - - project.reload - project.runners.first.id.should == runner.id - end - - it "should fail if it tries to link a non-existing project or runner" do - post api("/projects/#{project.id}/runners/non-existing"), options - response.status.should == 404 - - post api("/projects/non-existing/runners/#{runner.id}"), options - response.status.should == 404 - end - - it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 401 - end - end - - describe "DELETE /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:project) } - let(:runner) { FactoryGirl.create(:runner) } - - before do - post api("/projects/#{project.id}/runners/#{runner.id}"), options - end - - it "should remove the project from the runner" do - project.runners.should be_present - delete api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 200 - - project.reload - project.runners.should be_empty - end - - it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 401 - end - end - end -end diff --git a/spec/ci/requests/api/runners_spec.rb b/spec/ci/requests/api/runners_spec.rb deleted file mode 100644 index 47de3c2a95c..00000000000 --- a/spec/ci/requests/api/runners_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'spec_helper' - -describe API::API do - include ApiHelpers - include StubGitlabCalls - - before { - stub_gitlab_calls - } - - describe "GET /runners" do - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - let(:options) { - { - :private_token => private_token, - :url => gitlab_url - } - } - - before do - 5.times { FactoryGirl.create(:runner) } - end - - it "should retrieve a list of all runners" do - get api("/runners"), options - response.status.should == 200 - json_response.count.should == 5 - json_response.last.should have_key("id") - json_response.last.should have_key("token") - end - end - - describe "POST /runners/register" do - describe "should create a runner if token provided" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } - - it { response.status.should == 201 } - end - - describe "should create a runner with description" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } - - it { response.status.should == 201 } - it { Runner.first.description.should == "server.hostname" } - end - - describe "should create a runner with tags" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } - - it { response.status.should == 201 } - it { Runner.first.tag_list.sort.should == ["tag1", "tag2"] } - end - - describe "should create a runner if project token provided" do - let(:project) { FactoryGirl.create(:project) } - before { post api("/runners/register"), token: project.token } - - it { response.status.should == 201 } - it { project.runners.size.should == 1 } - end - - it "should return 403 error if token is invalid" do - post api("/runners/register"), token: 'invalid' - - response.status.should == 403 - end - - it "should return 400 error if no token" do - post api("/runners/register") - - response.status.should == 400 - end - end - - describe "DELETE /runners/delete" do - let!(:runner) { FactoryGirl.create(:runner) } - before { delete api("/runners/delete"), token: runner.token } - - it { response.status.should == 200 } - it { Runner.count.should == 0 } - end -end diff --git a/spec/ci/requests/api/triggers_spec.rb b/spec/ci/requests/api/triggers_spec.rb deleted file mode 100644 index 6e56c4b3b22..00000000000 --- a/spec/ci/requests/api/triggers_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'spec_helper' - -describe API::API do - include ApiHelpers - - describe 'POST /projects/:project_id/refs/:ref/trigger' do - let!(:trigger_token) { 'secure token' } - let!(:project) { FactoryGirl.create(:project) } - let!(:project2) { FactoryGirl.create(:project) } - let!(:trigger) { FactoryGirl.create(:trigger, project: project, token: trigger_token) } - let(:options) { - { - token: trigger_token - } - } - - context 'Handles errors' do - it 'should return bad request if token is missing' do - post api("/projects/#{project.id}/refs/master/trigger") - response.status.should == 400 - end - - it 'should return not found if project is not found' do - post api('/projects/0/refs/master/trigger'), options - response.status.should == 404 - end - - it 'should return unauthorized if token is for different project' do - post api("/projects/#{project2.id}/refs/master/trigger"), options - response.status.should == 401 - end - end - - context 'Have a commit' do - before do - @commit = FactoryGirl.create(:commit, project: project) - end - - it 'should create builds' do - post api("/projects/#{project.id}/refs/master/trigger"), options - response.status.should == 201 - @commit.builds.reload - @commit.builds.size.should == 2 - end - - it 'should return bad request with no builds created if there\'s no commit for that ref' do - post api("/projects/#{project.id}/refs/other-branch/trigger"), options - response.status.should == 400 - json_response['message'].should == 'No builds created' - end - - context 'Validates variables' do - let(:variables) { - {'TRIGGER_KEY' => 'TRIGGER_VALUE'} - } - - it 'should validate variables to be a hash' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') - response.status.should == 400 - json_response['message'].should == 'variables needs to be a hash' - end - - it 'should validate variables needs to be a map of key-valued strings' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: {key: %w(1 2)}) - response.status.should == 400 - json_response['message'].should == 'variables needs to be a map of key-valued strings' - end - - it 'create trigger request with variables' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) - response.status.should == 201 - @commit.builds.reload - @commit.builds.first.trigger_request.variables.should == variables - end - end - end - end -end diff --git a/spec/ci/requests/builds_spec.rb b/spec/ci/requests/builds_spec.rb deleted file mode 100644 index 73d540e372a..00000000000 --- a/spec/ci/requests/builds_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe "GET /:project/builds/:id/status.json" do - before do - get status_project_build_path(@project, @build), format: :json - end - - it { response.status.should == 200 } - it { response.body.should include(@build.sha) } - end -end diff --git a/spec/ci/requests/commits_spec.rb b/spec/ci/requests/commits_spec.rb deleted file mode 100644 index e9d8366c41a..00000000000 --- a/spec/ci/requests/commits_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe "Commits" do - before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - end - - describe "GET /:project/refs/:ref_name/commits/:id/status.json" do - before do - get status_project_ref_commit_path(@project, @commit.ref, @commit.sha), format: :json - end - - it { response.status.should == 200 } - it { response.body.should include(@commit.sha) } - end -end diff --git a/spec/ci/services/create_commit_service_spec.rb b/spec/ci/services/create_commit_service_spec.rb deleted file mode 100644 index 34e00d5b3c0..00000000000 --- a/spec/ci/services/create_commit_service_spec.rb +++ /dev/null @@ -1,130 +0,0 @@ -require 'spec_helper' - -describe CreateCommitService do - let(:service) { CreateCommitService.new } - let(:project) { FactoryGirl.create(:project) } - - describe :execute do - context 'valid params' do - let(:commit) do - service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] - ) - end - - it { commit.should be_kind_of(Commit) } - it { commit.should be_valid } - it { commit.should be_persisted } - it { commit.should == project.commits.last } - it { commit.builds.first.should be_kind_of(Build) } - end - - context "skip tag if there is no build for it" do - it "creates commit if there is appropriate job" do - result = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] - ) - result.should be_persisted - end - - it "creates commit if there is no appropriate job but deploy job has right ref setting" do - config = YAML.dump({deploy: {deploy: "ls", only: ["0_1"]}}) - - result = service.execute(project, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - ci_yaml_file: config, - commits: [ { message: "Message" } ] - ) - result.should be_persisted - end - end - - describe :ci_skip? do - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{message: "some message[ci skip]"}] - commit = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - commit.builds.any?.should be_false - commit.status.should == "skipped" - end - - it "does not skips builds creation if there is no [ci skip] tag in commit message" do - commits = [{message: "some message"}] - - commit = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - - commit.builds.first.name.should == "staging" - end - - it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - commits = [{message: "some message[ci skip]"}] - commit = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: "invalid: file" - ) - commit.builds.any?.should be_false - commit.status.should == "skipped" - end - end - - it "skips build creation if there are already builds" do - commits = [{message: "message"}] - commit = service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - commit.builds.count(:all).should == 2 - - commit = service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - commit.builds.count(:all).should == 2 - end - - it "creates commit with failed status if yaml is invalid" do - commits = [{message: "some message"}] - - commit = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: "invalid: file" - ) - - commit.status.should == "failed" - commit.builds.any?.should be_false - end - end -end diff --git a/spec/ci/services/create_project_service_spec.rb b/spec/ci/services/create_project_service_spec.rb deleted file mode 100644 index 31614968d55..00000000000 --- a/spec/ci/services/create_project_service_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'spec_helper' - -describe CreateProjectService do - let(:service) { CreateProjectService.new } - let(:current_user) { double.as_null_object } - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - - before { Network.any_instance.stub(enable_ci: true) } - - describe :execute do - context 'valid params' do - let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } - - it { project.should be_kind_of(Project) } - it { project.should be_persisted } - end - - context 'without project dump' do - it 'should raise exception' do - expect { service.execute(current_user, '', '') }.to raise_error - end - end - - context "forking" do - it "uses project as a template for settings and jobs" do - origin_project = FactoryGirl.create(:project) - origin_project.shared_runners_enabled = true - origin_project.public = true - origin_project.allow_git_fetch = true - origin_project.save! - - project = service.execute(current_user, project_dump, 'http://localhost/projects/:project_id', origin_project) - - project.shared_runners_enabled.should be_true - project.public.should be_true - project.allow_git_fetch.should be_true - end - end - end -end diff --git a/spec/ci/services/create_trigger_request_service_spec.rb b/spec/ci/services/create_trigger_request_service_spec.rb deleted file mode 100644 index 41db01c2235..00000000000 --- a/spec/ci/services/create_trigger_request_service_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'spec_helper' - -describe CreateTriggerRequestService do - let(:service) { CreateTriggerRequestService.new } - let(:project) { FactoryGirl.create :project } - let(:trigger) { FactoryGirl.create :trigger, project: project } - - describe :execute do - context 'valid params' do - subject { service.execute(project, trigger, 'master') } - - before do - @commit = FactoryGirl.create :commit, project: project - end - - it { subject.should be_kind_of(TriggerRequest) } - it { subject.commit.should == @commit } - end - - context 'no commit for ref' do - subject { service.execute(project, trigger, 'other-branch') } - - it { subject.should be_nil } - end - - context 'no builds created' do - subject { service.execute(project, trigger, 'master') } - - before do - FactoryGirl.create :commit_without_jobs, project: project - end - - it { subject.should be_nil } - end - - context 'for multiple commits' do - subject { service.execute(project, trigger, 'master') } - - before do - @commit1 = FactoryGirl.create :commit, committed_at: 2.hour.ago, project: project - @commit2 = FactoryGirl.create :commit, committed_at: 1.hour.ago, project: project - @commit3 = FactoryGirl.create :commit, committed_at: 3.hour.ago, project: project - end - - context 'retries latest one' do - it { subject.should be_kind_of(TriggerRequest) } - it { subject.should be_persisted } - it { subject.commit.should == @commit2 } - end - end - end -end diff --git a/spec/ci/services/event_service_spec.rb b/spec/ci/services/event_service_spec.rb deleted file mode 100644 index f7b9bf58127..00000000000 --- a/spec/ci/services/event_service_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -describe EventService do - let (:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } - let (:user) { double(username: "root", id: 1) } - - before do - Event.destroy_all - end - - describe :remove_project do - it "creates event" do - EventService.new.remove_project(user, project) - - Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been removed by root" - end - end - - describe :create_project do - it "creates event" do - EventService.new.create_project(user, project) - - Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been created by root" - end - end - - describe :change_project_settings do - it "creates event" do - EventService.new.change_project_settings(user, project) - - Event.last.description.should == "User \"root\" updated projects settings" - end - end -end \ No newline at end of file diff --git a/spec/ci/services/image_for_build_service_spec.rb b/spec/ci/services/image_for_build_service_spec.rb deleted file mode 100644 index 4c7094146bb..00000000000 --- a/spec/ci/services/image_for_build_service_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'spec_helper' - -describe ImageForBuildService do - let(:service) { ImageForBuildService.new } - let(:project) { FactoryGirl.create(:project) } - let(:commit) { FactoryGirl.create(:commit, project: project, ref: 'master') } - let(:build) { FactoryGirl.create(:build, commit: commit) } - - describe :execute do - before { build } - - context 'branch name' do - before { build.run! } - let(:image) { service.execute(project, ref: 'master') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-running.svg') } - it { image.name.should == 'build-running.svg' } - end - - context 'unknown branch name' do - let(:image) { service.execute(project, ref: 'feature') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-unknown.svg') } - it { image.name.should == 'build-unknown.svg' } - end - - context 'commit sha' do - before { build.run! } - let(:image) { service.execute(project, sha: build.sha) } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-running.svg') } - it { image.name.should == 'build-running.svg' } - end - - context 'unknown commit sha' do - let(:image) { service.execute(project, sha: '0000000') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-unknown.svg') } - it { image.name.should == 'build-unknown.svg' } - end - end -end diff --git a/spec/ci/services/register_build_service_spec.rb b/spec/ci/services/register_build_service_spec.rb deleted file mode 100644 index b5af777dd1d..00000000000 --- a/spec/ci/services/register_build_service_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'spec_helper' - -describe RegisterBuildService do - let!(:service) { RegisterBuildService.new } - let!(:project) { FactoryGirl.create :project } - let!(:commit) { FactoryGirl.create :commit, project: project } - let!(:pending_build) { FactoryGirl.create :build, project: project, commit: commit } - let!(:shared_runner) { FactoryGirl.create(:runner, is_shared: true) } - let!(:specific_runner) { FactoryGirl.create(:runner, is_shared: false) } - - before do - specific_runner.assign_to(project) - end - - describe :execute do - context 'runner follow tag list' do - it "picks build with the same tag" do - pending_build.tag_list = ["linux"] - pending_build.save - specific_runner.tag_list = ["linux"] - service.execute(specific_runner).should == pending_build - end - - it "does not pick build with different tag" do - pending_build.tag_list = ["linux"] - pending_build.save - specific_runner.tag_list = ["win32"] - service.execute(specific_runner).should be_false - end - - it "picks build without tag" do - service.execute(specific_runner).should == pending_build - end - - it "does not pick build with tag" do - pending_build.tag_list = ["linux"] - pending_build.save - service.execute(specific_runner).should be_false - end - - it "pick build without tag" do - specific_runner.tag_list = ["win32"] - service.execute(specific_runner).should == pending_build - end - end - - context 'allow shared runners' do - before do - project.shared_runners_enabled = true - project.save - end - - context 'shared runner' do - let(:build) { service.execute(shared_runner) } - - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == shared_runner } - end - - context 'specific runner' do - let(:build) { service.execute(specific_runner) } - - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == specific_runner } - end - end - - context 'disallow shared runners' do - context 'shared runner' do - let(:build) { service.execute(shared_runner) } - - it { build.should be_nil } - end - - context 'specific runner' do - let(:build) { service.execute(specific_runner) } - - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == specific_runner } - end - end - end -end diff --git a/spec/ci/services/web_hook_service_spec.rb b/spec/ci/services/web_hook_service_spec.rb deleted file mode 100644 index 2bb153942e8..00000000000 --- a/spec/ci/services/web_hook_service_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'spec_helper' - -describe WebHookService do - let (:project) { FactoryGirl.create :project } - let (:commit) { FactoryGirl.create :commit, project: project } - let (:build) { FactoryGirl.create :build, commit: commit } - let (:hook) { FactoryGirl.create :web_hook, project: project } - - describe :execute do - it "should execute successfully" do - stub_request(:post, hook.url).to_return(status: 200) - WebHookService.new.build_end(build).should be_true - end - end - - context 'build_data' do - it "contains all needed fields" do - build_data(build).should include( - :build_id, - :project_id, - :ref, - :build_status, - :build_started_at, - :build_finished_at, - :before_sha, - :project_name, - :gitlab_url, - :build_name - ) - end - end - - def build_data(build) - WebHookService.new.send :build_data, build - end -end diff --git a/spec/ci/six.tar.gz b/spec/ci/six.tar.gz deleted file mode 100644 index 80a8c6644e4..00000000000 Binary files a/spec/ci/six.tar.gz and /dev/null differ diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index 0069a782511..9af766eff33 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -1,8 +1,8 @@ require "spec_helper" -describe ProjectsController do +describe Ci::ProjectsController do before do - @project = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project end describe "POST #build" do @@ -55,25 +55,25 @@ describe ProjectsController do end let(:user) do - User.new(user_data) + Ci::User.new(user_data) end it "creates project" do allow(controller).to receive(:reset_cache) { true } allow(controller).to receive(:current_user) { user } - Network.any_instance.stub(:enable_ci).and_return(true) - Network.any_instance.stub(:project_hooks).and_return(true) + allow_any_instance_of(Ci::Network).to receive(:enable_ci).and_return(true) + allow_any_instance_of(Ci::Network).to receive(:project_hooks).and_return(true) post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access expect(response.code).to eq('302') - expect(assigns(:project)).not_to be_a_new(Project) + expect(assigns(:project)).not_to be_a_new(Ci::Project) end it "shows error" do allow(controller).to receive(:reset_cache) { true } allow(controller).to receive(:current_user) { user } - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(Ci::User).to receive(:can_manage_project?).and_return(false) post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access @@ -91,13 +91,13 @@ describe ProjectsController do end let(:user) do - User.new(user_data) + Ci::User.new(user_data) end it "searches projects" do allow(controller).to receive(:reset_cache) { true } allow(controller).to receive(:current_user) { user } - Network.any_instance.should_receive(:projects).with(hash_including(search: 'str'), :authorized) + allow_any_instance_of(Ci::Network).to receive(:projects).with(hash_including(search: 'str'), :authorized) xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb index c85d1027ce6..da8b7342fcf 100644 --- a/spec/factories/ci/trigger_requests.rb +++ b/spec/factories/ci/trigger_requests.rb @@ -1,8 +1,8 @@ # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do - factory :trigger_request do - factory :trigger_request_with_variables do + factory :ci_trigger_request do + factory :ci_trigger_request_with_variables do variables do { TRIGGER_KEY: 'TRIGGER_VALUE' diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb index 38cd3cbceb6..fd3afdb1ec2 100644 --- a/spec/factories/ci/triggers.rb +++ b/spec/factories/ci/triggers.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :ci_trigger_without_token, class: Ci::Trigger do - factory :trigger do + factory :ci_trigger do token 'token' end end diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/ci/admin/builds_spec.rb new file mode 100644 index 00000000000..e62e83692da --- /dev/null +++ b/spec/features/ci/admin/builds_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe "Admin Builds" do + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/builds" do + before do + build + visit admin_builds_path + end + + it { page.should have_content "All builds" } + it { page.should have_content build.short_sha } + end + + describe "Tabs" do + it "shows all builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + page.all(".build-link").size.should == 4 + end + + it "shows pending builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".nav.nav-tabs" do + click_on "Pending" + end + + page.find(".build-link").should have_content(build.id) + page.find(".build-link").should_not have_content(build1.id) + page.find(".build-link").should_not have_content(build2.id) + page.find(".build-link").should_not have_content(build3.id) + end + + it "shows running builds" do + build = FactoryGirl.create :build, commit: commit, status: "pending" + build1 = FactoryGirl.create :build, commit: commit, status: "running" + build2 = FactoryGirl.create :build, commit: commit, status: "success" + build3 = FactoryGirl.create :build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".nav.nav-tabs" do + click_on "Running" + end + + page.find(".build-link").should have_content(build1.id) + page.find(".build-link").should_not have_content(build.id) + page.find(".build-link").should_not have_content(build2.id) + page.find(".build-link").should_not have_content(build3.id) + end + end +end diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb new file mode 100644 index 00000000000..469c6ed102d --- /dev/null +++ b/spec/features/ci/admin/events_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "Admin Events" do + let(:event) { FactoryGirl.create :admin_event } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/events" do + before do + event + visit admin_events_path + end + + it { page.should have_content "Events" } + it { page.should have_content event.description } + end +end diff --git a/spec/features/ci/admin/projects_spec.rb b/spec/features/ci/admin/projects_spec.rb new file mode 100644 index 00000000000..6f87e368deb --- /dev/null +++ b/spec/features/ci/admin/projects_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe "Admin Projects" do + let(:project) { FactoryGirl.create :project } + + before do + skip_admin_auth + login_as :user + end + + describe "GET /admin/projects" do + before do + project + visit admin_projects_path + end + + it { page.should have_content "Projects" } + end +end diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb new file mode 100644 index 00000000000..2827a7fc6e5 --- /dev/null +++ b/spec/features/ci/admin/runners_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe "Admin Runners" do + before do + skip_admin_auth + login_as :user + end + + describe "Runners page" do + before do + runner = FactoryGirl.create(:runner) + commit = FactoryGirl.create(:commit) + FactoryGirl.create(:build, commit: commit, runner_id: runner.id) + visit admin_runners_path + end + + it { page.has_text? "Manage Runners" } + it { page.has_text? "To register a new runner" } + it { page.has_text? "Runners with last contact less than a minute ago: 1" } + + describe 'search' do + before do + FactoryGirl.create :runner, description: 'foo' + FactoryGirl.create :runner, description: 'bar' + + fill_in 'search', with: 'foo' + click_button 'Search' + end + + it { page.should have_content("foo") } + it { page.should_not have_content("bar") } + end + end + + describe "Runner show page" do + let(:runner) { FactoryGirl.create :runner } + + before do + FactoryGirl.create(:project, name: "foo") + FactoryGirl.create(:project, name: "bar") + visit admin_runner_path(runner) + end + + describe 'runner info' do + it { find_field('runner_token').value.should eq runner.token } + end + + describe 'projects' do + it { page.should have_content("foo") } + it { page.should have_content("bar") } + end + + describe 'search' do + before do + fill_in 'search', with: 'foo' + click_button 'Search' + end + + it { page.should have_content("foo") } + it { page.should_not have_content("bar") } + end + end +end diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb new file mode 100644 index 00000000000..fcd7996efd7 --- /dev/null +++ b/spec/features/ci/builds_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe "Builds" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/builds/:id" do + before do + login_as :user + visit project_build_path(@project, @build) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + + describe "GET /:project/builds/:id/cancel" do + before do + login_as :user + @build.run! + visit cancel_project_build_path(@project, @build) + end + + it { page.should have_content 'canceled' } + it { page.should have_content 'Retry' } + end + + describe "POST /:project/builds/:id/retry" do + before do + login_as :user + @build.cancel! + visit project_build_path(@project, @build) + click_link 'Retry' + end + + it { page.should have_content 'pending' } + it { page.should have_content 'Cancel' } + end + + describe "Show page public accessible" do + before do + @project = FactoryGirl.create :public_project + @commit = FactoryGirl.create :commit, project: @project + @runner = FactoryGirl.create :specific_runner + @build = FactoryGirl.create :build, commit: @commit, runner: @runner + + stub_gitlab_calls + visit project_build_path(@project, @build) + end + + it { page.should have_content @commit.sha[0..7] } + end +end diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb new file mode 100644 index 00000000000..202f05c516f --- /dev/null +++ b/spec/features/ci/commits_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe "Commits" do + context "Authenticated user" do + before do + login_as :user + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/commits/:sha" do + before do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + + describe "Cancel commit" do + it "cancels commit" do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + click_on "Cancel" + + page.should have_content "canceled" + end + end + + describe ".gitlab-ci.yml not found warning" do + it "does not show warning" do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + + page.should_not have_content ".gitlab-ci.yml not found in this commit" + end + + it "shows warning" do + @commit.push_data[:ci_yaml_file] = nil + @commit.save + + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + + page.should have_content ".gitlab-ci.yml not found in this commit" + end + end + end + + context "Public pages" do + before do + @project = FactoryGirl.create :public_project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/commits/:sha" do + before do + visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + end + + it { page.should have_content @commit.sha[0..7] } + it { page.should have_content @commit.git_commit_message } + it { page.should have_content @commit.git_author_name } + end + end +end diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb new file mode 100644 index 00000000000..77d1fba5769 --- /dev/null +++ b/spec/features/ci/events_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "Events" do + let(:project) { FactoryGirl.create :project } + let(:event) { FactoryGirl.create :admin_event, project: project } + + before do + login_as :user + end + + describe "GET /project/:id/events" do + before do + event + visit project_events_path(project) + end + + it { page.should have_content "Events" } + it { page.should have_content event.description } + end +end diff --git a/spec/features/ci/lint_spec.rb b/spec/features/ci/lint_spec.rb new file mode 100644 index 00000000000..0b3d4e099fb --- /dev/null +++ b/spec/features/ci/lint_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe "Lint" do + before do + login_as :user + end + + it "Yaml parsing", js: true do + content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + visit lint_path + fill_in "content", with: content + click_on "Validate" + within "table" do + page.should have_content("Job - rspec") + page.should have_content("Job - spinach") + page.should have_content("Deploy Job - staging") + page.should have_content("Deploy Job - production") + end + end + + it "Yaml parsing with error", js: true do + visit lint_path + fill_in "content", with: "" + click_on "Validate" + page.should have_content("Status: syntax is incorrect") + page.should have_content("Error: Please provide content of .gitlab-ci.yml") + end +end diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb new file mode 100644 index 00000000000..3f21af92a2b --- /dev/null +++ b/spec/features/ci/projects_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe "Projects" do + before do + login_as :user + @project = FactoryGirl.create :project, name: "GitLab / gitlab-shell" + end + + describe "GET /projects", js: true do + before do + stub_js_gitlab_calls + visit projects_path + end + + it { page.should have_content "GitLab / gitlab-shell" } + it { page.should have_selector ".search input#search" } + end + + describe "GET /projects/:id" do + before do + visit project_path(@project) + end + + it { page.should have_content @project.name } + it { page.should have_content 'All commits' } + end + + describe "GET /projects/:id/edit" do + before do + visit edit_project_path(@project) + end + + it { page.should have_content @project.name } + it { page.should have_content 'Build Schedule' } + + it "updates configuration" do + fill_in 'Timeout', with: '70' + click_button 'Save changes' + + page.should have_content 'was successfully updated' + + find_field('Timeout').value.should eq '70' + end + end + + describe "GET /projects/:id/charts" do + before do + visit project_charts_path(@project) + end + + it { page.should have_content 'Overall' } + it { page.should have_content 'Builds chart for last week' } + it { page.should have_content 'Builds chart for last month' } + it { page.should have_content 'Builds chart for last year' } + it { page.should have_content 'Commit duration in minutes for last 30 commits' } + end +end diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb new file mode 100644 index 00000000000..c41dc5b2e2e --- /dev/null +++ b/spec/features/ci/runners_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe "Runners" do + before do + login_as :user + end + + describe "specific runners" do + before do + @project = FactoryGirl.create :project + @project2 = FactoryGirl.create :project + stub_js_gitlab_calls + + # all projects should be authorized for user + Network.any_instance.stub(:projects).and_return([ + OpenStruct.new({id: @project.gitlab_id}), + OpenStruct.new({id: @project2.gitlab_id}) + ]) + + @shared_runner = FactoryGirl.create :shared_runner + @specific_runner = FactoryGirl.create :specific_runner + @specific_runner2 = FactoryGirl.create :specific_runner + @project.runners << @specific_runner + @project2.runners << @specific_runner2 + end + + it "places runners in right places" do + visit project_runners_path(@project) + page.find(".available-specific-runners").should have_content(@specific_runner2.display_name) + page.find(".activated-specific-runners").should have_content(@specific_runner.display_name) + page.find(".available-shared-runners").should have_content(@shared_runner.display_name) + end + + it "enables specific runner for project" do + visit project_runners_path(@project) + + within ".available-specific-runners" do + click_on "Enable for this project" + end + + page.find(".activated-specific-runners").should have_content(@specific_runner2.display_name) + end + + it "disables specific runner for project" do + @project2.runners << @specific_runner + + visit project_runners_path(@project) + + within ".activated-specific-runners" do + click_on "Disable for this project" + end + + page.find(".available-specific-runners").should have_content(@specific_runner.display_name) + end + + it "removes specific runner for project if this is last project for that runners" do + visit project_runners_path(@project) + + within ".activated-specific-runners" do + click_on "Remove runner" + end + + Runner.exists?(id: @specific_runner).should be_false + end + end + + describe "shared runners" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + end + + it "enables shared runners" do + visit project_runners_path(@project) + + click_on "Enable shared runners" + + @project.reload.shared_runners_enabled.should be_true + end + end + + describe "show page" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + @specific_runner = FactoryGirl.create :specific_runner + @project.runners << @specific_runner + end + + it "shows runner information" do + visit project_runners_path(@project) + + click_on @specific_runner.short_sha + + page.should have_content(@specific_runner.platform) + end + end +end diff --git a/spec/features/ci/triggers_spec.rb b/spec/features/ci/triggers_spec.rb new file mode 100644 index 00000000000..2076429383d --- /dev/null +++ b/spec/features/ci/triggers_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'Variables' do + before do + login_as :user + @project = FactoryGirl.create :project + stub_js_gitlab_calls + visit project_triggers_path(@project) + end + + context 'create a trigger' do + before do + click_on 'Add Trigger' + @project.triggers.count.should == 1 + end + + it 'contains trigger token' do + page.should have_content(@project.triggers.first.token) + end + + it 'revokes the trigger' do + click_on 'Revoke' + @project.triggers.count.should == 0 + end + end +end diff --git a/spec/features/ci/variables_spec.rb b/spec/features/ci/variables_spec.rb new file mode 100644 index 00000000000..2bb0d9dedde --- /dev/null +++ b/spec/features/ci/variables_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe "Variables" do + before do + login_as :user + end + + describe "specific runners" do + before do + @project = FactoryGirl.create :project + stub_js_gitlab_calls + end + + it "creates variable", js: true do + visit project_variables_path(@project) + click_on "Add a variable" + fill_in "Key", with: "SECRET_KEY" + fill_in "Value", with: "SECRET_VALUE" + click_on "Save changes" + + page.should have_content("Variables were successfully updated.") + @project.variables.count.should == 1 + end + + end +end diff --git a/spec/helpers/ci/application_helper_spec.rb b/spec/helpers/ci/application_helper_spec.rb new file mode 100644 index 00000000000..478c0266770 --- /dev/null +++ b/spec/helpers/ci/application_helper_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Ci::ApplicationHelper do + describe "#duration_in_words" do + it "returns minutes and seconds" do + intervals_in_words = { + 100 => "1 minute 40 seconds", + 121 => "2 minutes 1 second", + 3721 => "62 minutes 1 second", + 0 => "0 seconds" + } + + intervals_in_words.each do |interval, expectation| + duration_in_words(Time.now + interval, Time.now).should == expectation + end + end + + it "calculates interval from now if there is no finished_at" do + duration_in_words(nil, Time.now - 5).should == "5 seconds" + end + end + + describe "#time_interval_in_words" do + it "returns minutes and seconds" do + intervals_in_words = { + 100 => "1 minute 40 seconds", + 121 => "2 minutes 1 second", + 3721 => "62 minutes 1 second", + 0 => "0 seconds" + } + + intervals_in_words.each do |interval, expectation| + time_interval_in_words(interval).should == expectation + end + end + end +end diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb new file mode 100644 index 00000000000..e7681df10bd --- /dev/null +++ b/spec/helpers/ci/runners_helper_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Ci::RunnersHelper do + it "returns - not contacted yet" do + runner = FactoryGirl.build :runner + runner_status_icon(runner).should include("not connected yet") + end + + it "returns offline text" do + runner = FactoryGirl.build(:runner, contacted_at: 1.day.ago, active: true) + runner_status_icon(runner).should include("Runner is offline") + end + + it "returns online text" do + runner = FactoryGirl.build(:runner, contacted_at: 1.hour.ago, active: true) + runner_status_icon(runner).should include("Runner is online") + end +end diff --git a/spec/helpers/ci/user_helper_spec.rb b/spec/helpers/ci/user_helper_spec.rb new file mode 100644 index 00000000000..f95bfb355ed --- /dev/null +++ b/spec/helpers/ci/user_helper_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Ci::UserHelper do + describe :user_avatar_url do + let (:user) { User.new({'avatar_url' => avatar_url}) } + + context 'no avatar' do + let (:avatar_url) { nil } + + it 'should return a generic avatar' do + user_avatar_url(user).should == 'ci/no_avatar.png' + end + end + + context 'plain gravatar' do + let (:base_url) { 'http://www.gravatar.com/avatar/abcdefgh' } + let (:avatar_url) { "#{base_url}?s=40&d=mm" } + + it 'should return gravatar with default size' do + user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" + end + + it 'should return gravatar with custom size' do + user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" + end + end + + context 'secure gravatar' do + let (:base_url) { 'https://secure.gravatar.com/avatar/abcdefgh' } + let (:avatar_url) { "#{base_url}?s=40&d=mm" } + + it 'should return gravatar with default size' do + user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" + end + + it 'should return gravatar with custom size' do + user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" + end + end + + context 'custom avatar' do + let (:avatar_url) { 'http://example.local/avatar.png' } + + it 'should return custom avatar' do + user_avatar_url(user).should == avatar_url + end + end + end +end diff --git a/spec/helpers/ci/user_sessions_helper_spec.rb b/spec/helpers/ci/user_sessions_helper_spec.rb new file mode 100644 index 00000000000..5f654866d99 --- /dev/null +++ b/spec/helpers/ci/user_sessions_helper_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Ci::UserSessionsHelper do + describe :generate_oauth_hmac do + let (:salt) { 'a' } + let (:salt2) { 'b' } + let (:return_to) { 'b' } + + it 'should return null if return_to is also null' do + generate_oauth_hmac(salt, nil).should be_nil + end + + it 'should return not null if return_to is also not null' do + generate_oauth_hmac(salt, return_to).should_not be_nil + end + + it 'should return different hmacs for different salts' do + secret1 = generate_oauth_hmac(salt, return_to) + secret2 = generate_oauth_hmac(salt2, return_to) + secret1.should_not eq(secret2) + end + end + + describe :generate_oauth_state do + let (:return_to) { 'b' } + + it 'should return null if return_to is also null' do + generate_oauth_state(nil).should be_nil + end + + it 'should return two different states for same return_to' do + state1 = generate_oauth_state(return_to) + state2 = generate_oauth_state(return_to) + state1.should_not eq(state2) + end + end + + describe :get_ouath_state_return_to do + let (:return_to) { 'a' } + let (:state) { generate_oauth_state(return_to) } + + it 'should return return_to' do + get_ouath_state_return_to(state).should eq(return_to) + end + end + + describe :is_oauth_state_valid? do + let (:return_to) { 'a' } + let (:state) { generate_oauth_state(return_to) } + let (:forged) { "forged#{state}" } + let (:invalid) { 'aa' } + let (:invalid2) { 'aa:bb' } + let (:invalid3) { 'aa:bb:' } + + it 'should validate oauth state' do + is_oauth_state_valid?(state).should be_true + end + + it 'should not validate forged state' do + is_oauth_state_valid?(forged).should be_false + end + + it 'should not validate invalid state' do + is_oauth_state_valid?(invalid).should be_false + is_oauth_state_valid?(invalid2).should be_false + is_oauth_state_valid?(invalid3).should be_false + end + end +end diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb new file mode 100644 index 00000000000..aa60011685b --- /dev/null +++ b/spec/lib/ci/ansi2html_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe Ansi2html do + + it "prints non-ansi as-is" do + Ansi2html::convert("Hello").should == 'Hello' + end + + it "strips non-color-changing controll sequences" do + Ansi2html::convert("Hello \e[2Kworld").should == 'Hello world' + end + + it "prints simply red" do + Ansi2html::convert("\e[31mHello\e[0m").should == 'Hello' + end + + it "prints simply red without trailing reset" do + Ansi2html::convert("\e[31mHello").should == 'Hello' + end + + it "prints simply yellow" do + Ansi2html::convert("\e[33mHello\e[0m").should == 'Hello' + end + + it "prints default on blue" do + Ansi2html::convert("\e[39;44mHello").should == 'Hello' + end + + it "prints red on blue" do + Ansi2html::convert("\e[31;44mHello").should == 'Hello' + end + + it "resets colors after red on blue" do + Ansi2html::convert("\e[31;44mHello\e[0m world").should == 'Hello world' + end + + it "performs color change from red/blue to yellow/blue" do + Ansi2html::convert("\e[31;44mHello \e[33mworld").should == 'Hello world' + end + + it "performs color change from red/blue to yellow/green" do + Ansi2html::convert("\e[31;44mHello \e[33;42mworld").should == 'Hello world' + end + + it "performs color change from red/blue to reset to yellow/green" do + Ansi2html::convert("\e[31;44mHello\e[0m \e[33;42mworld").should == 'Hello world' + end + + it "ignores unsupported codes" do + Ansi2html::convert("\e[51mHello\e[0m").should == 'Hello' + end + + it "prints light red" do + Ansi2html::convert("\e[91mHello\e[0m").should == 'Hello' + end + + it "prints default on light red" do + Ansi2html::convert("\e[101mHello\e[0m").should == 'Hello' + end + + it "performs color change from red/blue to default/blue" do + Ansi2html::convert("\e[31;44mHello \e[39mworld").should == 'Hello world' + end + + it "performs color change from light red/blue to default/blue" do + Ansi2html::convert("\e[91;44mHello \e[39mworld").should == 'Hello world' + end + + it "prints bold text" do + Ansi2html::convert("\e[1mHello").should == 'Hello' + end + + it "resets bold text" do + Ansi2html::convert("\e[1mHello\e[21m world").should == 'Hello world' + Ansi2html::convert("\e[1mHello\e[22m world").should == 'Hello world' + end + + it "prints italic text" do + Ansi2html::convert("\e[3mHello").should == 'Hello' + end + + it "resets italic text" do + Ansi2html::convert("\e[3mHello\e[23m world").should == 'Hello world' + end + + it "prints underlined text" do + Ansi2html::convert("\e[4mHello").should == 'Hello' + end + + it "resets underlined text" do + Ansi2html::convert("\e[4mHello\e[24m world").should == 'Hello world' + end + + it "prints concealed text" do + Ansi2html::convert("\e[8mHello").should == 'Hello' + end + + it "resets concealed text" do + Ansi2html::convert("\e[8mHello\e[28m world").should == 'Hello world' + end + + it "prints crossed-out text" do + Ansi2html::convert("\e[9mHello").should == 'Hello' + end + + it "resets crossed-out text" do + Ansi2html::convert("\e[9mHello\e[29m world").should == 'Hello world' + end + + it "can print 256 xterm fg colors" do + Ansi2html::convert("\e[38;5;16mHello").should == 'Hello' + end + + it "can print 256 xterm fg colors on normal magenta background" do + Ansi2html::convert("\e[38;5;16;45mHello").should == 'Hello' + end + + it "can print 256 xterm bg colors" do + Ansi2html::convert("\e[48;5;240mHello").should == 'Hello' + end + + it "can print 256 xterm bg colors on normal magenta foreground" do + Ansi2html::convert("\e[48;5;16;35mHello").should == 'Hello' + end + + it "prints bold colored text vividly" do + Ansi2html::convert("\e[1;31mHello\e[0m").should == 'Hello' + end + + it "prints bold light colored text correctly" do + Ansi2html::convert("\e[1;91mHello\e[0m").should == 'Hello' + end +end diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb new file mode 100644 index 00000000000..236cfc2a1f6 --- /dev/null +++ b/spec/lib/ci/charts_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe "Charts" do + + context "build_times" do + before do + @project = FactoryGirl.create(:project) + @commit = FactoryGirl.create(:commit, project: @project) + FactoryGirl.create(:build, commit: @commit) + end + + it 'should return build times in minutes' do + chart = Charts::BuildTime.new(@project) + chart.build_times.should == [2] + end + end +end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb new file mode 100644 index 00000000000..ed3d4e84054 --- /dev/null +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -0,0 +1,311 @@ +require 'spec_helper' + +describe GitlabCiYamlProcessor do + + describe "#builds_for_ref" do + let (:type) { 'test' } + + it "returns builds if no branch specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 + config_processor.builds_for_stage_and_ref(type, "master").first.should == { + stage: "test", + except: nil, + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: {}, + allow_failure: false + } + end + + it "does not return builds if only has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["deploy"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 + end + + it "does not return builds if only has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["/^deploy$/"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 + end + + it "returns builds if only has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", only: ["master"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 + end + + it "does not build tags" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", except: ["tags"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "0-1", true).size.should == 0 + end + + it "returns builds if only has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: {script: "rspec", type: type, only: ["master", "deploy"]} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 + end + + it "returns build only for specified type" do + + config = YAML.dump({ + before_script: ["pwd"], + build: {script: "build", type: "build", only: ["master", "deploy"]}, + rspec: {script: "rspec", type: type, only: ["master", "deploy"]}, + staging: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, + production: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("production", "deploy").size.should == 0 + config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 + config_processor.builds_for_stage_and_ref("deploy", "deploy").size.should == 2 + end + end + + describe "Image and service handling" do + it "returns image and service when defined" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 + config_processor.builds_for_stage_and_ref("test", "master").first.should == { + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.1", + services: ["mysql"] + }, + allow_failure: false + } + end + + it "returns image and service when overridden for job" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: {image: "ruby:2.5", services: ["postgresql"], script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 + config_processor.builds_for_stage_and_ref("test", "master").first.should == { + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.5", + services: ["postgresql"] + }, + allow_failure: false + } + end + end + + describe "Variables" do + it "returns variables when defined" do + variables = { + var1: "value1", + var2: "value2", + } + config = YAML.dump({ + variables: variables, + before_script: ["pwd"], + rspec: {script: "rspec"} + }) + + config_processor = GitlabCiYamlProcessor.new(config) + config_processor.variables.should == variables + end + end + + describe "Error handling" do + it "indicates that object is invalid" do + expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) + end + + it "returns errors if tags parameter is invalid" do + config = YAML.dump({rspec: {script: "test", tags: "mysql"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") + end + + it "returns errors if before_script parameter is invalid" do + config = YAML.dump({before_script: "bundle update", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") + end + + it "returns errors if image parameter is invalid" do + config = YAML.dump({image: ["test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") + end + + it "returns errors if job image parameter is invalid" do + config = YAML.dump({rspec: {script: "test", image: ["test"]}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") + end + + it "returns errors if services parameter is not an array" do + config = YAML.dump({services: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if services parameter is not an array of strings" do + config = YAML.dump({services: [10, "test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if job services parameter is not an array" do + config = YAML.dump({rspec: {script: "test", services: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if job services parameter is not an array of strings" do + config = YAML.dump({rspec: {script: "test", services: [10, "test"]}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if there are unknown parameters" do + config = YAML.dump({extra: "bundle update"}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do + config = YAML.dump({extra: {services: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there is no any jobs defined" do + config = YAML.dump({before_script: ["bundle update"]}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") + end + + it "returns errors if job allow_failure parameter is not an boolean" do + config = YAML.dump({rspec: {script: "test", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") + end + + it "returns errors if job stage is not a string" do + config = YAML.dump({rspec: {script: "test", type: 1, allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a pre-defined stage" do + config = YAML.dump({rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a defined stage" do + config = YAML.dump({types: ["build", "test"], rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") + end + + it "returns errors if stages is not an array" do + config = YAML.dump({types: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if stages is not an array of strings" do + config = YAML.dump({types: [true, "test"], rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if variables is not a map" do + config = YAML.dump({variables: "test", rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end + + it "returns errors if variables is not a map of key-valued strings" do + config = YAML.dump({variables: {test: false}, rspec: {script: "test"}}) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end + end +end diff --git a/spec/lib/ci/upgrader_spec.rb b/spec/lib/ci/upgrader_spec.rb new file mode 100644 index 00000000000..40a98307ad2 --- /dev/null +++ b/spec/lib/ci/upgrader_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Upgrader do + let(:upgrader) { Upgrader.new } + let(:current_version) { GitlabCi::VERSION } + + describe 'current_version_raw' do + it { upgrader.current_version_raw.should == current_version } + end + + describe 'latest_version?' do + it 'should be true if newest version' do + upgrader.stub(latest_version_raw: current_version) + upgrader.latest_version?.should be_true + end + end + + describe 'latest_version_raw' do + it 'should be latest version for GitlabCI 3' do + allow(upgrader).to receive(:current_version_raw).and_return('3.0.0') + expect(upgrader.latest_version_raw).to eq('v3.2.0') + end + + it 'should get the latest version from tags' do + allow(upgrader).to receive(:fetch_git_tags).and_return([ + '1b5bee25b51724214c7a3307ef94027ab93ec982 refs/tags/v7.8.1', + '424cb42e35947fa304ef83eb211ffc657e31aef3 refs/tags/v7.8.1^{}', + '498e5ba63be1bb99e30c6e720902d864aac4413c refs/tags/v7.9.0.rc1', + '96aaf45ae93bd43e8b3f5d4d353d64d3cbe1e63b refs/tags/v7.9.0.rc1^{}', + '94aaf45ae93bd43e8b3fad4a353d64d3cbe1e62b refs/tags/v7.1.0', + '96aaf45ae93ba13e8b3f5d4d353d64d3cbe1e251 refs/tags/v7.1.0^{}', + '29359d64442bf54b4ca1d8b439fd9e5f9cd83252 refs/tags/v7.10.0', + '4d9213a6378bff43a69ae099702fb81e29335e7a refs/tags/v7.10.0^{}', + '1d93e1626bda93622ca7a2ae2825e2e94dabf3c6 refs/tags/v7.12.0', + '0188a9d1c2efdc52bfad36ad303686be997de713 refs/tags/v7.12.0^{}']) + expect(upgrader.latest_version_raw).to eq("v7.12.0") + end + end +end diff --git a/spec/mailers/ci/notify_spec.rb b/spec/mailers/ci/notify_spec.rb new file mode 100644 index 00000000000..6a2c845cd0e --- /dev/null +++ b/spec/mailers/ci/notify_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Notify do + include EmailSpec::Helpers + include EmailSpec::Matchers + + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe 'build success' do + subject { Notify.build_success_email(@build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build success for/ + end + + it 'contains name of project' do + should have_body_text /build successful/ + end + end + + describe 'build fail' do + subject { Notify.build_fail_email(@build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build failed for/ + end + + it 'contains name of project' do + should have_body_text /build failed/ + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb new file mode 100644 index 00000000000..d1e58438f7b --- /dev/null +++ b/spec/models/ci/build_spec.rb @@ -0,0 +1,350 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# + +require 'spec_helper' + +describe Ci::Build do + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit } + + it { should belong_to(:commit) } + it { should validate_presence_of :status } + + it { should respond_to :success? } + it { should respond_to :failed? } + it { should respond_to :running? } + it { should respond_to :pending? } + it { should respond_to :trace_html } + + describe :first_pending do + let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday } + let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' } + before { first; second } + subject { Ci::Build.first_pending } + + it { should be_a(Ci::Build) } + it('returns with the first pending build') { should eq(first) } + end + + describe :create_from do + before do + build.status = 'success' + build.save + end + let(:create_from_build) { Ci::Build.create_from build } + + it ('there should be a pending task') do + expect(Ci::Build.pending.count(:all)).to eq 0 + create_from_build + expect(Ci::Build.pending.count(:all)).to be > 0 + end + end + + describe :started? do + subject { build.started? } + + context 'without started_at' do + before { build.started_at = nil } + + it { should be_falsey } + end + + %w(running success failed).each do |status| + context "if build status is #{status}" do + before { build.status = status } + + it { should be_truthy } + end + end + + %w(pending canceled).each do |status| + context "if build status is #{status}" do + before { build.status = status } + + it { should be_falsey } + end + end + end + + describe :active? do + subject { build.active? } + + %w(pending running).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_truthy } + end + end + + %w(success failed canceled).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_falsey } + end + end + end + + describe :complete? do + subject { build.complete? } + + %w(success failed canceled).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_truthy } + end + end + + %w(pending running).each do |state| + context "if build.status is #{state}" do + before { build.status = state } + + it { should be_falsey } + end + end + end + + describe :ignored? do + subject { build.ignored? } + + context 'if build is not allowed to fail' do + before { build.allow_failure = false } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { should be_falsey } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { should be_falsey } + end + end + + context 'if build is allowed to fail' do + before { build.allow_failure = true } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { should be_falsey } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { should be_truthy } + end + end + end + + describe :trace do + subject { build.trace_html } + + it { should be_empty } + + context 'if build.trace contains text' do + let(:text) { 'example output' } + before { build.trace = text } + + it { should include(text) } + it { should have_at_least(text.length).items } + end + end + + describe :timeout do + subject { build.timeout } + + it { should eq(commit.project.timeout) } + end + + describe :duration do + subject { build.duration } + + it { should eq(120.0) } + + context 'if the building process has not started yet' do + before do + build.started_at = nil + build.finished_at = nil + end + + it { should be_nil } + end + + context 'if the building process has started' do + before do + build.started_at = Time.now - 1.minute + build.finished_at = nil + end + + it { should be_a(Float) } + it { should > 0.0 } + end + end + + describe :options do + let(:options) { + { + :image => "ruby:2.1", + :services => [ + "postgres" + ] + } + } + + subject { build.options } + it { should eq(options) } + end + + describe :ref do + subject { build.ref } + + it { should eq(commit.ref) } + end + + describe :sha do + subject { build.sha } + + it { should eq(commit.sha) } + end + + describe :short_sha do + subject { build.short_sha } + + it { should eq(commit.short_sha) } + end + + describe :before_sha do + subject { build.before_sha } + + it { should eq(commit.before_sha) } + end + + describe :allow_git_fetch do + subject { build.allow_git_fetch } + + it { should eq(project.allow_git_fetch) } + end + + describe :project do + subject { build.project } + + it { should eq(commit.project) } + end + + describe :project_id do + subject { build.project_id } + + it { should eq(commit.project_id) } + end + + describe :project_name do + subject { build.project_name } + + it { should eq(project.name) } + end + + describe :repo_url do + subject { build.repo_url } + + it { should eq(project.repo_url_with_auth) } + end + + describe :extract_coverage do + context 'valid content & regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { should eq(98.29) } + end + + context 'valid content & bad regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } + + it { should be_nil } + end + + context 'no coverage content & regex' do + subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } + + it { should be_nil } + end + + context 'multiple results in content & regex' do + subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { should eq(98.29) } + end + end + + describe :variables do + context 'returns variables' do + subject { build.variables } + + let(:variables) { + [ + {key: :DB_NAME, value: 'postgres', public: true} + ] + } + + it { should eq(variables) } + + context 'and secure variables' do + let(:secure_variables) { + [ + {key: 'SECRET_KEY', value: 'secret_value', public: false} + ] + } + + before do + build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') + end + + it { should eq(variables + secure_variables) } + + context 'and trigger variables' do + let(:trigger) { FactoryGirl.create :ci_trigger, project: project } + let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger } + let(:trigger_variables) { + [ + {key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false} + ] + } + + before do + build.trigger_request = trigger_request + end + + it { should eq(variables + secure_variables + trigger_variables) } + end + end + end + end +end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb new file mode 100644 index 00000000000..6d5b0597e13 --- /dev/null +++ b/spec/models/ci/commit_spec.rb @@ -0,0 +1,264 @@ +# == Schema Information +# +# Table name: commits +# +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# + +require 'spec_helper' + +describe Ci::Commit do + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit_with_project) { FactoryGirl.create :ci_commit, project: project } + let(:config_processor) { GitlabCiYamlProcessor.new(gitlab_ci_yaml) } + + it { should belong_to(:project) } + it { should have_many(:builds) } + it { should validate_presence_of :before_sha } + it { should validate_presence_of :sha } + it { should validate_presence_of :ref } + it { should validate_presence_of :push_data } + + it { should respond_to :git_author_name } + it { should respond_to :git_author_email } + it { should respond_to :short_sha } + + describe :last_build do + subject { commit.last_build } + before do + @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday + @second = FactoryGirl.create :ci_build, commit: commit + end + + it { should be_a(Ci::Build) } + it('returns with the most recently created build') { should eq(@second) } + end + + describe :retry do + before do + @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday + @second = FactoryGirl.create :ci_build, commit: commit + end + + it "creates new build" do + expect(commit.builds.count(:all)).to eq 2 + commit.retry + expect(commit.builds.count(:all)).to eq 3 + end + end + + describe :project_recipients do + + context 'always sending notification' do + it 'should return commit_pusher_email as only recipient when no additional recipients are given' do + project = FactoryGirl.create :ci_project, + email_add_pusher: true, + email_recipients: '' + commit = FactoryGirl.create :ci_commit, project: project + expected = 'commit_pusher_email' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == [expected] + end + + it 'should return commit_pusher_email and additional recipients' do + project = FactoryGirl.create :ci_project, + email_add_pusher: true, + email_recipients: 'rec1 rec2' + commit = FactoryGirl.create :ci_commit, project: project + expected = 'commit_pusher_email' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == ['rec1', 'rec2', expected] + end + + it 'should return recipients' do + project = FactoryGirl.create :ci_project, + email_add_pusher: false, + email_recipients: 'rec1 rec2' + commit = FactoryGirl.create :ci_commit, project: project + commit.project_recipients.should == ['rec1', 'rec2'] + end + + it 'should return unique recipients only' do + project = FactoryGirl.create :ci_project, + email_add_pusher: true, + email_recipients: 'rec1 rec1 rec2' + commit = FactoryGirl.create :ci_commit, project: project + expected = 'rec2' + commit.stub(:push_data) { { user_email: expected } } + commit.project_recipients.should == ['rec1', 'rec2'] + end + end + end + + describe :valid_commit_sha do + context 'commit.sha can not start with 00000000' do + before do + commit.sha = '0' * 40 + commit.valid_commit_sha + end + + it('commit errors should not be empty') { commit.errors.should_not be_empty } + end + end + + describe :compare? do + subject { commit_with_project.compare? } + + context 'if commit.before_sha are not nil' do + it { should be_true } + end + end + + describe :short_sha do + subject { commit.short_before_sha } + + it { should have(8).items } + it { commit.before_sha.should start_with(subject) } + end + + describe :short_sha do + subject { commit.short_sha } + + it { should have(8).items } + it { commit.sha.should start_with(subject) } + end + + describe :create_next_builds do + before do + commit.stub(:config_processor).and_return(config_processor) + end + + it "creates builds for next type" do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_next_builds(nil).should be_true + commit.builds.reload + commit.builds.size.should == 4 + + commit.create_next_builds(nil).should be_true + commit.builds.reload + commit.builds.size.should == 5 + + commit.create_next_builds(nil).should be_false + end + end + + describe :create_builds do + before do + commit.stub(:config_processor).and_return(config_processor) + end + + it 'creates builds' do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + end + + context 'for build triggers' do + let(:trigger) { FactoryGirl.create :ci_trigger, project: project } + let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger } + + it 'creates builds' do + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + end + + it 'rebuilds commit' do + commit.create_builds.should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 4 + end + + it 'creates next builds' do + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + + commit.create_next_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 4 + end + + context 'for [ci skip]' do + before do + commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]' + commit.save + end + + it 'rebuilds commit' do + commit.status.should == 'skipped' + commit.create_builds(trigger_request).should be_true + commit.builds.reload + commit.builds.size.should == 2 + commit.status.should == 'pending' + end + end + end + end + + describe "#finished_at" do + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + + it "returns finished_at of latest build" do + build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60 + build1 = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120 + + commit.finished_at.to_i.should == build.finished_at.to_i + end + + it "returns nil if there is no finished build" do + build = FactoryGirl.create :ci_not_started_build, commit: commit + + commit.finished_at.should be_nil + end + end + + describe "coverage" do + let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + + it "calculates average when there are two builds with coverage" do + FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there are two builds with coverage and one with nil" do + FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit + FactoryGirl.create :ci_build, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there are two builds with coverage and one is retried" do + FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit + FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, commit: commit + FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit + commit.coverage.should == "35.00" + end + + it "calculates average when there is one build without coverage" do + FactoryGirl.create :ci_build, commit: commit + commit.coverage.should be_nil + end + end +end diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb new file mode 100644 index 00000000000..4830d98bdf8 --- /dev/null +++ b/spec/models/ci/mail_service_spec.rb @@ -0,0 +1,184 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe Ci::MailService do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + context "active" do + before do + subject.active = true + end + end + end + + describe 'Sends email for' do + let(:mail) { Ci::MailService.new } + + describe 'failed build' do + let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_fail_email).with(build.id, email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + end + end + + describe 'successfull build' do + let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successfull build and project has email_recipients' do + let(:project) { + FactoryGirl.create(:ci_project, + email_add_pusher: true, + email_only_broken_builds: false, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email("git@example.com") + should_email("jeroen@example.com") + mail.execute(build) + end + + def should_email(email) + Notify.should_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successful build and notify only broken builds' do + let(:project) { + FactoryGirl.create(:ci_project, + email_add_pusher: true, + email_only_broken_builds: true, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + should_email(commit.git_author_email) + should_email("jeroen@example.com") + mail.execute(build) if mail.can_execute?(build) + end + + def should_email(email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + + describe 'successful build and can test service' do + let(:project) { + FactoryGirl.create(:ci_project, + email_add_pusher: true, + email_only_broken_builds: false, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + + before do + mail.stub( + project: project + ) + build + end + + it do + mail.can_test?.should == true + end + end + + describe 'retried build should not receive email' do + let(:project) { + FactoryGirl.create(:ci_project, + email_add_pusher: true, + email_only_broken_builds: true, + email_recipients: "jeroen@example.com") + } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + + before do + mail.stub( + project: project + ) + end + + it do + Build.retry(build) + should_email(commit.git_author_email) + should_email("jeroen@example.com") + mail.execute(build) if mail.can_execute?(build) + end + + def should_email(email) + Notify.should_not_receive(:build_success_email).with(build.id, email) + Notify.should_not_receive(:build_fail_email).with(build.id, email) + end + end + end +end diff --git a/spec/models/ci/network_spec.rb b/spec/models/ci/network_spec.rb new file mode 100644 index 00000000000..b80adba5b08 --- /dev/null +++ b/spec/models/ci/network_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Network do + let(:network) { Network.new } + + describe :enable_ci do + subject { network.enable_ci '', '', '' } + + context 'on success' do + before do + response = double + response.stub(:code) { 200 } + network.class.stub(:put) { response } + end + + it { should be_true } + end + + context 'on failure' do + before do + response = double + response.stub(:code) { 404 } + network.class.stub(:put) { response } + end + + it { should be_nil } + end + end + + describe :disable_ci do + let(:response) { double } + subject { network.disable_ci '', '' } + + context 'on success' do + let(:parsed_response) { 'parsed' } + before do + response.stub(:code) { 200 } + response.stub(:parsed_response) { parsed_response } + network.class.stub(:delete) { response } + end + + it { should equal(parsed_response) } + end + + context 'on failure' do + before do + response.stub(:code) { 404 } + network.class.stub(:delete) { response } + end + + it { should be_nil } + end + end +end diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb new file mode 100644 index 00000000000..3571cb94793 --- /dev/null +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe Ci::HipChatMessage do + subject { HipChatMessage.new(build) } + + let(:project) { FactoryGirl.create(:project) } + + context "One build" do + let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + + let(:build) do + commit.create_builds + commit.builds.first + end + + context 'when build succeeds' do + it 'returns a successful message' do + build.update(status: "success") + + expect( subject.status_color ).to eq 'green' + expect( subject.notify? ).to be_false + expect( subject.to_s ).to match(/Build '[^']+' #\d+/) + expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) + end + end + + context 'when build fails' do + it 'returns a failure message' do + build.update(status: "failed") + + expect( subject.status_color ).to eq 'red' + expect( subject.notify? ).to be_true + expect( subject.to_s ).to match(/Build '[^']+' #\d+/) + expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) + end + end + end + + context "Several builds" do + let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + + let(:build) do + commit.builds.first + end + + context 'when all matrix builds succeed' do + it 'returns a successful message' do + commit.create_builds + commit.builds.update_all(status: "success") + commit.reload + + expect( subject.status_color ).to eq 'green' + expect( subject.notify? ).to be_false + expect( subject.to_s ).to match(/Commit #\d+/) + expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) + end + end + + context 'when at least one matrix build fails' do + it 'returns a failure message' do + commit.create_builds + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + expect( subject.status_color ).to eq 'red' + expect( subject.notify? ).to be_true + expect( subject.to_s ).to match(/Commit #\d+/) + expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) + end + end + end +end diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb new file mode 100644 index 00000000000..71dba8fc358 --- /dev/null +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -0,0 +1,75 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + + +require 'spec_helper' + +describe Ci::HipChatService do + + describe "Validations" do + + context "active" do + before do + subject.active = true + end + + it { should validate_presence_of :hipchat_room } + it { should validate_presence_of :hipchat_token } + + end + end + + describe "Execute" do + + let(:service) { HipChatService.new } + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } + + before do + service.stub( + project: project, + project_id: project.id, + notify_only_broken_builds: false, + hipchat_room: 123, + hipchat_token: 'a1b2c3d4e5f6' + ) + + WebMock.stub_request(:post, api_url) + end + + + it "should call the HipChat API" do + service.execute(build) + HipChatNotifierWorker.drain + + expect( WebMock ).to have_requested(:post, api_url).once + end + + it "calls the worker with expected arguments" do + expect( HipChatNotifierWorker ).to receive(:perform_async) \ + .with(an_instance_of(String), hash_including( + token: 'a1b2c3d4e5f6', + room: 123, + server: 'https://api.hipchat.com', + color: 'red', + notify: true + )) + + service.execute(build) + end + end +end + diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb new file mode 100644 index 00000000000..4a7284fe460 --- /dev/null +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' + +describe Ci::SlackMessage do + subject { SlackMessage.new(commit) } + + let(:project) { FactoryGirl.create :project } + + context "One build" do + let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + + let(:build) do + commit.create_builds + commit.builds.first + end + + context 'when build succeeded' do + let(:color) { 'good' } + + it 'returns a message with succeeded build' do + build.update(status: "success") + + subject.color.should == color + subject.fallback.should include('Build') + subject.fallback.should include("\##{build.id}") + subject.fallback.should include('succeeded') + subject.attachments.first[:fields].should be_empty + end + end + + context 'when build failed' do + let(:color) { 'danger' } + + it 'returns a message with failed build' do + build.update(status: "failed") + + subject.color.should == color + subject.fallback.should include('Build') + subject.fallback.should include("\##{build.id}") + subject.fallback.should include('failed') + subject.attachments.first[:fields].should be_empty + end + end + end + + context "Several builds" do + let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + + context 'when all matrix builds succeeded' do + let(:color) { 'good' } + + it 'returns a message with success' do + commit.create_builds + commit.builds.update_all(status: "success") + commit.reload + + subject.color.should == color + subject.fallback.should include('Commit') + subject.fallback.should include("\##{commit.id}") + subject.fallback.should include('succeeded') + subject.attachments.first[:fields].should be_empty + end + end + + context 'when one of matrix builds failed' do + let(:color) { 'danger' } + + it 'returns a message with information about failed build' do + commit.create_builds + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + subject.color.should == color + subject.fallback.should include('Commit') + subject.fallback.should include("\##{commit.id}") + subject.fallback.should include('failed') + subject.attachments.first[:fields].size.should == 1 + subject.attachments.first[:fields].first[:title].should == second_build.name + subject.attachments.first[:fields].first[:value].should include("\##{second_build.id}") + end + end + end +end diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb new file mode 100644 index 00000000000..952349a9def --- /dev/null +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -0,0 +1,58 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe Ci::SlackService do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + context "active" do + before do + subject.active = true + end + + it { should validate_presence_of :webhook } + end + end + + describe "Execute" do + let(:slack) { SlackService.new } + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } + let(:notify_only_broken_builds) { false } + + before do + slack.stub( + project: project, + project_id: project.id, + webhook: webhook_url, + notify_only_broken_builds: notify_only_broken_builds + ) + + WebMock.stub_request(:post, webhook_url) + end + + it "should call Slack API" do + slack.execute(build) + SlackNotifierWorker.drain + + WebMock.should have_requested(:post, webhook_url).once + end + end +end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb new file mode 100644 index 00000000000..aa76b99154b --- /dev/null +++ b/spec/models/ci/project_spec.rb @@ -0,0 +1,185 @@ +# == Schema Information +# +# Table name: projects +# +# id :integer not null, primary key +# name :string(255) not null +# timeout :integer default(3600), not null +# created_at :datetime +# updated_at :datetime +# token :string(255) +# default_ref :string(255) +# path :string(255) +# always_build :boolean default(FALSE), not null +# polling_interval :integer +# public :boolean default(FALSE), not null +# ssh_url_to_repo :string(255) +# gitlab_id :integer +# allow_git_fetch :boolean default(TRUE), not null +# email_recipients :string(255) default(""), not null +# email_add_pusher :boolean default(TRUE), not null +# email_only_broken_builds :boolean default(TRUE), not null +# skip_refs :string(255) +# coverage_regex :string(255) +# shared_runners_enabled :boolean default(FALSE) +# generated_yaml_config :text +# + +require 'spec_helper' + +describe Project do + subject { FactoryGirl.build :project } + + it { should have_many(:commits) } + + it { should validate_presence_of :name } + it { should validate_presence_of :timeout } + it { should validate_presence_of :default_ref } + + describe 'before_validation' do + it 'should set an random token if none provided' do + project = FactoryGirl.create :project_without_token + project.token.should_not == "" + end + + it 'should not set an random toke if one provided' do + project = FactoryGirl.create :project + project.token.should == "iPWx6WM4lhHNedGfBpPJNP" + end + end + + describe "ordered_by_last_commit_date" do + it "returns ordered projects" do + newest_project = FactoryGirl.create :project + oldest_project = FactoryGirl.create :project + project_without_commits = FactoryGirl.create :project + + FactoryGirl.create :commit, committed_at: 1.hour.ago, project: newest_project + FactoryGirl.create :commit, committed_at: 2.hour.ago, project: oldest_project + + Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits] + end + end + + context :valid_project do + let(:project) { FactoryGirl.create :project } + + context :project_with_commit_and_builds do + before do + commit = FactoryGirl.create(:commit, project: project) + FactoryGirl.create(:build, commit: commit) + end + + it { project.status.should == 'pending' } + it { project.last_commit.should be_kind_of(Commit) } + it { project.human_status.should == 'pending' } + end + end + + describe '#email_notification?' do + it do + project = FactoryGirl.create :project, email_add_pusher: true + project.email_notification?.should == true + end + + it do + project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: 'test tesft' + project.email_notification?.should == true + end + + it do + project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: '' + project.email_notification?.should == false + end + end + + describe '#broken_or_success?' do + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(true) + project.stub(:success?).and_return(true) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(true) + project.stub(:success?).and_return(false) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(false) + project.stub(:success?).and_return(true) + project.broken_or_success?.should == true + } + + it { + project = FactoryGirl.create :project, email_add_pusher: true + project.stub(:broken?).and_return(false) + project.stub(:success?).and_return(false) + project.broken_or_success?.should == false + } + end + + describe 'Project.parse' do + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + let(:parsed_project) { Project.parse(project_dump) } + + + it { parsed_project.should be_valid } + it { parsed_project.should be_kind_of(Project) } + it { parsed_project.name.should eq("GitLab / api.gitlab.org") } + it { parsed_project.gitlab_id.should eq(189) } + it { parsed_project.gitlab_url.should eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } + + it "parses plain hash" do + Project.parse(project_dump).name.should eq("GitLab / api.gitlab.org") + end + end + + describe :repo_url_with_auth do + let(:project) { FactoryGirl.create :project } + subject { project.repo_url_with_auth } + + it { should be_a(String) } + it { should end_with(".git") } + it { should start_with(project.gitlab_url[0..6]) } + it { should include(project.token) } + it { should include('gitlab-ci-token') } + it { should include(project.gitlab_url[7..-1]) } + end + + describe :search do + let!(:project) { FactoryGirl.create(:project, name: "foo") } + + it { Project.search('fo').should include(project) } + it { Project.search('bar').should be_empty } + end + + describe :any_runners do + it "there are no runners available" do + project = FactoryGirl.create(:project) + project.any_runners?.should be_false + end + + it "there is a specific runner" do + project = FactoryGirl.create(:project) + project.runners << FactoryGirl.create(:specific_runner) + project.any_runners?.should be_true + end + + it "there is a shared runner" do + project = FactoryGirl.create(:project, shared_runners_enabled: true) + FactoryGirl.create(:shared_runner) + project.any_runners?.should be_true + end + + it "there is a shared runner, but they are prohibited to use" do + project = FactoryGirl.create(:project) + FactoryGirl.create(:shared_runner) + project.any_runners?.should be_false + end + end +end diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb new file mode 100644 index 00000000000..0218d484130 --- /dev/null +++ b/spec/models/ci/runner_project_spec.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: runner_projects +# +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +require 'spec_helper' + +describe Ci::RunnerProject do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb new file mode 100644 index 00000000000..8677d86aa02 --- /dev/null +++ b/spec/models/ci/runner_spec.rb @@ -0,0 +1,70 @@ +# == Schema Information +# +# Table name: runners +# +# id :integer not null, primary key +# token :string(255) +# created_at :datetime +# updated_at :datetime +# description :string(255) +# contacted_at :datetime +# active :boolean default(TRUE), not null +# is_shared :boolean default(FALSE) +# name :string(255) +# version :string(255) +# revision :string(255) +# platform :string(255) +# architecture :string(255) +# + +require 'spec_helper' + +describe Ci::Runner do + describe '#display_name' do + it 'should return the description if it has a value' do + runner = FactoryGirl.build(:runner, description: 'Linux/Ruby-1.9.3-p448') + expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448' + end + + it 'should return the token if it does not have a description' do + runner = FactoryGirl.create(:runner) + expect(runner.display_name).to eq runner.description + end + + it 'should return the token if the description is an empty string' do + runner = FactoryGirl.build(:runner, description: '') + expect(runner.display_name).to eq runner.token + end + end + + describe :assign_to do + let!(:project) { FactoryGirl.create :project } + let!(:shared_runner) { FactoryGirl.create(:shared_runner) } + + before { shared_runner.assign_to(project) } + + it { shared_runner.should be_specific } + it { shared_runner.projects.should == [project] } + it { shared_runner.only_for?(project).should be_true } + end + + describe "belongs_to_one_project?" do + it "returns false if there are two projects runner assigned to" do + runner = FactoryGirl.create(:specific_runner) + project = FactoryGirl.create(:project) + project1 = FactoryGirl.create(:project) + project.runners << runner + project1.runners << runner + + runner.belongs_to_one_project?.should be_false + end + + it "returns true" do + runner = FactoryGirl.create(:specific_runner) + project = FactoryGirl.create(:project) + project.runners << runner + + runner.belongs_to_one_project?.should be_true + end + end +end diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb new file mode 100644 index 00000000000..5a90229ec43 --- /dev/null +++ b/spec/models/ci/service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# + +require 'spec_helper' + +describe Ci::Service do + + describe "Associations" do + it { should belong_to :project } + end + + describe "Mass assignment" do + end + + describe "Test Button" do + before do + @service = Service.new + end + + describe "Testable" do + let (:project) { FactoryGirl.create :project } + let (:commit) { FactoryGirl.create :commit, project: project } + let (:build) { FactoryGirl.create :build, commit: commit } + + before do + @service.stub( + project: project + ) + build + @testable = @service.can_test? + end + + describe :can_test do + it { @testable.should == true } + end + end + end +end diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb new file mode 100644 index 00000000000..7c928f9d9dc --- /dev/null +++ b/spec/models/ci/trigger_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Ci::Trigger do + let(:project) { FactoryGirl.create :project } + + describe 'before_validation' do + it 'should set an random token if none provided' do + trigger = FactoryGirl.create :trigger_without_token, project: project + trigger.token.should_not be_nil + end + + it 'should not set an random token if one provided' do + trigger = FactoryGirl.create :trigger, project: project + trigger.token.should == 'token' + end + end +end diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb new file mode 100644 index 00000000000..d1b87988b74 --- /dev/null +++ b/spec/models/ci/user_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +describe Ci::User do + + describe "has_developer_access?" do + before do + @user = User.new({}) + end + + let(:project_with_owner_access) do + { + "name" => "gitlab-shell", + "permissions" => { + "project_access" => { + "access_level"=> 10, + "notification_level" => 3 + }, + "group_access" => { + "access_level" => 50, + "notification_level" => 3 + } + } + } + end + + let(:project_with_reporter_access) do + { + "name" => "gitlab-shell", + "permissions" => { + "project_access" => { + "access_level" => 20, + "notification_level" => 3 + }, + "group_access" => { + "access_level" => 10, + "notification_level" => 3 + } + } + } + end + + it "returns false for reporter" do + @user.stub(:project_info).and_return(project_with_reporter_access) + + @user.has_developer_access?(1).should be_false + end + + it "returns true for owner" do + @user.stub(:project_info).and_return(project_with_owner_access) + + @user.has_developer_access?(1).should be_true + end + end + + describe "authorized_projects" do + let (:user) { User.new({}) } + + before do + FactoryGirl.create :project, gitlab_id: 1 + FactoryGirl.create :project, gitlab_id: 2 + gitlab_project = OpenStruct.new({id: 1}) + gitlab_project1 = OpenStruct.new({id: 2}) + User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + end + + it "returns projects" do + User.any_instance.stub(:can_manage_project?).and_return(true) + + user.authorized_projects.count.should == 2 + end + + it "empty list if user miss manage permission" do + User.any_instance.stub(:can_manage_project?).and_return(false) + + user.authorized_projects.count.should == 0 + end + end + + describe "authorized_runners" do + it "returns authorized runners" do + project = FactoryGirl.create :project, gitlab_id: 1 + project1 = FactoryGirl.create :project, gitlab_id: 2 + gitlab_project = OpenStruct.new({id: 1}) + gitlab_project1 = OpenStruct.new({id: 2}) + User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + User.any_instance.stub(:can_manage_project?).and_return(true) + user = User.new({}) + + runner = FactoryGirl.create :specific_runner + runner1 = FactoryGirl.create :specific_runner + runner2 = FactoryGirl.create :specific_runner + + project.runners << runner + project1.runners << runner1 + + user.authorized_runners.should include(runner, runner1) + user.authorized_runners.should_not include(runner2) + end + end +end diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb new file mode 100644 index 00000000000..447512bf6df --- /dev/null +++ b/spec/models/ci/variable_spec.rb @@ -0,0 +1,44 @@ +# == Schema Information +# +# Table name: variables +# +# id :integer not null, primary key +# project_id :integer not null +# key :string(255) +# value :text +# encrypted_value :text +# encrypted_value_salt :string(255) +# encrypted_value_iv :string(255) +# + +require 'spec_helper' + +describe Ci::Variable do + subject { Variable.new } + + let(:secret_value) { 'secret' } + + before :each do + subject.value = secret_value + end + + describe :value do + it 'stores the encrypted value' do + subject.encrypted_value.should_not be_nil + end + + it 'stores an iv for value' do + subject.encrypted_value_iv.should_not be_nil + end + + it 'stores a salt for value' do + subject.encrypted_value_salt.should_not be_nil + end + + it 'fails to decrypt if iv is incorrect' do + subject.encrypted_value_iv = nil + subject.instance_variable_set(:@value, nil) + expect { subject.value }.to raise_error + end + end +end diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb new file mode 100644 index 00000000000..4211576ce5e --- /dev/null +++ b/spec/models/ci/web_hook_spec.rb @@ -0,0 +1,64 @@ +# == Schema Information +# +# Table name: web_hooks +# +# id :integer not null, primary key +# url :string(255) not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +require 'spec_helper' + +describe Ci::WebHook do + describe "Associations" do + it { should belong_to :project } + end + + describe "Validations" do + it { should validate_presence_of(:url) } + + context "url format" do + it { should allow_value("http://example.com").for(:url) } + it { should allow_value("https://excample.com").for(:url) } + it { should allow_value("http://test.com/api").for(:url) } + it { should allow_value("http://test.com/api?key=abc").for(:url) } + it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } + + it { should_not allow_value("example.com").for(:url) } + it { should_not allow_value("ftp://example.com").for(:url) } + it { should_not allow_value("herp-and-derp").for(:url) } + end + end + + describe "execute" do + before(:each) do + @web_hook = FactoryGirl.create(:web_hook) + @project = @web_hook.project + @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} + + WebMock.stub_request(:post, @web_hook.url) + end + + it "POSTs to the web hook URL" do + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).once + end + + it "POSTs the data as JSON" do + json = @data.to_json + + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).with(body: json).once + end + + it "catches exceptions" do + WebHook.should_receive(:post).and_raise("Some HTTP Post error") + + lambda { + @web_hook.execute(@data) + }.should raise_error + end + end +end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb new file mode 100644 index 00000000000..7da212da83a --- /dev/null +++ b/spec/requests/ci/api/builds_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe Ci::API::API do + include ApiHelpers + + let(:runner) { FactoryGirl.create(:runner, tag_list: ["mysql", "ruby"]) } + let(:project) { FactoryGirl.create(:project) } + + describe "Builds API for runners" do + let(:shared_runner) { FactoryGirl.create(:runner, token: "SharedRunner") } + let(:shared_project) { FactoryGirl.create(:project, name: "SharedProject") } + + before do + FactoryGirl.create :runner_project, project_id: project.id, runner_id: runner.id + end + + describe "POST /builds/register" do + it "should start a build" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + build = commit.builds.first + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response['sha'].should == build.sha + runner.reload.platform.should == "darwin" + end + + it "should return 404 error if no pending build found" do + post api("/builds/register"), token: runner.token + + response.status.should == 404 + end + + it "should return 404 error if no builds for specific runner" do + commit = FactoryGirl.create(:commit, project: shared_project) + FactoryGirl.create(:build, commit: commit, status: 'pending' ) + + post api("/builds/register"), token: runner.token + + response.status.should == 404 + end + + it "should return 404 error if no builds for shared runner" do + commit = FactoryGirl.create(:commit, project: project) + FactoryGirl.create(:build, commit: commit, status: 'pending' ) + + post api("/builds/register"), token: shared_runner.token + + response.status.should == 404 + end + + it "returns options" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["options"].should == {"image" => "ruby:2.1", "services" => ["postgres"]} + end + + it "returns variables" do + commit = FactoryGirl.create(:commit, project: project) + commit.create_builds + project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["variables"].should == [ + {"key" => "DB_NAME", "value" => "postgres", "public" => true}, + {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, + ] + end + + it "returns variables for triggers" do + trigger = FactoryGirl.create(:trigger, project: project) + commit = FactoryGirl.create(:commit, project: project) + + trigger_request = FactoryGirl.create(:trigger_request_with_variables, commit: commit, trigger: trigger) + commit.create_builds(trigger_request) + project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + + post api("/builds/register"), token: runner.token, info: {platform: :darwin} + + response.status.should == 201 + json_response["variables"].should == [ + {"key" => "DB_NAME", "value" => "postgres", "public" => true}, + {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, + {"key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false}, + ] + end + end + + describe "PUT /builds/:id" do + let(:commit) { FactoryGirl.create(:commit, project: project)} + let(:build) { FactoryGirl.create(:build, commit: commit, runner_id: runner.id) } + + it "should update a running build" do + build.run! + put api("/builds/#{build.id}"), token: runner.token + response.status.should == 200 + end + + it 'Should not override trace information when no trace is given' do + build.run! + build.update!(trace: 'hello_world') + put api("/builds/#{build.id}"), token: runner.token + expect(build.reload.trace).to eq 'hello_world' + end + end + end +end diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb new file mode 100644 index 00000000000..99021dd681d --- /dev/null +++ b/spec/requests/ci/api/commits_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Ci::API::API, 'Commits' do + include ApiHelpers + + let(:project) { FactoryGirl.create(:project) } + let(:commit) { FactoryGirl.create(:commit, project: project) } + + let(:options) { + { + project_token: project.token, + project_id: project.id + } + } + + describe "GET /commits" do + before { commit } + + it "should return commits per project" do + get api("/commits"), options + + response.status.should == 200 + json_response.count.should == 1 + json_response.first["project_id"].should == project.id + json_response.first["sha"].should == commit.sha + end + end + + describe "POST /commits" do + let(:data) { + { + "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref" => "refs/heads/master", + "commits" => [ + { + "id" => "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "message" => "Update Catalan translation to e38cb41.", + "timestamp" => "2011-12-12T14:27:31+02:00", + "url" => "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "author" => { + "name" => "Jordi Mallach", + "email" => "jordi@softcatala.org", + } + } + ], + ci_yaml_file: gitlab_ci_yaml + } + } + + it "should create a build" do + post api("/commits"), options.merge(data: data) + + response.status.should == 201 + json_response['sha'].should == "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" + end + + it "should return 400 error if no data passed" do + post api("/commits"), options + + response.status.should == 400 + json_response['message'].should == "400 (Bad request) \"data\" not given" + end + end +end diff --git a/spec/requests/ci/api/forks_spec.rb b/spec/requests/ci/api/forks_spec.rb new file mode 100644 index 00000000000..74efc0c30be --- /dev/null +++ b/spec/requests/ci/api/forks_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Ci::API::API do + include ApiHelpers + + let(:project) { FactoryGirl.create(:project) } + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + + let(:options) { + { + private_token: private_token, + url: gitlab_url + } + } + + before { + stub_gitlab_calls + } + + + describe "POST /forks" do + let(:project_info) { + { + project_id: project.gitlab_id, + project_token: project.token, + data: { + id: 2, + name_with_namespace: "Gitlab.org / Underscore", + path_with_namespace: "gitlab-org/underscore", + default_branch: "master", + ssh_url_to_repo: "git@example.com:gitlab-org/underscore" + } + } + } + + context "with valid info" do + before do + options.merge!(project_info) + end + + it "should create a project with valid data" do + post api("/forks"), options + response.status.should == 201 + json_response['name'].should == "Gitlab.org / Underscore" + end + end + + context "with invalid project info" do + before do + options.merge!({}) + end + + it "should error with invalid data" do + post api("/forks"), options + response.status.should == 400 + end + end + end +end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb new file mode 100644 index 00000000000..65cfc909b48 --- /dev/null +++ b/spec/requests/ci/api/projects_spec.rb @@ -0,0 +1,251 @@ +require 'spec_helper' + +describe Ci::API::API do + include ApiHelpers + + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + + let(:options) { + { + private_token: private_token, + url: gitlab_url + } + } + + before { + stub_gitlab_calls + } + + context "requests for scoped projects" do + # NOTE: These ids are tied to the actual projects on demo.gitlab.com + describe "GET /projects" do + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "gitlab-ci", gitlab_id: 4) } + + it "should return all projects on the CI instance" do + get api("/projects"), options + response.status.should == 200 + json_response.count.should == 2 + json_response.first["id"].should == project1.id + json_response.last["id"].should == project2.id + end + end + + describe "GET /projects/owned" do + # NOTE: This user doesn't own any of these projects on demo.gitlab.com + let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:project, name: "random-project", gitlab_id: 9898) } + + it "should return all projects on the CI instance" do + get api("/projects/owned"), options + + response.status.should == 200 + json_response.count.should == 0 + end + end + end + + describe "POST /projects/:project_id/webhooks" do + let!(:project) { FactoryGirl.create(:project) } + + context "Valid Webhook URL" do + let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } + + before do + options.merge!(webhook) + end + + it "should create webhook for specified project" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 201 + json_response["url"].should == webhook[:web_hook] + end + + it "fails to create webhook for non existsing project" do + post api("/projects/non-existant-id/webhooks"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 401 + end + end + + context "Invalid Webhook URL" do + let!(:webhook) { {web_hook: "ala_ma_kota" } } + + before do + options.merge!(webhook) + end + + it "fails to create webhook for not valid url" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 400 + end + end + + context "Missed web_hook parameter" do + it "fails to create webhook for not provided url" do + post api("/projects/#{project.id}/webhooks"), options + response.status.should == 400 + end + end + end + + describe "GET /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + + context "with an existing project" do + it "should retrieve the project info" do + get api("/projects/#{project.id}"), options + response.status.should == 200 + json_response['id'].should == project.id + end + end + + context "with a non-existing project" do + it "should return 404 error if project not found" do + get api("/projects/non_existent_id"), options + response.status.should == 404 + end + end + end + + describe "PUT /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + let!(:project_info) { {name: "An updated name!" } } + + before do + options.merge!(project_info) + end + + it "should update a specific project's information" do + put api("/projects/#{project.id}"), options + response.status.should == 200 + json_response["name"].should == project_info[:name] + end + + it "fails to update a non-existing project" do + put api("/projects/non-existant-id"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + put api("/projects/#{project.id}"), options + response.status.should == 401 + end + end + + describe "DELETE /projects/:id" do + let!(:project) { FactoryGirl.create(:project) } + + it "should delete a specific project" do + delete api("/projects/#{project.id}"), options + response.status.should == 200 + + expect { project.reload }.to raise_error + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + delete api("/projects/#{project.id}"), options + response.status.should == 401 + end + + it "is getting not found error" do + delete api("/projects/not-existing_id"), options + response.status.should == 404 + end + end + + describe "POST /projects" do + let(:project_info) { + { + name: "My project", + gitlab_id: 1, + path: "testing/testing", + ssh_url_to_repo: "ssh://example.com/testing/testing.git" + } + } + + let(:invalid_project_info) { {} } + + context "with valid project info" do + before do + options.merge!(project_info) + end + + it "should create a project with valid data" do + post api("/projects"), options + response.status.should == 201 + json_response['name'].should == project_info[:name] + end + end + + context "with invalid project info" do + before do + options.merge!(invalid_project_info) + end + + it "should error with invalid data" do + post api("/projects"), options + response.status.should == 400 + end + end + + describe "POST /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:project) } + let(:runner) { FactoryGirl.create(:runner) } + + it "should add the project to the runner" do + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 201 + + project.reload + project.runners.first.id.should == runner.id + end + + it "should fail if it tries to link a non-existing project or runner" do + post api("/projects/#{project.id}/runners/non-existing"), options + response.status.should == 404 + + post api("/projects/non-existing/runners/#{runner.id}"), options + response.status.should == 404 + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 401 + end + end + + describe "DELETE /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:project) } + let(:runner) { FactoryGirl.create(:runner) } + + before do + post api("/projects/#{project.id}/runners/#{runner.id}"), options + end + + it "should remove the project from the runner" do + project.runners.should be_present + delete api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 200 + + project.reload + project.runners.should be_empty + end + + it "non-manager is not authorized" do + User.any_instance.stub(:can_manage_project?).and_return(false) + post api("/projects/#{project.id}/runners/#{runner.id}"), options + response.status.should == 401 + end + end + end +end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb new file mode 100644 index 00000000000..3faebd40bae --- /dev/null +++ b/spec/requests/ci/api/runners_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Ci::API::API do + include ApiHelpers + include StubGitlabCalls + + before { + stub_gitlab_calls + } + + describe "GET /runners" do + let(:gitlab_url) { GitlabCi.config.gitlab_server.url } + let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + let(:options) { + { + :private_token => private_token, + :url => gitlab_url + } + } + + before do + 5.times { FactoryGirl.create(:runner) } + end + + it "should retrieve a list of all runners" do + get api("/runners"), options + response.status.should == 200 + json_response.count.should == 5 + json_response.last.should have_key("id") + json_response.last.should have_key("token") + end + end + + describe "POST /runners/register" do + describe "should create a runner if token provided" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } + + it { response.status.should == 201 } + end + + describe "should create a runner with description" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } + + it { response.status.should == 201 } + it { Runner.first.description.should == "server.hostname" } + end + + describe "should create a runner with tags" do + before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } + + it { response.status.should == 201 } + it { Runner.first.tag_list.sort.should == ["tag1", "tag2"] } + end + + describe "should create a runner if project token provided" do + let(:project) { FactoryGirl.create(:project) } + before { post api("/runners/register"), token: project.token } + + it { response.status.should == 201 } + it { project.runners.size.should == 1 } + end + + it "should return 403 error if token is invalid" do + post api("/runners/register"), token: 'invalid' + + response.status.should == 403 + end + + it "should return 400 error if no token" do + post api("/runners/register") + + response.status.should == 400 + end + end + + describe "DELETE /runners/delete" do + let!(:runner) { FactoryGirl.create(:runner) } + before { delete api("/runners/delete"), token: runner.token } + + it { response.status.should == 200 } + it { Runner.count.should == 0 } + end +end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb new file mode 100644 index 00000000000..5da40a69991 --- /dev/null +++ b/spec/requests/ci/api/triggers_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe Ci::API::API do + include ApiHelpers + + describe 'POST /projects/:project_id/refs/:ref/trigger' do + let!(:trigger_token) { 'secure token' } + let!(:project) { FactoryGirl.create(:project) } + let!(:project2) { FactoryGirl.create(:project) } + let!(:trigger) { FactoryGirl.create(:trigger, project: project, token: trigger_token) } + let(:options) { + { + token: trigger_token + } + } + + context 'Handles errors' do + it 'should return bad request if token is missing' do + post api("/projects/#{project.id}/refs/master/trigger") + response.status.should == 400 + end + + it 'should return not found if project is not found' do + post api('/projects/0/refs/master/trigger'), options + response.status.should == 404 + end + + it 'should return unauthorized if token is for different project' do + post api("/projects/#{project2.id}/refs/master/trigger"), options + response.status.should == 401 + end + end + + context 'Have a commit' do + before do + @commit = FactoryGirl.create(:commit, project: project) + end + + it 'should create builds' do + post api("/projects/#{project.id}/refs/master/trigger"), options + response.status.should == 201 + @commit.builds.reload + @commit.builds.size.should == 2 + end + + it 'should return bad request with no builds created if there\'s no commit for that ref' do + post api("/projects/#{project.id}/refs/other-branch/trigger"), options + response.status.should == 400 + json_response['message'].should == 'No builds created' + end + + context 'Validates variables' do + let(:variables) { + {'TRIGGER_KEY' => 'TRIGGER_VALUE'} + } + + it 'should validate variables to be a hash' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') + response.status.should == 400 + json_response['message'].should == 'variables needs to be a hash' + end + + it 'should validate variables needs to be a map of key-valued strings' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: {key: %w(1 2)}) + response.status.should == 400 + json_response['message'].should == 'variables needs to be a map of key-valued strings' + end + + it 'create trigger request with variables' do + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) + response.status.should == 201 + @commit.builds.reload + @commit.builds.first.trigger_request.variables.should == variables + end + end + end + end +end diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb new file mode 100644 index 00000000000..73d540e372a --- /dev/null +++ b/spec/requests/ci/builds_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe "Builds" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + @build = FactoryGirl.create :build, commit: @commit + end + + describe "GET /:project/builds/:id/status.json" do + before do + get status_project_build_path(@project, @build), format: :json + end + + it { response.status.should == 200 } + it { response.body.should include(@build.sha) } + end +end diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb new file mode 100644 index 00000000000..e9d8366c41a --- /dev/null +++ b/spec/requests/ci/commits_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe "Commits" do + before do + @project = FactoryGirl.create :project + @commit = FactoryGirl.create :commit, project: @project + end + + describe "GET /:project/refs/:ref_name/commits/:id/status.json" do + before do + get status_project_ref_commit_path(@project, @commit.ref, @commit.sha), format: :json + end + + it { response.status.should == 200 } + it { response.body.should include(@commit.sha) } + end +end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb new file mode 100644 index 00000000000..34e00d5b3c0 --- /dev/null +++ b/spec/services/ci/create_commit_service_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper' + +describe CreateCommitService do + let(:service) { CreateCommitService.new } + let(:project) { FactoryGirl.create(:project) } + + describe :execute do + context 'valid params' do + let(:commit) do + service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + end + + it { commit.should be_kind_of(Commit) } + it { commit.should be_valid } + it { commit.should be_persisted } + it { commit.should == project.commits.last } + it { commit.builds.first.should be_kind_of(Build) } + end + + context "skip tag if there is no build for it" do + it "creates commit if there is appropriate job" do + result = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + result.should be_persisted + end + + it "creates commit if there is no appropriate job but deploy job has right ref setting" do + config = YAML.dump({deploy: {deploy: "ls", only: ["0_1"]}}) + + result = service.execute(project, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: config, + commits: [ { message: "Message" } ] + ) + result.should be_persisted + end + end + + describe :ci_skip? do + it "skips builds creation if there is [ci skip] tag in commit message" do + commits = [{message: "some message[ci skip]"}] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.any?.should be_false + commit.status.should == "skipped" + end + + it "does not skips builds creation if there is no [ci skip] tag in commit message" do + commits = [{message: "some message"}] + + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + + commit.builds.first.name.should == "staging" + end + + it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do + commits = [{message: "some message[ci skip]"}] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: "invalid: file" + ) + commit.builds.any?.should be_false + commit.status.should == "skipped" + end + end + + it "skips build creation if there are already builds" do + commits = [{message: "message"}] + commit = service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.count(:all).should == 2 + + commit = service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + commit.builds.count(:all).should == 2 + end + + it "creates commit with failed status if yaml is invalid" do + commits = [{message: "some message"}] + + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: "invalid: file" + ) + + commit.status.should == "failed" + commit.builds.any?.should be_false + end + end +end diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb new file mode 100644 index 00000000000..31614968d55 --- /dev/null +++ b/spec/services/ci/create_project_service_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe CreateProjectService do + let(:service) { CreateProjectService.new } + let(:current_user) { double.as_null_object } + let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + + before { Network.any_instance.stub(enable_ci: true) } + + describe :execute do + context 'valid params' do + let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } + + it { project.should be_kind_of(Project) } + it { project.should be_persisted } + end + + context 'without project dump' do + it 'should raise exception' do + expect { service.execute(current_user, '', '') }.to raise_error + end + end + + context "forking" do + it "uses project as a template for settings and jobs" do + origin_project = FactoryGirl.create(:project) + origin_project.shared_runners_enabled = true + origin_project.public = true + origin_project.allow_git_fetch = true + origin_project.save! + + project = service.execute(current_user, project_dump, 'http://localhost/projects/:project_id', origin_project) + + project.shared_runners_enabled.should be_true + project.public.should be_true + project.allow_git_fetch.should be_true + end + end + end +end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb new file mode 100644 index 00000000000..41db01c2235 --- /dev/null +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe CreateTriggerRequestService do + let(:service) { CreateTriggerRequestService.new } + let(:project) { FactoryGirl.create :project } + let(:trigger) { FactoryGirl.create :trigger, project: project } + + describe :execute do + context 'valid params' do + subject { service.execute(project, trigger, 'master') } + + before do + @commit = FactoryGirl.create :commit, project: project + end + + it { subject.should be_kind_of(TriggerRequest) } + it { subject.commit.should == @commit } + end + + context 'no commit for ref' do + subject { service.execute(project, trigger, 'other-branch') } + + it { subject.should be_nil } + end + + context 'no builds created' do + subject { service.execute(project, trigger, 'master') } + + before do + FactoryGirl.create :commit_without_jobs, project: project + end + + it { subject.should be_nil } + end + + context 'for multiple commits' do + subject { service.execute(project, trigger, 'master') } + + before do + @commit1 = FactoryGirl.create :commit, committed_at: 2.hour.ago, project: project + @commit2 = FactoryGirl.create :commit, committed_at: 1.hour.ago, project: project + @commit3 = FactoryGirl.create :commit, committed_at: 3.hour.ago, project: project + end + + context 'retries latest one' do + it { subject.should be_kind_of(TriggerRequest) } + it { subject.should be_persisted } + it { subject.commit.should == @commit2 } + end + end + end +end diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb new file mode 100644 index 00000000000..f7b9bf58127 --- /dev/null +++ b/spec/services/ci/event_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe EventService do + let (:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } + let (:user) { double(username: "root", id: 1) } + + before do + Event.destroy_all + end + + describe :remove_project do + it "creates event" do + EventService.new.remove_project(user, project) + + Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been removed by root" + end + end + + describe :create_project do + it "creates event" do + EventService.new.create_project(user, project) + + Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been created by root" + end + end + + describe :change_project_settings do + it "creates event" do + EventService.new.change_project_settings(user, project) + + Event.last.description.should == "User \"root\" updated projects settings" + end + end +end \ No newline at end of file diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb new file mode 100644 index 00000000000..4c7094146bb --- /dev/null +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe ImageForBuildService do + let(:service) { ImageForBuildService.new } + let(:project) { FactoryGirl.create(:project) } + let(:commit) { FactoryGirl.create(:commit, project: project, ref: 'master') } + let(:build) { FactoryGirl.create(:build, commit: commit) } + + describe :execute do + before { build } + + context 'branch name' do + before { build.run! } + let(:image) { service.execute(project, ref: 'master') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-running.svg') } + it { image.name.should == 'build-running.svg' } + end + + context 'unknown branch name' do + let(:image) { service.execute(project, ref: 'feature') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-unknown.svg') } + it { image.name.should == 'build-unknown.svg' } + end + + context 'commit sha' do + before { build.run! } + let(:image) { service.execute(project, sha: build.sha) } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-running.svg') } + it { image.name.should == 'build-running.svg' } + end + + context 'unknown commit sha' do + let(:image) { service.execute(project, sha: '0000000') } + + it { image.should be_kind_of(OpenStruct) } + it { image.path.to_s.should include('public/build-unknown.svg') } + it { image.name.should == 'build-unknown.svg' } + end + end +end diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb new file mode 100644 index 00000000000..b5af777dd1d --- /dev/null +++ b/spec/services/ci/register_build_service_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe RegisterBuildService do + let!(:service) { RegisterBuildService.new } + let!(:project) { FactoryGirl.create :project } + let!(:commit) { FactoryGirl.create :commit, project: project } + let!(:pending_build) { FactoryGirl.create :build, project: project, commit: commit } + let!(:shared_runner) { FactoryGirl.create(:runner, is_shared: true) } + let!(:specific_runner) { FactoryGirl.create(:runner, is_shared: false) } + + before do + specific_runner.assign_to(project) + end + + describe :execute do + context 'runner follow tag list' do + it "picks build with the same tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["linux"] + service.execute(specific_runner).should == pending_build + end + + it "does not pick build with different tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["win32"] + service.execute(specific_runner).should be_false + end + + it "picks build without tag" do + service.execute(specific_runner).should == pending_build + end + + it "does not pick build with tag" do + pending_build.tag_list = ["linux"] + pending_build.save + service.execute(specific_runner).should be_false + end + + it "pick build without tag" do + specific_runner.tag_list = ["win32"] + service.execute(specific_runner).should == pending_build + end + end + + context 'allow shared runners' do + before do + project.shared_runners_enabled = true + project.save + end + + context 'shared runner' do + let(:build) { service.execute(shared_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == shared_runner } + end + + context 'specific runner' do + let(:build) { service.execute(specific_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == specific_runner } + end + end + + context 'disallow shared runners' do + context 'shared runner' do + let(:build) { service.execute(shared_runner) } + + it { build.should be_nil } + end + + context 'specific runner' do + let(:build) { service.execute(specific_runner) } + + it { build.should be_kind_of(Build) } + it { build.should be_valid } + it { build.should be_running } + it { build.runner.should == specific_runner } + end + end + end +end diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb new file mode 100644 index 00000000000..2bb153942e8 --- /dev/null +++ b/spec/services/ci/web_hook_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe WebHookService do + let (:project) { FactoryGirl.create :project } + let (:commit) { FactoryGirl.create :commit, project: project } + let (:build) { FactoryGirl.create :build, commit: commit } + let (:hook) { FactoryGirl.create :web_hook, project: project } + + describe :execute do + it "should execute successfully" do + stub_request(:post, hook.url).to_return(status: 200) + WebHookService.new.build_end(build).should be_true + end + end + + context 'build_data' do + it "contains all needed fields" do + build_data(build).should include( + :build_id, + :project_id, + :ref, + :build_status, + :build_started_at, + :build_finished_at, + :before_sha, + :project_name, + :gitlab_url, + :build_name + ) + end + end + + def build_data(build) + WebHookService.new.send :build_data, build + end +end -- cgit v1.2.1 From 90c338a49541c95452181af9e0d0bcf9da6c51ad Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 14:37:34 +0200 Subject: Move helpers back to original directory --- app/controllers/application_controller.rb | 8 +- app/controllers/oauth/applications_controller.rb | 4 +- .../oauth/authorized_applications_controller.rb | 2 +- app/controllers/projects/network_controller.rb | 2 +- app/controllers/projects/refs_controller.rb | 2 +- app/controllers/projects/wikis_controller.rb | 2 +- app/controllers/search_controller.rb | 2 +- app/helpers/appearances_helper.rb | 21 ++ app/helpers/application_helper.rb | 315 +++++++++++++++++++ app/helpers/application_settings_helper.rb | 59 ++++ app/helpers/auth_helper.rb | 54 ++++ app/helpers/blob_helper.rb | 74 +++++ app/helpers/branches_helper.rb | 17 ++ app/helpers/broadcast_messages_helper.rb | 16 + app/helpers/commits_helper.rb | 183 ++++++++++++ app/helpers/compare_helper.rb | 21 ++ app/helpers/dashboard_helper.rb | 9 + app/helpers/diff_helper.rb | 170 +++++++++++ app/helpers/emails_helper.rb | 57 ++++ app/helpers/events_helper.rb | 200 +++++++++++++ app/helpers/explore_helper.rb | 17 ++ app/helpers/external_wiki_helper.rb | 11 + app/helpers/git_helper.rb | 5 + app/helpers/gitlab/appearances_helper.rb | 23 -- app/helpers/gitlab/application_helper.rb | 317 -------------------- app/helpers/gitlab/application_settings_helper.rb | 61 ---- app/helpers/gitlab/auth_helper.rb | 52 ---- app/helpers/gitlab/blob_helper.rb | 76 ----- app/helpers/gitlab/branches_helper.rb | 19 -- app/helpers/gitlab/broadcast_messages_helper.rb | 18 -- app/helpers/gitlab/commits_helper.rb | 185 ------------ app/helpers/gitlab/compare_helper.rb | 23 -- app/helpers/gitlab/dashboard_helper.rb | 11 - app/helpers/gitlab/diff_helper.rb | 172 ----------- app/helpers/gitlab/emails_helper.rb | 59 ---- app/helpers/gitlab/events_helper.rb | 205 ------------- app/helpers/gitlab/explore_helper.rb | 19 -- app/helpers/gitlab/external_wiki_helper.rb | 13 - app/helpers/gitlab/git_helper.rb | 7 - app/helpers/gitlab/gitlab_markdown_helper.rb | 195 ------------ app/helpers/gitlab/gitlab_routing_helper.rb | 69 ----- app/helpers/gitlab/graph_helper.rb | 18 -- app/helpers/gitlab/groups_helper.rb | 35 --- app/helpers/gitlab/icons_helper.rb | 87 ------ app/helpers/gitlab/issues_helper.rb | 90 ------ app/helpers/gitlab/labels_helper.rb | 103 ------- app/helpers/gitlab/merge_requests_helper.rb | 76 ----- app/helpers/gitlab/milestones_helper.rb | 38 --- app/helpers/gitlab/namespaces_helper.rb | 38 --- app/helpers/gitlab/nav_helper.rb | 23 -- app/helpers/gitlab/notes_helper.rb | 78 ----- app/helpers/gitlab/notifications_helper.rb | 17 -- app/helpers/gitlab/page_layout_helper.rb | 37 --- app/helpers/gitlab/preferences_helper.rb | 67 ----- app/helpers/gitlab/projects_helper.rb | 332 --------------------- app/helpers/gitlab/search_helper.rb | 114 ------- app/helpers/gitlab/selects_helper.rb | 47 --- app/helpers/gitlab/snippets_helper.rb | 22 -- app/helpers/gitlab/sorting_helper.rb | 98 ------ app/helpers/gitlab/submodule_helper.rb | 76 ----- app/helpers/gitlab/tab_helper.rb | 133 --------- app/helpers/gitlab/tags_helper.rb | 16 - app/helpers/gitlab/tree_helper.rb | 89 ------ app/helpers/gitlab/version_check_helper.rb | 9 - app/helpers/gitlab/visibility_level_helper.rb | 97 ------ app/helpers/gitlab/wiki_helper.rb | 26 -- app/helpers/gitlab_markdown_helper.rb | 196 ++++++++++++ app/helpers/gitlab_routing_helper.rb | 67 +++++ app/helpers/graph_helper.rb | 16 + app/helpers/groups_helper.rb | 42 +++ app/helpers/icons_helper.rb | 85 ++++++ app/helpers/issues_helper.rb | 88 ++++++ app/helpers/labels_helper.rb | 101 +++++++ app/helpers/merge_requests_helper.rb | 74 +++++ app/helpers/milestones_helper.rb | 36 +++ app/helpers/namespaces_helper.rb | 36 +++ app/helpers/nav_helper.rb | 21 ++ app/helpers/notes_helper.rb | 76 +++++ app/helpers/notifications_helper.rb | 15 + app/helpers/preferences_helper.rb | 44 +++ app/helpers/projects_helper.rb | 330 ++++++++++++++++++++ app/helpers/search_helper.rb | 112 +++++++ app/helpers/selects_helper.rb | 45 +++ app/helpers/snippets_helper.rb | 20 ++ app/helpers/sorting_helper.rb | 96 ++++++ app/helpers/submodule_helper.rb | 74 +++++ app/helpers/tab_helper.rb | 131 ++++++++ app/helpers/tags_helper.rb | 14 + app/helpers/tree_helper.rb | 88 ++++++ app/helpers/version_check_helper.rb | 7 + app/helpers/visibility_level_helper.rb | 95 ++++++ app/mailers/base_mailer.rb | 4 +- app/mailers/notify.rb | 6 +- lib/gitlab/url_builder.rb | 2 +- spec/lib/ci/ansi2html_spec.rb | 2 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 4 +- spec/lib/ci/upgrader_spec.rb | 39 --- spec/services/ci/create_commit_service_spec.rb | 8 +- spec/services/ci/create_project_service_spec.rb | 2 +- .../ci/create_trigger_request_service_spec.rb | 2 +- spec/services/ci/event_service_spec.rb | 6 +- spec/services/ci/image_for_build_service_spec.rb | 2 +- spec/services/ci/register_build_service_spec.rb | 2 +- spec/services/ci/web_hook_service_spec.rb | 2 +- 104 files changed, 3168 insertions(+), 3363 deletions(-) create mode 100644 app/helpers/appearances_helper.rb create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/application_settings_helper.rb create mode 100644 app/helpers/auth_helper.rb create mode 100644 app/helpers/blob_helper.rb create mode 100644 app/helpers/branches_helper.rb create mode 100644 app/helpers/broadcast_messages_helper.rb create mode 100644 app/helpers/commits_helper.rb create mode 100644 app/helpers/compare_helper.rb create mode 100644 app/helpers/dashboard_helper.rb create mode 100644 app/helpers/diff_helper.rb create mode 100644 app/helpers/emails_helper.rb create mode 100644 app/helpers/events_helper.rb create mode 100644 app/helpers/explore_helper.rb create mode 100644 app/helpers/external_wiki_helper.rb create mode 100644 app/helpers/git_helper.rb delete mode 100644 app/helpers/gitlab/appearances_helper.rb delete mode 100644 app/helpers/gitlab/application_helper.rb delete mode 100644 app/helpers/gitlab/application_settings_helper.rb delete mode 100644 app/helpers/gitlab/auth_helper.rb delete mode 100644 app/helpers/gitlab/blob_helper.rb delete mode 100644 app/helpers/gitlab/branches_helper.rb delete mode 100644 app/helpers/gitlab/broadcast_messages_helper.rb delete mode 100644 app/helpers/gitlab/commits_helper.rb delete mode 100644 app/helpers/gitlab/compare_helper.rb delete mode 100644 app/helpers/gitlab/dashboard_helper.rb delete mode 100644 app/helpers/gitlab/diff_helper.rb delete mode 100644 app/helpers/gitlab/emails_helper.rb delete mode 100644 app/helpers/gitlab/events_helper.rb delete mode 100644 app/helpers/gitlab/explore_helper.rb delete mode 100644 app/helpers/gitlab/external_wiki_helper.rb delete mode 100644 app/helpers/gitlab/git_helper.rb delete mode 100644 app/helpers/gitlab/gitlab_markdown_helper.rb delete mode 100644 app/helpers/gitlab/gitlab_routing_helper.rb delete mode 100644 app/helpers/gitlab/graph_helper.rb delete mode 100644 app/helpers/gitlab/groups_helper.rb delete mode 100644 app/helpers/gitlab/icons_helper.rb delete mode 100644 app/helpers/gitlab/issues_helper.rb delete mode 100644 app/helpers/gitlab/labels_helper.rb delete mode 100644 app/helpers/gitlab/merge_requests_helper.rb delete mode 100644 app/helpers/gitlab/milestones_helper.rb delete mode 100644 app/helpers/gitlab/namespaces_helper.rb delete mode 100644 app/helpers/gitlab/nav_helper.rb delete mode 100644 app/helpers/gitlab/notes_helper.rb delete mode 100644 app/helpers/gitlab/notifications_helper.rb delete mode 100644 app/helpers/gitlab/page_layout_helper.rb delete mode 100644 app/helpers/gitlab/preferences_helper.rb delete mode 100644 app/helpers/gitlab/projects_helper.rb delete mode 100644 app/helpers/gitlab/search_helper.rb delete mode 100644 app/helpers/gitlab/selects_helper.rb delete mode 100644 app/helpers/gitlab/snippets_helper.rb delete mode 100644 app/helpers/gitlab/sorting_helper.rb delete mode 100644 app/helpers/gitlab/submodule_helper.rb delete mode 100644 app/helpers/gitlab/tab_helper.rb delete mode 100644 app/helpers/gitlab/tags_helper.rb delete mode 100644 app/helpers/gitlab/tree_helper.rb delete mode 100644 app/helpers/gitlab/version_check_helper.rb delete mode 100644 app/helpers/gitlab/visibility_level_helper.rb delete mode 100644 app/helpers/gitlab/wiki_helper.rb create mode 100644 app/helpers/gitlab_markdown_helper.rb create mode 100644 app/helpers/gitlab_routing_helper.rb create mode 100644 app/helpers/graph_helper.rb create mode 100644 app/helpers/groups_helper.rb create mode 100644 app/helpers/icons_helper.rb create mode 100644 app/helpers/issues_helper.rb create mode 100644 app/helpers/labels_helper.rb create mode 100644 app/helpers/merge_requests_helper.rb create mode 100644 app/helpers/milestones_helper.rb create mode 100644 app/helpers/namespaces_helper.rb create mode 100644 app/helpers/nav_helper.rb create mode 100644 app/helpers/notes_helper.rb create mode 100644 app/helpers/notifications_helper.rb create mode 100644 app/helpers/preferences_helper.rb create mode 100644 app/helpers/projects_helper.rb create mode 100644 app/helpers/search_helper.rb create mode 100644 app/helpers/selects_helper.rb create mode 100644 app/helpers/snippets_helper.rb create mode 100644 app/helpers/sorting_helper.rb create mode 100644 app/helpers/submodule_helper.rb create mode 100644 app/helpers/tab_helper.rb create mode 100644 app/helpers/tags_helper.rb create mode 100644 app/helpers/tree_helper.rb create mode 100644 app/helpers/version_check_helper.rb create mode 100644 app/helpers/visibility_level_helper.rb delete mode 100644 spec/lib/ci/upgrader_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 54d15b7d7c1..f029abc5013 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,13 +1,9 @@ require 'gon' class ApplicationController < ActionController::Base - def self.railtie_helpers_paths - "app/helpers/gitlab" - end - include Gitlab::CurrentSettings - include Gitlab::GitlabRoutingHelper - include Gitlab::PageLayoutHelper + include GitlabRoutingHelper + include PageLayoutHelper PER_PAGE = 20 diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 4e007d2a4d0..dc22101cd5e 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -1,7 +1,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::CurrentSettings - include Gitlab::PageLayoutHelper - + include PageLayoutHelper + before_action :verify_user_oauth_applications_enabled before_action :authenticate_user! diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 08d94408fc8..4193ac11399 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -1,5 +1,5 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController - include Gitlab::PageLayoutHelper + include PageLayoutHelper layout 'profile' diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index b70e12365da..b181c47baec 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -1,6 +1,6 @@ class Projects::NetworkController < Projects::ApplicationController include ExtractsPath - include Gitlab::ApplicationHelper + include ApplicationHelper before_action :require_non_empty_project before_action :assign_ref_vars diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index a9081a5ae16..6080c849c8d 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -1,6 +1,6 @@ class Projects::RefsController < Projects::ApplicationController include ExtractsPath - include Gitlab::TreeHelper + include TreeHelper before_action :require_non_empty_project before_action :assign_ref_vars diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 870ff035b03..b2bf9fa05ea 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -5,7 +5,7 @@ class Projects::WikisController < Projects::ApplicationController before_action :authorize_create_wiki!, only: [:edit, :create, :history] before_action :authorize_admin_wiki!, only: :destroy before_action :load_project_wiki - include Gitlab::WikiHelper + #include WikiHelper def pages @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 63d336b2bd5..eb0408a95e5 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,5 @@ class SearchController < ApplicationController - include Gitlab::SearchHelper + include SearchHelper layout 'search' diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb new file mode 100644 index 00000000000..14df8d4cbd7 --- /dev/null +++ b/app/helpers/appearances_helper.rb @@ -0,0 +1,21 @@ +module AppearancesHelper + def brand_item + nil + end + + def brand_title + 'GitLab Community Edition' + end + + def brand_image + nil + end + + def brand_text + nil + end + + def brand_header_logo + image_tag 'logo.svg' + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 00000000000..a803b66c502 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,315 @@ +require 'digest/md5' +require 'uri' + +module ApplicationHelper + # Check if a particular controller is the current one + # + # args - One or more controller names to check + # + # Examples + # + # # On TreeController + # current_controller?(:tree) # => true + # current_controller?(:commits) # => false + # current_controller?(:commits, :tree) # => true + def current_controller?(*args) + args.any? { |v| v.to_s.downcase == controller.controller_name } + end + + # Check if a particular action is the current one + # + # args - One or more action names to check + # + # Examples + # + # # On Projects#new + # current_action?(:new) # => true + # current_action?(:create) # => false + # current_action?(:new, :create) # => true + def current_action?(*args) + args.any? { |v| v.to_s.downcase == action_name } + end + + def project_icon(project_id, options = {}) + project = + if project_id.is_a?(Project) + project = project_id + else + Project.find_with_namespace(project_id) + end + + if project.avatar_url + image_tag project.avatar_url, options + else # generated icon + project_identicon(project, options) + end + end + + def project_identicon(project, options = {}) + allowed_colors = { + red: 'FFEBEE', + purple: 'F3E5F5', + indigo: 'E8EAF6', + blue: 'E3F2FD', + teal: 'E0F2F1', + orange: 'FBE9E7', + gray: 'EEEEEE' + } + + options[:class] ||= '' + options[:class] << ' identicon' + bg_key = project.id % 7 + style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" + + content_tag(:div, class: options[:class], style: style) do + project.name[0, 1].upcase + end + end + + def avatar_icon(user_email = '', size = nil) + user = User.find_by(email: user_email) + + if user + user.avatar_url(size) || default_avatar + else + gravatar_icon(user_email, size) + end + end + + def gravatar_icon(user_email = '', size = nil) + GravatarService.new.execute(user_email, size) || + default_avatar + end + + def default_avatar + image_path('no_avatar.png') + end + + def last_commit(project) + if project.repo_exists? + time_ago_with_tooltip(project.repository.commit.committed_date) + else + 'Never' + end + rescue + 'Never' + end + + def grouped_options_refs + repository = @project.repository + + options = [ + ['Branches', repository.branch_names], + ['Tags', VersionSorter.rsort(repository.tag_names)] + ] + + # If reference is commit id - we should add it to branch/tag selectbox + if(@ref && !options.flatten.include?(@ref) && + @ref =~ /\A[0-9a-zA-Z]{6,52}\z/) + options << ['Commit', [@ref]] + end + + grouped_options_for_select(options, @ref || @project.default_branch) + end + + def emoji_autocomplete_source + # should be an array of strings + # so to_s can be called, because it is sufficient and to_json is too slow + Emoji.names.to_s + end + + # Define whenever show last push event + # with suggestion to create MR + def show_last_push_widget?(event) + # Skip if event is not about added or modified non-master branch + return false unless event && event.last_push_to_non_root? && !event.rm_ref? + + project = event.project + + # Skip if project repo is empty or MR disabled + return false unless project && !project.empty_repo? && project.merge_requests_enabled + + # Skip if user already created appropriate MR + return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? + + # Skip if user removed branch right after that + return false unless project.repository.branch_names.include?(event.branch_name) + + true + end + + def hexdigest(string) + Digest::SHA1.hexdigest string + end + + def simple_sanitize(str) + sanitize(str, tags: %w(a span)) + end + + def body_data_page + path = controller.controller_path.split('/') + namespace = path.first if path.second + + [namespace, controller.controller_name, controller.action_name].compact.join(':') + end + + # shortcut for gitlab config + def gitlab_config + Gitlab.config.gitlab + end + + # shortcut for gitlab extra config + def extra_config + Gitlab.config.extra + end + + def search_placeholder + if @project && @project.persisted? + 'Search in this project' + elsif @snippet || @snippets || @show_snippets + 'Search snippets' + elsif @group && @group.persisted? + 'Search in this group' + else + 'Search' + end + end + + def broadcast_message + BroadcastMessage.current + end + + # Render a `time` element with Javascript-based relative date and tooltip + # + # time - Time object + # placement - Tooltip placement String (default: "top") + # html_class - Custom class for `time` element (default: "time_ago") + # skip_js - When true, exclude the `script` tag (default: false) + # + # By default also includes a `script` element with Javascript necessary to + # initialize the `timeago` jQuery extension. If this method is called many + # times, for example rendering hundreds of commits, it's advisable to disable + # this behavior using the `skip_js` argument and re-initializing `timeago` + # manually once all of the elements have been rendered. + # + # A `js-timeago` class is always added to the element, even when a custom + # `html_class` argument is provided. + # + # Returns an HTML-safe String + def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) + element = content_tag :time, time.to_s, + class: "#{html_class} js-timeago", + datetime: time.getutc.iso8601, + title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), + data: { toggle: 'tooltip', placement: placement } + + element += javascript_tag "$('.js-timeago').timeago()" unless skip_js + + element + end + + def render_markup(file_name, file_content) + if gitlab_markdown?(file_name) + Haml::Helpers.preserve(markdown(file_content)) + elsif asciidoc?(file_name) + asciidoc(file_content) + elsif plain?(file_name) + content_tag :pre, class: 'plain-readme' do + file_content + end + else + GitHub::Markup.render(file_name, file_content). + force_encoding(file_content.encoding).html_safe + end + rescue RuntimeError + simple_format(file_content) + end + + def plain?(filename) + Gitlab::MarkupHelper.plain?(filename) + end + + def markup?(filename) + Gitlab::MarkupHelper.markup?(filename) + end + + def gitlab_markdown?(filename) + Gitlab::MarkupHelper.gitlab_markdown?(filename) + end + + def asciidoc?(filename) + Gitlab::MarkupHelper.asciidoc?(filename) + end + + def promo_host + 'about.gitlab.com' + end + + def promo_url + 'https://' + promo_host + end + + def page_filter_path(options = {}) + without = options.delete(:without) + + exist_opts = { + state: params[:state], + scope: params[:scope], + label_name: params[:label_name], + milestone_id: params[:milestone_id], + assignee_id: params[:assignee_id], + author_id: params[:author_id], + sort: params[:sort], + } + + options = exist_opts.merge(options) + + if without.present? + without.each do |key| + options.delete(key) + end + end + + path = request.path + path << "?#{options.to_param}" + path + end + + def outdated_browser? + browser.ie? && browser.version.to_i < 10 + end + + def path_to_key(key, admin = false) + if admin + admin_user_key_path(@user, key) + else + profile_key_path(key) + end + end + + def state_filters_text_for(entity, project) + titles = { + opened: "Open" + } + + entity_title = titles[entity] || entity.to_s.humanize + + count = + if project.nil? + nil + elsif current_controller?(:issues) + project.issues.send(entity).count + elsif current_controller?(:merge_requests) + project.merge_requests.send(entity).count + end + + html = content_tag :span, entity_title + + if count.present? + html += " " + html += content_tag :span, number_with_delimiter(count), class: 'badge' + end + + html.html_safe + end +end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb new file mode 100644 index 00000000000..7d6b58ee21a --- /dev/null +++ b/app/helpers/application_settings_helper.rb @@ -0,0 +1,59 @@ +module ApplicationSettingsHelper + def gravatar_enabled? + current_application_settings.gravatar_enabled? + end + + def twitter_sharing_enabled? + current_application_settings.twitter_sharing_enabled? + end + + def signup_enabled? + current_application_settings.signup_enabled? + end + + def signin_enabled? + current_application_settings.signin_enabled? + end + + def extra_sign_in_text + current_application_settings.sign_in_text + end + + def user_oauth_applications? + current_application_settings.user_oauth_applications + end + + # Return a group of checkboxes that use Bootstrap's button plugin for a + # toggle button effect. + def restricted_level_checkboxes(help_block_id) + Gitlab::VisibilityLevel.options.map do |name, level| + checked = restricted_visibility_levels(true).include?(level) + css_class = 'btn' + css_class += ' active' if checked + checkbox_name = 'application_setting[restricted_visibility_levels][]' + + label_tag(checkbox_name, class: css_class) do + check_box_tag(checkbox_name, level, checked, + autocomplete: 'off', + 'aria-describedby' => help_block_id) + name + end + end + end + + # Return a group of checkboxes that use Bootstrap's button plugin for a + # toggle button effect. + def import_sources_checkboxes(help_block_id) + Gitlab::ImportSources.options.map do |name, source| + checked = current_application_settings.import_sources.include?(source) + css_class = 'btn' + css_class += ' active' if checked + checkbox_name = 'application_setting[import_sources][]' + + label_tag(checkbox_name, class: css_class) do + check_box_tag(checkbox_name, source, checked, + autocomplete: 'off', + 'aria-describedby' => help_block_id) + name + end + end + end +end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb new file mode 100644 index 00000000000..d9502181c4f --- /dev/null +++ b/app/helpers/auth_helper.rb @@ -0,0 +1,54 @@ +module AuthHelper + PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze + FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze + + def ldap_enabled? + Gitlab.config.ldap.enabled + end + + def provider_has_icon?(name) + PROVIDERS_WITH_ICONS.include?(name.to_s) + end + + def auth_providers + Gitlab::OAuth::Provider.providers + end + + def label_for_provider(name) + Gitlab::OAuth::Provider.label_for(name) + end + + def form_based_provider?(name) + FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s } + end + + def form_based_providers + auth_providers.select { |provider| form_based_provider?(provider) } + end + + def crowd_enabled? + auth_providers.include? :crowd + end + + def button_based_providers + auth_providers.reject { |provider| form_based_provider?(provider) } + end + + def provider_image_tag(provider, size = 64) + label = label_for_provider(provider) + + if provider_has_icon?(provider) + file_name = "#{provider.to_s.split('_').first}_#{size}.png" + + image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}") + else + label + end + end + + def auth_active?(provider) + current_user.identities.exists?(provider: provider.to_s) + end + + extend self +end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb new file mode 100644 index 00000000000..77d99140c43 --- /dev/null +++ b/app/helpers/blob_helper.rb @@ -0,0 +1,74 @@ +module BlobHelper + def highlight(blob_name, blob_content, nowrap: false, continue: false) + @formatter ||= Rouge::Formatters::HTMLGitlab.new( + nowrap: nowrap, + cssclass: 'code highlight', + lineanchors: true, + lineanchorsid: 'LC' + ) + + begin + @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new + result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe + rescue + @lexer = Rouge::Lexers::PlainText + result = @formatter.format(@lexer.lex(blob_content)).html_safe + end + + result + end + + def no_highlight_files + %w(credits changelog news copying copyright license authors) + end + + def edit_blob_link(project, ref, path, options = {}) + blob = + begin + project.repository.blob_at(ref, path) + rescue + nil + end + + if blob && blob.text? + text = 'Edit' + after = options[:after] || '' + from_mr = options[:from_merge_request_id] + link_opts = {} + link_opts[:from_merge_request_id] = from_mr if from_mr + cls = 'btn btn-small' + if allowed_tree_edit?(project, ref) + link_to(text, + namespace_project_edit_blob_path(project.namespace, project, + tree_join(ref, path), + link_opts), + class: cls + ) + else + content_tag :span, text, class: cls + ' disabled' + end + after.html_safe + else + '' + end + end + + def leave_edit_message + "Leave edit mode?\nAll unsaved changes will be lost." + end + + def editing_preview_title(filename) + if Gitlab::MarkupHelper.previewable?(filename) + 'Preview' + else + 'Preview changes' + end + end + + # Return an image icon depending on the file mode and extension + # + # mode - File unix mode + # mode - File name + def blob_icon(mode, name) + icon("#{file_type_icon_class('file', mode, name)} fw") + end +end diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb new file mode 100644 index 00000000000..d6eaa7d57bc --- /dev/null +++ b/app/helpers/branches_helper.rb @@ -0,0 +1,17 @@ +module BranchesHelper + def can_remove_branch?(project, branch_name) + if project.protected_branch? branch_name + false + elsif branch_name == project.repository.root_ref + false + else + can?(current_user, :push_code, project) + end + end + + def can_push_branch?(project, branch_name) + return false unless project.repository.branch_names.include?(branch_name) + + ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) + end +end diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb new file mode 100644 index 00000000000..6484dca6b55 --- /dev/null +++ b/app/helpers/broadcast_messages_helper.rb @@ -0,0 +1,16 @@ +module BroadcastMessagesHelper + def broadcast_styling(broadcast_message) + styling = '' + + if broadcast_message.color.present? + styling << "background-color: #{broadcast_message.color}" + styling << '; ' if broadcast_message.font.present? + end + + if broadcast_message.font.present? + styling << "color: #{broadcast_message.font}" + end + + styling + end +end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb new file mode 100644 index 00000000000..d13d80be293 --- /dev/null +++ b/app/helpers/commits_helper.rb @@ -0,0 +1,183 @@ +# encoding: utf-8 +module CommitsHelper + # Returns a link to the commit author. If the author has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the author email as specified in the commit. + # + # options: + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def commit_author_link(commit, options = {}) + commit_person_link(commit, options.merge(source: :author)) + end + + # Just like #author_link but for the committer. + def commit_committer_link(commit, options = {}) + commit_person_link(commit, options.merge(source: :committer)) + end + + def image_diff_class(diff) + if diff.deleted_file + "deleted" + elsif diff.new_file + "added" + else + nil + end + end + + def commit_to_html(commit, project, inline = true) + template = inline ? "inline_commit" : "commit" + escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? + end + + # Breadcrumb links for a Project and, if applicable, a tree path + def commits_breadcrumbs + return unless @project && @ref + + # Add the root project link and the arrow icon + crumbs = content_tag(:li) do + link_to( + @project.path, + namespace_project_commits_path(@project.namespace, @project, @ref) + ) + end + + if @path + parts = @path.split('/') + + parts.each_with_index do |part, i| + crumbs << content_tag(:li) do + # The text is just the individual part, but the link needs all the parts before it + link_to( + part, + namespace_project_commits_path( + @project.namespace, + @project, + tree_join(@ref, parts[0..i].join('/')) + ) + ) + end + end + end + + crumbs.html_safe + end + + # Return Project default branch, if it present in array + # Else - first branch in array (mb last actual branch) + def commit_default_branch(project, branches) + branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop + end + + # Returns the sorted alphabetically links to branches, separated by a comma + def commit_branches_links(project, branches) + branches.sort.map do |branch| + link_to( + namespace_project_tree_path(project.namespace, project, branch) + ) do + content_tag :span, class: 'label label-gray' do + icon('code-fork') + ' ' + branch + end + end + end.join(" ").html_safe + end + + # Returns the sorted links to tags, separated by a comma + def commit_tags_links(project, tags) + sorted = VersionSorter.rsort(tags) + sorted.map do |tag| + link_to( + namespace_project_commits_path(project.namespace, project, + project.repository.find_tag(tag).name) + ) do + content_tag :span, class: 'label label-gray' do + icon('tag') + ' ' + tag + end + end + end.join(" ").html_safe + end + + def link_to_browse_code(project, commit) + if current_controller?(:projects, :commits) + if @repo.blob_at(commit.id, @path) + return link_to( + "Browse File »", + namespace_project_blob_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) + elsif @path.present? + return link_to( + "Browse Dir »", + namespace_project_tree_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) + end + end + link_to( + "Browse Code »", + namespace_project_tree_path(project.namespace, project, commit), + class: "pull-right" + ) + end + + protected + + # Private: Returns a link to a person. If the person has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the person email as specified in the commit. + # + # options: + # source: one of :author or :committer + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def commit_person_link(commit, options = {}) + user = commit.send(options[:source]) + + source_name = clean(commit.send "#{options[:source]}_name".to_sym) + source_email = clean(commit.send "#{options[:source]}_email".to_sym) + + person_name = user.try(:name) || source_name + person_email = user.try(:email) || source_email + + text = + if options[:avatar] + avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") + %Q{#{avatar} #{person_name}} + else + person_name + end + + options = { + class: "commit-#{options[:source]}-link has_tooltip", + data: { :'original-title' => sanitize(source_email) } + } + + if user.nil? + mail_to(source_email, text.html_safe, options) + else + link_to(text.html_safe, user_path(user), options) + end + end + + def view_file_btn(commit_sha, diff, project) + link_to( + namespace_project_blob_path(project.namespace, project, + tree_join(commit_sha, diff.new_path)), + class: 'btn btn-small view-file js-view-file' + ) do + raw('View file @') + content_tag(:span, commit_sha[0..6], + class: 'commit-short-id') + end + end + + def truncate_sha(sha) + Commit.truncate_sha(sha) + end + + def clean(string) + Sanitize.clean(string, remove_contents: true) + end +end diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb new file mode 100644 index 00000000000..f1dc906cab4 --- /dev/null +++ b/app/helpers/compare_helper.rb @@ -0,0 +1,21 @@ +module CompareHelper + def create_mr_button?(from = params[:from], to = params[:to], project = @project) + from.present? && + to.present? && + from != to && + project.merge_requests_enabled && + project.repository.branch_names.include?(from) && + project.repository.branch_names.include?(to) + end + + def create_mr_path(from = params[:from], to = params[:to], project = @project) + new_namespace_project_merge_request_path( + project.namespace, + project, + merge_request: { + source_branch: to, + target_branch: from + } + ) + end +end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb new file mode 100644 index 00000000000..c25b54eadc6 --- /dev/null +++ b/app/helpers/dashboard_helper.rb @@ -0,0 +1,9 @@ +module DashboardHelper + def assigned_issues_dashboard_path + issues_dashboard_path(assignee_id: current_user.id) + end + + def assigned_mrs_dashboard_path + merge_requests_dashboard_path(assignee_id: current_user.id) + end +end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb new file mode 100644 index 00000000000..6ffa1a7121d --- /dev/null +++ b/app/helpers/diff_helper.rb @@ -0,0 +1,170 @@ +module DiffHelper + def allowed_diff_size + if diff_hard_limit_enabled? + Commit::DIFF_HARD_LIMIT_FILES + else + Commit::DIFF_SAFE_FILES + end + end + + def allowed_diff_lines + if diff_hard_limit_enabled? + Commit::DIFF_HARD_LIMIT_LINES + else + Commit::DIFF_SAFE_LINES + end + end + + def safe_diff_files(diffs) + lines = 0 + safe_files = [] + diffs.first(allowed_diff_size).each do |diff| + lines += diff.diff.lines.count + break if lines > allowed_diff_lines + safe_files << Gitlab::Diff::File.new(diff) + end + safe_files + end + + def diff_hard_limit_enabled? + # Enabling hard limit allows user to see more diff information + if params[:force_show_diff].present? + true + else + false + end + end + + def generate_line_code(file_path, line) + Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) + end + + def parallel_diff(diff_file, index) + lines = [] + skip_next = false + + # Building array of lines + # + # [ + # left_type, left_line_number, left_line_content, left_line_code, + # right_line_type, right_line_number, right_line_content, right_line_code + # ] + # + diff_file.diff_lines.each do |line| + + full_line = line.text + type = line.type + line_code = generate_line_code(diff_file.file_path, line) + line_new = line.new_pos + line_old = line.old_pos + + next_line = diff_file.next_line(line.index) + + if next_line + next_line_code = generate_line_code(diff_file.file_path, next_line) + next_type = next_line.type + next_line = next_line.text + end + + if type == 'match' || type.nil? + # line in the right panel is the same as in the left one + line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code] + lines.push(line) + elsif type == 'old' + if next_type == 'new' + # Left side has text removed, right side has text added + line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code] + lines.push(line) + skip_next = true + elsif next_type == 'old' || next_type.nil? + # Left side has text removed, right side doesn't have any change + # No next line code, no new line number, no new line text + line = [type, line_old, full_line, line_code, next_type, nil, " ", nil] + lines.push(line) + end + elsif type == 'new' + if skip_next + # Change has been already included in previous line so no need to do it again + skip_next = false + next + else + # Change is only on the right side, left side has no change + line = [nil, nil, " ", line_code, type, line_new, full_line, line_code] + lines.push(line) + end + end + end + lines + end + + def unfold_bottom_class(bottom) + (bottom) ? 'js-unfold-bottom' : '' + end + + def unfold_class(unfold) + (unfold) ? 'unfold js-unfold' : '' + end + + def diff_line_content(line) + if line.blank? + "  " + else + line + end + end + + def line_comments + @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code) + end + + def organize_comments(type_left, type_right, line_code_left, line_code_right) + comments_left = comments_right = nil + + unless type_left.nil? && type_right == 'new' + comments_left = line_comments[line_code_left] + end + + unless type_left.nil? && type_right.nil? + comments_right = line_comments[line_code_right] + end + + [comments_left, comments_right] + end + + def inline_diff_btn + params_copy = params.dup + params_copy[:view] = 'inline' + # Always use HTML to handle case where JSON diff rendered this button + params_copy.delete(:format) + + link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do + 'Inline' + end + end + + def parallel_diff_btn + params_copy = params.dup + params_copy[:view] = 'parallel' + # Always use HTML to handle case where JSON diff rendered this button + params_copy.delete(:format) + + link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do + 'Side-by-side' + end + end + + def submodule_link(blob, ref, repository = @repository) + tree, commit = submodule_links(blob, ref, repository) + commit_id = if commit.nil? + blob.id[0..10] + else + link_to "#{blob.id[0..10]}", commit + end + + [ + content_tag(:span, link_to(truncate(blob.name, length: 40), tree)), + '@', + content_tag(:span, commit_id, class: 'monospace'), + ].join(' ').html_safe + end +end diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb new file mode 100644 index 00000000000..45788ba95ac --- /dev/null +++ b/app/helpers/emails_helper.rb @@ -0,0 +1,57 @@ +module EmailsHelper + + # Google Actions + # https://developers.google.com/gmail/markup/reference/go-to-action + def email_action(url) + name = action_title(url) + if name + data = { + "@context" => "http://schema.org", + "@type" => "EmailMessage", + "action" => { + "@type" => "ViewAction", + "name" => name, + "url" => url, + } + } + + content_tag :script, type: 'application/ld+json' do + data.to_json.html_safe + end + end + end + + def action_title(url) + return unless url + ["merge_requests", "issues", "commit"].each do |action| + if url.split("/").include?(action) + return "View #{action.humanize.singularize}" + end + end + end + + def color_email_diff(diffcontent) + formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github') + lexer = Rouge::Lexers::Diff + raw formatter.format(lexer.lex(diffcontent)) + end + + def password_reset_token_valid_time + valid_hours = Devise.reset_password_within / 60 / 60 + if valid_hours >= 24 + unit = 'day' + valid_length = (valid_hours / 24).floor + else + unit = 'hour' + valid_length = valid_hours.floor + end + + pluralize(valid_length, unit) + end + + def reset_token_expire_message + link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email)) + msg = "This link is valid for #{password_reset_token_valid_time}. " + msg << "After it expires, you can #{link_tag}." + end +end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb new file mode 100644 index 00000000000..76602614bcd --- /dev/null +++ b/app/helpers/events_helper.rb @@ -0,0 +1,200 @@ +module EventsHelper + def link_to_author(event) + author = event.author + + if author + link_to author.name, user_path(author.username) + else + event.author_name + end + end + + def event_action_name(event) + target = if event.target_type + if event.note? + event.note_target_type + else + event.target_type.titleize.downcase + end + else + 'project' + end + + [event.action_name, target].join(" ") + end + + def event_filter_link(key, tooltip) + key = key.to_s + active = 'active' if @event_filter.active?(key) + link_opts = { + class: "event-filter-link btn btn-default #{active}", + id: "#{key}_event_filter", + title: "Filter by #{tooltip.downcase}", + } + + link_to request.path, link_opts do + content_tag(:span, ' ' + tooltip) + end + end + + def icon_for_event + { + EventFilter.push => 'upload', + EventFilter.merged => 'check-square-o', + EventFilter.comments => 'comments', + EventFilter.team => 'user', + } + end + + def event_feed_title(event) + words = [] + words << event.author_name + words << event_action_name(event) + + if event.push? + words << event.ref_type + words << event.ref_name + words << "at" + elsif event.commented? + if event.note_commit? + words << event.note_short_commit_id + else + words << "##{truncate event.note_target_iid}" + end + words << "at" + elsif event.target + words << "##{event.target_iid}:" + words << event.target.title if event.target.respond_to?(:title) + words << "at" + end + + words << event.project_name + + words.join(" ") + end + + def event_feed_url(event) + if event.issue? + namespace_project_issue_url(event.project.namespace, event.project, + event.issue) + elsif event.merge_request? + namespace_project_merge_request_url(event.project.namespace, + event.project, event.merge_request) + elsif event.note? && event.note_commit? + namespace_project_commit_url(event.project.namespace, event.project, + event.note_target) + elsif event.note? + if event.note_target + if event.note_commit? + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)) + elsif event.note_project_snippet? + namespace_project_snippet_path(event.project.namespace, + event.project, event.note_target) + else + event_note_target_path(event) + end + end + elsif event.push? + if event.push_with_commits? && event.md_ref? + if event.commits_count > 1 + namespace_project_compare_url(event.project.namespace, event.project, + from: event.commit_from, to: + event.commit_to) + else + namespace_project_commit_url(event.project.namespace, event.project, + id: event.commit_to) + end + else + namespace_project_commits_url(event.project.namespace, event.project, + event.ref_name) + end + end + end + + def event_feed_summary(event) + if event.issue? + render "events/event_issue", issue: event.issue + elsif event.push? + render "events/event_push", event: event + elsif event.merge_request? + render "events/event_merge_request", merge_request: event.merge_request + elsif event.note? + render "events/event_note", note: event.note + end + end + + def event_note_target_path(event) + if event.note? && event.note_commit? + namespace_project_commit_path(event.project.namespace, event.project, + event.note_target) + else + polymorphic_path([event.project.namespace.becomes(Namespace), + event.project, event.note_target], + anchor: dom_id(event.target)) + end + end + + def event_note_title_html(event) + if event.note_target + if event.note_commit? + link_to( + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)), + class: "commit_short_id" + ) do + "#{event.note_target_type} #{event.note_short_commit_id}" + end + elsif event.note_project_snippet? + link_to(namespace_project_snippet_path(event.project.namespace, + event.project, + event.note_target)) do + "#{event.note_target_type} ##{truncate event.note_target_id}" + end + else + link_to event_note_target_path(event) do + "#{event.note_target_type} ##{truncate event.note_target_iid}" + end + end + else + content_tag :strong do + "(deleted)" + end + end + end + + def event_note(text, options = {}) + text = first_line_in_markdown(text, 150, options) + sanitize(text, tags: %w(a img b pre code p span)) + end + + def event_commit_title(message) + escape_once(truncate(message.split("\n").first, length: 70)) + rescue + "--broken encoding" + end + + def event_to_atom(xml, event) + if event.proper? + xml.entry do + event_link = event_feed_url(event) + event_title = event_feed_title(event) + event_summary = event_feed_summary(event) + + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" + xml.link href: event_link + xml.title truncate(event_title, length: 80) + xml.updated event.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) + xml.author do |author| + xml.name event.author_name + xml.email event.author_email + end + + xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } + end + end + end +end diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb new file mode 100644 index 00000000000..0d291f9a87e --- /dev/null +++ b/app/helpers/explore_helper.rb @@ -0,0 +1,17 @@ +module ExploreHelper + def explore_projects_filter_path(options={}) + exist_opts = { + sort: params[:sort], + scope: params[:scope], + group: params[:group], + tag: params[:tag], + visibility_level: params[:visibility_level], + } + + options = exist_opts.merge(options) + + path = explore_projects_path + path << "?#{options.to_param}" + path + end +end diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb new file mode 100644 index 00000000000..838b85afdfe --- /dev/null +++ b/app/helpers/external_wiki_helper.rb @@ -0,0 +1,11 @@ +module ExternalWikiHelper + def get_project_wiki_path(project) + external_wiki_service = project.services. + select { |service| service.to_param == 'external_wiki' }.first + if external_wiki_service.present? && external_wiki_service.active? + external_wiki_service.properties['external_wiki_url'] + else + namespace_project_wiki_path(project.namespace, project, :home) + end + end +end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb new file mode 100644 index 00000000000..09684955233 --- /dev/null +++ b/app/helpers/git_helper.rb @@ -0,0 +1,5 @@ +module GitHelper + def strip_gpg_signature(text) + text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") + end +end diff --git a/app/helpers/gitlab/appearances_helper.rb b/app/helpers/gitlab/appearances_helper.rb deleted file mode 100644 index 54cafcd9e40..00000000000 --- a/app/helpers/gitlab/appearances_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - module AppearancesHelper - def brand_item - nil - end - - def brand_title - 'GitLab Community Edition' - end - - def brand_image - nil - end - - def brand_text - nil - end - - def brand_header_logo - image_tag 'logo.svg' - end - end -end diff --git a/app/helpers/gitlab/application_helper.rb b/app/helpers/gitlab/application_helper.rb deleted file mode 100644 index b019ffa5fe2..00000000000 --- a/app/helpers/gitlab/application_helper.rb +++ /dev/null @@ -1,317 +0,0 @@ -require 'digest/md5' -require 'uri' - -module Gitlab - module ApplicationHelper - # Check if a particular controller is the current one - # - # args - One or more controller names to check - # - # Examples - # - # # On TreeController - # current_controller?(:tree) # => true - # current_controller?(:commits) # => false - # current_controller?(:commits, :tree) # => true - def current_controller?(*args) - args.any? { |v| v.to_s.downcase == controller.controller_name } - end - - # Check if a particular action is the current one - # - # args - One or more action names to check - # - # Examples - # - # # On Projects#new - # current_action?(:new) # => true - # current_action?(:create) # => false - # current_action?(:new, :create) # => true - def current_action?(*args) - args.any? { |v| v.to_s.downcase == action_name } - end - - def project_icon(project_id, options = {}) - project = - if project_id.is_a?(Project) - project = project_id - else - Project.find_with_namespace(project_id) - end - - if project.avatar_url - image_tag project.avatar_url, options - else # generated icon - project_identicon(project, options) - end - end - - def project_identicon(project, options = {}) - allowed_colors = { - red: 'FFEBEE', - purple: 'F3E5F5', - indigo: 'E8EAF6', - blue: 'E3F2FD', - teal: 'E0F2F1', - orange: 'FBE9E7', - gray: 'EEEEEE' - } - - options[:class] ||= '' - options[:class] << ' identicon' - bg_key = project.id % 7 - style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" - - content_tag(:div, class: options[:class], style: style) do - project.name[0, 1].upcase - end - end - - def avatar_icon(user_email = '', size = nil) - user = User.find_by(email: user_email) - - if user - user.avatar_url(size) || default_avatar - else - gravatar_icon(user_email, size) - end - end - - def gravatar_icon(user_email = '', size = nil) - GravatarService.new.execute(user_email, size) || - default_avatar - end - - def default_avatar - image_path('no_avatar.png') - end - - def last_commit(project) - if project.repo_exists? - time_ago_with_tooltip(project.repository.commit.committed_date) - else - 'Never' - end - rescue - 'Never' - end - - def grouped_options_refs - repository = @project.repository - - options = [ - ['Branches', repository.branch_names], - ['Tags', VersionSorter.rsort(repository.tag_names)] - ] - - # If reference is commit id - we should add it to branch/tag selectbox - if(@ref && !options.flatten.include?(@ref) && - @ref =~ /\A[0-9a-zA-Z]{6,52}\z/) - options << ['Commit', [@ref]] - end - - grouped_options_for_select(options, @ref || @project.default_branch) - end - - def emoji_autocomplete_source - # should be an array of strings - # so to_s can be called, because it is sufficient and to_json is too slow - Emoji.names.to_s - end - - # Define whenever show last push event - # with suggestion to create MR - def show_last_push_widget?(event) - # Skip if event is not about added or modified non-master branch - return false unless event && event.last_push_to_non_root? && !event.rm_ref? - - project = event.project - - # Skip if project repo is empty or MR disabled - return false unless project && !project.empty_repo? && project.merge_requests_enabled - - # Skip if user already created appropriate MR - return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? - - # Skip if user removed branch right after that - return false unless project.repository.branch_names.include?(event.branch_name) - - true - end - - def hexdigest(string) - Digest::SHA1.hexdigest string - end - - def simple_sanitize(str) - sanitize(str, tags: %w(a span)) - end - - def body_data_page - path = controller.controller_path.split('/') - namespace = path.first if path.second - - [namespace, controller.controller_name, controller.action_name].compact.join(':') - end - - # shortcut for gitlab config - def gitlab_config - Gitlab.config.gitlab - end - - # shortcut for gitlab extra config - def extra_config - Gitlab.config.extra - end - - def search_placeholder - if @project && @project.persisted? - 'Search in this project' - elsif @snippet || @snippets || @show_snippets - 'Search snippets' - elsif @group && @group.persisted? - 'Search in this group' - else - 'Search' - end - end - - def broadcast_message - BroadcastMessage.current - end - - # Render a `time` element with Javascript-based relative date and tooltip - # - # time - Time object - # placement - Tooltip placement String (default: "top") - # html_class - Custom class for `time` element (default: "time_ago") - # skip_js - When true, exclude the `script` tag (default: false) - # - # By default also includes a `script` element with Javascript necessary to - # initialize the `timeago` jQuery extension. If this method is called many - # times, for example rendering hundreds of commits, it's advisable to disable - # this behavior using the `skip_js` argument and re-initializing `timeago` - # manually once all of the elements have been rendered. - # - # A `js-timeago` class is always added to the element, even when a custom - # `html_class` argument is provided. - # - # Returns an HTML-safe String - def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) - element = content_tag :time, time.to_s, - class: "#{html_class} js-timeago", - datetime: time.getutc.iso8601, - title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), - data: { toggle: 'tooltip', placement: placement } - - element += javascript_tag "$('.js-timeago').timeago()" unless skip_js - - element - end - - def render_markup(file_name, file_content) - if gitlab_markdown?(file_name) - Haml::Helpers.preserve(markdown(file_content)) - elsif asciidoc?(file_name) - asciidoc(file_content) - elsif plain?(file_name) - content_tag :pre, class: 'plain-readme' do - file_content - end - else - GitHub::Markup.render(file_name, file_content). - force_encoding(file_content.encoding).html_safe - end - rescue RuntimeError - simple_format(file_content) - end - - def plain?(filename) - Gitlab::MarkupHelper.plain?(filename) - end - - def markup?(filename) - Gitlab::MarkupHelper.markup?(filename) - end - - def gitlab_markdown?(filename) - Gitlab::MarkupHelper.gitlab_markdown?(filename) - end - - def asciidoc?(filename) - Gitlab::MarkupHelper.asciidoc?(filename) - end - - def promo_host - 'about.gitlab.com' - end - - def promo_url - 'https://' + promo_host - end - - def page_filter_path(options = {}) - without = options.delete(:without) - - exist_opts = { - state: params[:state], - scope: params[:scope], - label_name: params[:label_name], - milestone_id: params[:milestone_id], - assignee_id: params[:assignee_id], - author_id: params[:author_id], - sort: params[:sort], - } - - options = exist_opts.merge(options) - - if without.present? - without.each do |key| - options.delete(key) - end - end - - path = request.path - path << "?#{options.to_param}" - path - end - - def outdated_browser? - browser.ie? && browser.version.to_i < 10 - end - - def path_to_key(key, admin = false) - if admin - admin_user_key_path(@user, key) - else - profile_key_path(key) - end - end - - def state_filters_text_for(entity, project) - titles = { - opened: "Open" - } - - entity_title = titles[entity] || entity.to_s.humanize - - count = - if project.nil? - nil - elsif current_controller?(:issues) - project.issues.send(entity).count - elsif current_controller?(:merge_requests) - project.merge_requests.send(entity).count - end - - html = content_tag :span, entity_title - - if count.present? - html += " " - html += content_tag :span, number_with_delimiter(count), class: 'badge' - end - - html.html_safe - end - end -end diff --git a/app/helpers/gitlab/application_settings_helper.rb b/app/helpers/gitlab/application_settings_helper.rb deleted file mode 100644 index 7132d3dcdad..00000000000 --- a/app/helpers/gitlab/application_settings_helper.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Gitlab - module ApplicationSettingsHelper - def gravatar_enabled? - current_application_settings.gravatar_enabled? - end - - def twitter_sharing_enabled? - current_application_settings.twitter_sharing_enabled? - end - - def signup_enabled? - current_application_settings.signup_enabled? - end - - def signin_enabled? - current_application_settings.signin_enabled? - end - - def extra_sign_in_text - current_application_settings.sign_in_text - end - - def user_oauth_applications? - current_application_settings.user_oauth_applications - end - - # Return a group of checkboxes that use Bootstrap's button plugin for a - # toggle button effect. - def restricted_level_checkboxes(help_block_id) - Gitlab::VisibilityLevel.options.map do |name, level| - checked = restricted_visibility_levels(true).include?(level) - css_class = 'btn' - css_class += ' active' if checked - checkbox_name = 'application_setting[restricted_visibility_levels][]' - - label_tag(checkbox_name, class: css_class) do - check_box_tag(checkbox_name, level, checked, - autocomplete: 'off', - 'aria-describedby' => help_block_id) + name - end - end - end - - # Return a group of checkboxes that use Bootstrap's button plugin for a - # toggle button effect. - def import_sources_checkboxes(help_block_id) - Gitlab::ImportSources.options.map do |name, source| - checked = current_application_settings.import_sources.include?(source) - css_class = 'btn' - css_class += ' active' if checked - checkbox_name = 'application_setting[import_sources][]' - - label_tag(checkbox_name, class: css_class) do - check_box_tag(checkbox_name, source, checked, - autocomplete: 'off', - 'aria-describedby' => help_block_id) + name - end - end - end - end -end diff --git a/app/helpers/gitlab/auth_helper.rb b/app/helpers/gitlab/auth_helper.rb deleted file mode 100644 index fbd52dbca3d..00000000000 --- a/app/helpers/gitlab/auth_helper.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Gitlab - module AuthHelper - PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze - FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze - - def ldap_enabled? - Gitlab.config.ldap.enabled - end - - def provider_has_icon?(name) - PROVIDERS_WITH_ICONS.include?(name.to_s) - end - - def auth_providers - Gitlab::OAuth::Provider.providers - end - - def label_for_provider(name) - Gitlab::OAuth::Provider.label_for(name) - end - - def form_based_provider?(name) - FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s } - end - - def form_based_providers - auth_providers.select { |provider| form_based_provider?(provider) } - end - - def button_based_providers - auth_providers.reject { |provider| form_based_provider?(provider) } - end - - def provider_image_tag(provider, size = 64) - label = label_for_provider(provider) - - if provider_has_icon?(provider) - file_name = "#{provider.to_s.split('_').first}_#{size}.png" - - image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}") - else - label - end - end - - def auth_active?(provider) - current_user.identities.exists?(provider: provider.to_s) - end - - extend self - end -end diff --git a/app/helpers/gitlab/blob_helper.rb b/app/helpers/gitlab/blob_helper.rb deleted file mode 100644 index 8b53ba8b54f..00000000000 --- a/app/helpers/gitlab/blob_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Gitlab - module BlobHelper - def highlight(blob_name, blob_content, nowrap: false, continue: false) - @formatter ||= Rouge::Formatters::HTMLGitlab.new( - nowrap: nowrap, - cssclass: 'code highlight', - lineanchors: true, - lineanchorsid: 'LC' - ) - - begin - @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new - result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe - rescue - @lexer = Rouge::Lexers::PlainText - result = @formatter.format(@lexer.lex(blob_content)).html_safe - end - - result - end - - def no_highlight_files - %w(credits changelog news copying copyright license authors) - end - - def edit_blob_link(project, ref, path, options = {}) - blob = - begin - project.repository.blob_at(ref, path) - rescue - nil - end - - if blob && blob.text? - text = 'Edit' - after = options[:after] || '' - from_mr = options[:from_merge_request_id] - link_opts = {} - link_opts[:from_merge_request_id] = from_mr if from_mr - cls = 'btn btn-small' - if allowed_tree_edit?(project, ref) - link_to(text, - namespace_project_edit_blob_path(project.namespace, project, - tree_join(ref, path), - link_opts), - class: cls - ) - else - content_tag :span, text, class: cls + ' disabled' - end + after.html_safe - else - '' - end - end - - def leave_edit_message - "Leave edit mode?\nAll unsaved changes will be lost." - end - - def editing_preview_title(filename) - if Gitlab::MarkupHelper.previewable?(filename) - 'Preview' - else - 'Preview changes' - end - end - - # Return an image icon depending on the file mode and extension - # - # mode - File unix mode - # mode - File name - def blob_icon(mode, name) - icon("#{file_type_icon_class('file', mode, name)} fw") - end - end -end diff --git a/app/helpers/gitlab/branches_helper.rb b/app/helpers/gitlab/branches_helper.rb deleted file mode 100644 index ecc56002e84..00000000000 --- a/app/helpers/gitlab/branches_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab - module BranchesHelper - def can_remove_branch?(project, branch_name) - if project.protected_branch? branch_name - false - elsif branch_name == project.repository.root_ref - false - else - can?(current_user, :push_code, project) - end - end - - def can_push_branch?(project, branch_name) - return false unless project.repository.branch_names.include?(branch_name) - - ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) - end - end -end diff --git a/app/helpers/gitlab/broadcast_messages_helper.rb b/app/helpers/gitlab/broadcast_messages_helper.rb deleted file mode 100644 index 93f0b0ec5ae..00000000000 --- a/app/helpers/gitlab/broadcast_messages_helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module BroadcastMessagesHelper - def broadcast_styling(broadcast_message) - styling = '' - - if broadcast_message.color.present? - styling << "background-color: #{broadcast_message.color}" - styling << '; ' if broadcast_message.font.present? - end - - if broadcast_message.font.present? - styling << "color: #{broadcast_message.font}" - end - - styling - end - end -end diff --git a/app/helpers/gitlab/commits_helper.rb b/app/helpers/gitlab/commits_helper.rb deleted file mode 100644 index 8a3de838b39..00000000000 --- a/app/helpers/gitlab/commits_helper.rb +++ /dev/null @@ -1,185 +0,0 @@ -# encoding: utf-8 -module Gitlab - module CommitsHelper - # Returns a link to the commit author. If the author has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the author email as specified in the commit. - # - # options: - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_author_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :author)) - end - - # Just like #author_link but for the committer. - def commit_committer_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :committer)) - end - - def image_diff_class(diff) - if diff.deleted_file - "deleted" - elsif diff.new_file - "added" - else - nil - end - end - - def commit_to_html(commit, project, inline = true) - template = inline ? "inline_commit" : "commit" - escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? - end - - # Breadcrumb links for a Project and, if applicable, a tree path - def commits_breadcrumbs - return unless @project && @ref - - # Add the root project link and the arrow icon - crumbs = content_tag(:li) do - link_to( - @project.path, - namespace_project_commits_path(@project.namespace, @project, @ref) - ) - end - - if @path - parts = @path.split('/') - - parts.each_with_index do |part, i| - crumbs << content_tag(:li) do - # The text is just the individual part, but the link needs all the parts before it - link_to( - part, - namespace_project_commits_path( - @project.namespace, - @project, - tree_join(@ref, parts[0..i].join('/')) - ) - ) - end - end - end - - crumbs.html_safe - end - - # Return Project default branch, if it present in array - # Else - first branch in array (mb last actual branch) - def commit_default_branch(project, branches) - branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop - end - - # Returns the sorted alphabetically links to branches, separated by a comma - def commit_branches_links(project, branches) - branches.sort.map do |branch| - link_to( - namespace_project_tree_path(project.namespace, project, branch) - ) do - content_tag :span, class: 'label label-gray' do - icon('code-fork') + ' ' + branch - end - end - end.join(" ").html_safe - end - - # Returns the sorted links to tags, separated by a comma - def commit_tags_links(project, tags) - sorted = VersionSorter.rsort(tags) - sorted.map do |tag| - link_to( - namespace_project_commits_path(project.namespace, project, - project.repository.find_tag(tag).name) - ) do - content_tag :span, class: 'label label-gray' do - icon('tag') + ' ' + tag - end - end - end.join(" ").html_safe - end - - def link_to_browse_code(project, commit) - if current_controller?(:projects, :commits) - if @repo.blob_at(commit.id, @path) - return link_to( - "Browse File »", - namespace_project_blob_path(project.namespace, project, - tree_join(commit.id, @path)), - class: "pull-right" - ) - elsif @path.present? - return link_to( - "Browse Dir »", - namespace_project_tree_path(project.namespace, project, - tree_join(commit.id, @path)), - class: "pull-right" - ) - end - end - link_to( - "Browse Code »", - namespace_project_tree_path(project.namespace, project, commit), - class: "pull-right" - ) - end - - protected - - # Private: Returns a link to a person. If the person has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the person email as specified in the commit. - # - # options: - # source: one of :author or :committer - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_person_link(commit, options = {}) - user = commit.send(options[:source]) - - source_name = clean(commit.send "#{options[:source]}_name".to_sym) - source_email = clean(commit.send "#{options[:source]}_email".to_sym) - - person_name = user.try(:name) || source_name - person_email = user.try(:email) || source_email - - text = - if options[:avatar] - avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") - %Q{#{avatar} #{person_name}} - else - person_name - end - - options = { - class: "commit-#{options[:source]}-link has_tooltip", - data: { :'original-title' => sanitize(source_email) } - } - - if user.nil? - mail_to(source_email, text.html_safe, options) - else - link_to(text.html_safe, user_path(user), options) - end - end - - def view_file_btn(commit_sha, diff, project) - link_to( - namespace_project_blob_path(project.namespace, project, - tree_join(commit_sha, diff.new_path)), - class: 'btn btn-small view-file js-view-file' - ) do - raw('View file @') + content_tag(:span, commit_sha[0..6], - class: 'commit-short-id') - end - end - - def truncate_sha(sha) - Commit.truncate_sha(sha) - end - - def clean(string) - Sanitize.clean(string, remove_contents: true) - end - end -end diff --git a/app/helpers/gitlab/compare_helper.rb b/app/helpers/gitlab/compare_helper.rb deleted file mode 100644 index 407d25d3102..00000000000 --- a/app/helpers/gitlab/compare_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - module CompareHelper - def create_mr_button?(from = params[:from], to = params[:to], project = @project) - from.present? && - to.present? && - from != to && - project.merge_requests_enabled && - project.repository.branch_names.include?(from) && - project.repository.branch_names.include?(to) - end - - def create_mr_path(from = params[:from], to = params[:to], project = @project) - new_namespace_project_merge_request_path( - project.namespace, - project, - merge_request: { - source_branch: to, - target_branch: from - } - ) - end - end -end diff --git a/app/helpers/gitlab/dashboard_helper.rb b/app/helpers/gitlab/dashboard_helper.rb deleted file mode 100644 index 2211c93999e..00000000000 --- a/app/helpers/gitlab/dashboard_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Gitlab - module DashboardHelper - def assigned_issues_dashboard_path - issues_dashboard_path(assignee_id: current_user.id) - end - - def assigned_mrs_dashboard_path - merge_requests_dashboard_path(assignee_id: current_user.id) - end - end -end diff --git a/app/helpers/gitlab/diff_helper.rb b/app/helpers/gitlab/diff_helper.rb deleted file mode 100644 index 02907eb80f3..00000000000 --- a/app/helpers/gitlab/diff_helper.rb +++ /dev/null @@ -1,172 +0,0 @@ -module Gitlab - module DiffHelper - def allowed_diff_size - if diff_hard_limit_enabled? - Commit::DIFF_HARD_LIMIT_FILES - else - Commit::DIFF_SAFE_FILES - end - end - - def allowed_diff_lines - if diff_hard_limit_enabled? - Commit::DIFF_HARD_LIMIT_LINES - else - Commit::DIFF_SAFE_LINES - end - end - - def safe_diff_files(diffs) - lines = 0 - safe_files = [] - diffs.first(allowed_diff_size).each do |diff| - lines += diff.diff.lines.count - break if lines > allowed_diff_lines - safe_files << Gitlab::Diff::File.new(diff) - end - safe_files - end - - def diff_hard_limit_enabled? - # Enabling hard limit allows user to see more diff information - if params[:force_show_diff].present? - true - else - false - end - end - - def generate_line_code(file_path, line) - Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) - end - - def parallel_diff(diff_file, index) - lines = [] - skip_next = false - - # Building array of lines - # - # [ - # left_type, left_line_number, left_line_content, left_line_code, - # right_line_type, right_line_number, right_line_content, right_line_code - # ] - # - diff_file.diff_lines.each do |line| - - full_line = line.text - type = line.type - line_code = generate_line_code(diff_file.file_path, line) - line_new = line.new_pos - line_old = line.old_pos - - next_line = diff_file.next_line(line.index) - - if next_line - next_line_code = generate_line_code(diff_file.file_path, next_line) - next_type = next_line.type - next_line = next_line.text - end - - if type == 'match' || type.nil? - # line in the right panel is the same as in the left one - line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code] - lines.push(line) - elsif type == 'old' - if next_type == 'new' - # Left side has text removed, right side has text added - line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code] - lines.push(line) - skip_next = true - elsif next_type == 'old' || next_type.nil? - # Left side has text removed, right side doesn't have any change - # No next line code, no new line number, no new line text - line = [type, line_old, full_line, line_code, next_type, nil, " ", nil] - lines.push(line) - end - elsif type == 'new' - if skip_next - # Change has been already included in previous line so no need to do it again - skip_next = false - next - else - # Change is only on the right side, left side has no change - line = [nil, nil, " ", line_code, type, line_new, full_line, line_code] - lines.push(line) - end - end - end - lines - end - - def unfold_bottom_class(bottom) - (bottom) ? 'js-unfold-bottom' : '' - end - - def unfold_class(unfold) - (unfold) ? 'unfold js-unfold' : '' - end - - def diff_line_content(line) - if line.blank? - "  " - else - line - end - end - - def line_comments - @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code) - end - - def organize_comments(type_left, type_right, line_code_left, line_code_right) - comments_left = comments_right = nil - - unless type_left.nil? && type_right == 'new' - comments_left = line_comments[line_code_left] - end - - unless type_left.nil? && type_right.nil? - comments_right = line_comments[line_code_right] - end - - [comments_left, comments_right] - end - - def inline_diff_btn - params_copy = params.dup - params_copy[:view] = 'inline' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do - 'Inline' - end - end - - def parallel_diff_btn - params_copy = params.dup - params_copy[:view] = 'parallel' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do - 'Side-by-side' - end - end - - def submodule_link(blob, ref, repository = @repository) - tree, commit = submodule_links(blob, ref, repository) - commit_id = if commit.nil? - blob.id[0..10] - else - link_to "#{blob.id[0..10]}", commit - end - - [ - content_tag(:span, link_to(truncate(blob.name, length: 40), tree)), - '@', - content_tag(:span, commit_id, class: 'monospace'), - ].join(' ').html_safe - end - end -end diff --git a/app/helpers/gitlab/emails_helper.rb b/app/helpers/gitlab/emails_helper.rb deleted file mode 100644 index 84f106dd536..00000000000 --- a/app/helpers/gitlab/emails_helper.rb +++ /dev/null @@ -1,59 +0,0 @@ -module Gitlab - module EmailsHelper - - # Google Actions - # https://developers.google.com/gmail/markup/reference/go-to-action - def email_action(url) - name = action_title(url) - if name - data = { - "@context" => "http://schema.org", - "@type" => "EmailMessage", - "action" => { - "@type" => "ViewAction", - "name" => name, - "url" => url, - } - } - - content_tag :script, type: 'application/ld+json' do - data.to_json.html_safe - end - end - end - - def action_title(url) - return unless url - ["merge_requests", "issues", "commit"].each do |action| - if url.split("/").include?(action) - return "View #{action.humanize.singularize}" - end - end - end - - def color_email_diff(diffcontent) - formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github') - lexer = Rouge::Lexers::Diff - raw formatter.format(lexer.lex(diffcontent)) - end - - def password_reset_token_valid_time - valid_hours = Devise.reset_password_within / 60 / 60 - if valid_hours >= 24 - unit = 'day' - valid_length = (valid_hours / 24).floor - else - unit = 'hour' - valid_length = valid_hours.floor - end - - pluralize(valid_length, unit) - end - - def reset_token_expire_message - link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email)) - msg = "This link is valid for #{password_reset_token_valid_time}. " - msg << "After it expires, you can #{link_tag}." - end - end -end diff --git a/app/helpers/gitlab/events_helper.rb b/app/helpers/gitlab/events_helper.rb deleted file mode 100644 index 65522dae533..00000000000 --- a/app/helpers/gitlab/events_helper.rb +++ /dev/null @@ -1,205 +0,0 @@ -module Gitlab - module EventsHelper - def link_to_author(event) - author = event.author - - if author - link_to author.name, user_path(author.username) - else - event.author_name - end - end - - def event_action_name(event) - target = if event.target_type - if event.note? - event.note_target_type - else - event.target_type.titleize.downcase - end - else - 'project' - end - - [event.action_name, target].join(" ") - end - - def event_filter_link(key, tooltip) - key = key.to_s - active = 'active' if @event_filter.active?(key) - link_opts = { - class: 'event_filter_link', - id: "#{key}_event_filter", - title: "Filter by #{tooltip.downcase}", - data: { toggle: 'tooltip', placement: 'top' } - } - - content_tag :li, class: "filter_icon #{active}" do - link_to request.path, link_opts do - icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip) - end - end - end - - def icon_for_event - { - EventFilter.push => 'upload', - EventFilter.merged => 'check-square-o', - EventFilter.comments => 'comments', - EventFilter.team => 'user', - } - end - - def event_feed_title(event) - words = [] - words << event.author_name - words << event_action_name(event) - - if event.push? - words << event.ref_type - words << event.ref_name - words << "at" - elsif event.commented? - if event.note_commit? - words << event.note_short_commit_id - else - words << "##{truncate event.note_target_iid}" - end - words << "at" - elsif event.target - words << "##{event.target_iid}:" - words << event.target.title if event.target.respond_to?(:title) - words << "at" - end - - words << event.project_name - - words.join(" ") - end - - def event_feed_url(event) - if event.issue? - namespace_project_issue_url(event.project.namespace, event.project, - event.issue) - elsif event.merge_request? - namespace_project_merge_request_url(event.project.namespace, - event.project, event.merge_request) - elsif event.note? && event.note_commit? - namespace_project_commit_url(event.project.namespace, event.project, - event.note_target) - elsif event.note? - if event.note_target - if event.note_commit? - namespace_project_commit_path(event.project.namespace, event.project, - event.note_commit_id, - anchor: dom_id(event.target)) - elsif event.note_project_snippet? - namespace_project_snippet_path(event.project.namespace, - event.project, event.note_target) - else - event_note_target_path(event) - end - end - elsif event.push? - if event.push_with_commits? && event.md_ref? - if event.commits_count > 1 - namespace_project_compare_url(event.project.namespace, event.project, - from: event.commit_from, to: - event.commit_to) - else - namespace_project_commit_url(event.project.namespace, event.project, - id: event.commit_to) - end - else - namespace_project_commits_url(event.project.namespace, event.project, - event.ref_name) - end - end - end - - def event_feed_summary(event) - if event.issue? - render "events/event_issue", issue: event.issue - elsif event.push? - render "events/event_push", event: event - elsif event.merge_request? - render "events/event_merge_request", merge_request: event.merge_request - elsif event.note? - render "events/event_note", note: event.note - end - end - - def event_note_target_path(event) - if event.note? && event.note_commit? - namespace_project_commit_path(event.project.namespace, event.project, - event.note_target) - else - polymorphic_path([event.project.namespace.becomes(Namespace), - event.project, event.note_target], - anchor: dom_id(event.target)) - end - end - - def event_note_title_html(event) - if event.note_target - if event.note_commit? - link_to( - namespace_project_commit_path(event.project.namespace, event.project, - event.note_commit_id, - anchor: dom_id(event.target)), - class: "commit_short_id" - ) do - "#{event.note_target_type} #{event.note_short_commit_id}" - end - elsif event.note_project_snippet? - link_to(namespace_project_snippet_path(event.project.namespace, - event.project, - event.note_target)) do - "#{event.note_target_type} ##{truncate event.note_target_id}" - end - else - link_to event_note_target_path(event) do - "#{event.note_target_type} ##{truncate event.note_target_iid}" - end - end - else - content_tag :strong do - "(deleted)" - end - end - end - - def event_note(text, options = {}) - text = first_line_in_markdown(text, 150, options) - sanitize(text, tags: %w(a img b pre code p span)) - end - - def event_commit_title(message) - escape_once(truncate(message.split("\n").first, length: 70)) - rescue - "--broken encoding" - end - - def event_to_atom(xml, event) - if event.proper? - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - event_summary = event_feed_summary(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link href: event_link - xml.title truncate(event_title, length: 80) - xml.updated event.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - - xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } - end - end - end - end -end diff --git a/app/helpers/gitlab/explore_helper.rb b/app/helpers/gitlab/explore_helper.rb deleted file mode 100644 index b8e0f482b94..00000000000 --- a/app/helpers/gitlab/explore_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab - module ExploreHelper - def explore_projects_filter_path(options={}) - exist_opts = { - sort: params[:sort], - scope: params[:scope], - group: params[:group], - tag: params[:tag], - visibility_level: params[:visibility_level], - } - - options = exist_opts.merge(options) - - path = explore_projects_path - path << "?#{options.to_param}" - path - end - end -end diff --git a/app/helpers/gitlab/external_wiki_helper.rb b/app/helpers/gitlab/external_wiki_helper.rb deleted file mode 100644 index 710cdc727d0..00000000000 --- a/app/helpers/gitlab/external_wiki_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module ExternalWikiHelper - def get_project_wiki_path(project) - external_wiki_service = project.services. - select { |service| service.to_param == 'external_wiki' }.first - if external_wiki_service.present? && external_wiki_service.active? - external_wiki_service.properties['external_wiki_url'] - else - namespace_project_wiki_path(project.namespace, project, :home) - end - end - end -end diff --git a/app/helpers/gitlab/git_helper.rb b/app/helpers/gitlab/git_helper.rb deleted file mode 100644 index 867b30b8c74..00000000000 --- a/app/helpers/gitlab/git_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Gitlab - module GitHelper - def strip_gpg_signature(text) - text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") - end - end -end diff --git a/app/helpers/gitlab/gitlab_markdown_helper.rb b/app/helpers/gitlab/gitlab_markdown_helper.rb deleted file mode 100644 index 265cb4672fe..00000000000 --- a/app/helpers/gitlab/gitlab_markdown_helper.rb +++ /dev/null @@ -1,195 +0,0 @@ -require 'nokogiri' - -module Gitlab - module GitlabMarkdownHelper - include Gitlab::Markdown - include PreferencesHelper - - # Use this in places where you would normally use link_to(gfm(...), ...). - # - # It solves a problem occurring with nested links (i.e. - # "outer text gfm ref more outer text"). This will not be - # interpreted as intended. Browsers will parse something like - # "outer text gfm ref more outer text" (notice the last part is - # not linked any more). link_to_gfm corrects that. It wraps all parts to - # explicitly produce the correct linking behavior (i.e. - # "outer text gfm ref more outer text"). - def link_to_gfm(body, url, html_options = {}) - return "" if body.blank? - - escaped_body = if body =~ /\A\ at the beginning of a line", - "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___" - ].freeze - - # Returns a random markdown tip for use as a textarea placeholder - def random_markdown_tip - MARKDOWN_TIPS.sample - end - - private - - # Return +text+, truncated to +max_chars+ characters, excluding any HTML - # tags. - def truncate_visible(text, max_chars) - doc = Nokogiri::HTML.fragment(text) - content_length = 0 - truncated = false - - doc.traverse do |node| - if node.text? || node.content.empty? - if truncated - node.remove - next - end - - # Handle line breaks within a node - if node.content.strip.lines.length > 1 - node.content = "#{node.content.lines.first.chomp}..." - truncated = true - end - - num_remaining = max_chars - content_length - if node.content.length > num_remaining - node.content = node.content.truncate(num_remaining) - truncated = true - end - content_length += node.content.length - end - - truncated = truncate_if_block(node, truncated) - end - - doc.to_html - end - - # Used by #truncate_visible. If +node+ is the first block element, and the - # text hasn't already been truncated, then append "..." to the node contents - # and return true. Otherwise return false. - def truncate_if_block(node, truncated) - if node.element? && node.description.block? && !truncated - node.content = "#{node.content}..." if node.next_sibling - true - else - truncated - end - end - - # Returns the text necessary to reference `entity` across projects - # - # project - Project to reference - # entity - Object that responds to `to_reference` - # - # Examples: - # - # cross_project_reference(project, project.issues.first) - # # => 'namespace1/project1#123' - # - # cross_project_reference(project, project.merge_requests.first) - # # => 'namespace1/project1!345' - # - # Returns a String - def cross_project_reference(project, entity) - if entity.respond_to?(:to_reference) - "#{project.to_reference}#{entity.to_reference}" - else - '' - end - end - end -end diff --git a/app/helpers/gitlab/gitlab_routing_helper.rb b/app/helpers/gitlab/gitlab_routing_helper.rb deleted file mode 100644 index 7f1e455d5de..00000000000 --- a/app/helpers/gitlab/gitlab_routing_helper.rb +++ /dev/null @@ -1,69 +0,0 @@ -# Shorter routing method for project and project items -# Since update to rails 4.1.9 we are now allowed to use `/` in project routing -# so we use nested routing for project resources which include project and -# project namespace. To avoid writing long methods every time we define shortcuts for -# some of routing. -# -# For example instead of this: -# -# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) -# -# We can simply use shortcut: -# -# merge_request_path(merge_request) -# -module Gitlab - module GitlabRoutingHelper - def project_path(project, *args) - namespace_project_path(project.namespace, project, *args) - end - - def activity_project_path(project, *args) - activity_namespace_project_path(project.namespace, project, *args) - end - - def edit_project_path(project, *args) - edit_namespace_project_path(project.namespace, project, *args) - end - - def issue_path(entity, *args) - namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) - end - - def merge_request_path(entity, *args) - namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) - end - - def milestone_path(entity, *args) - namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) - end - - def project_url(project, *args) - namespace_project_url(project.namespace, project, *args) - end - - def edit_project_url(project, *args) - edit_namespace_project_url(project.namespace, project, *args) - end - - def issue_url(entity, *args) - namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) - end - - def merge_request_url(entity, *args) - namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) - end - - def project_snippet_url(entity, *args) - namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) - end - - def toggle_subscription_path(entity, *args) - if entity.is_a?(Issue) - toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity) - else - toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity) - end - end - end -end diff --git a/app/helpers/gitlab/graph_helper.rb b/app/helpers/gitlab/graph_helper.rb deleted file mode 100644 index 047f5c19095..00000000000 --- a/app/helpers/gitlab/graph_helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module GraphHelper - def get_refs(repo, commit) - refs = "" - refs << commit.ref_names(repo).join(' ') - - # append note count - refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 - - refs - end - - def parents_zip_spaces(parents, parent_spaces) - ids = parents.map { |p| p.id } - ids.zip(parent_spaces) - end - end -end diff --git a/app/helpers/gitlab/groups_helper.rb b/app/helpers/gitlab/groups_helper.rb deleted file mode 100644 index 8172c617249..00000000000 --- a/app/helpers/gitlab/groups_helper.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Gitlab - module GroupsHelper - def remove_user_from_group_message(group, member) - if member.user - "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?" - else - "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?" - end - end - - def leave_group_message(group) - "Are you sure you want to leave \"#{group}\" group?" - end - - def should_user_see_group_roles?(user, group) - if user - user.is_admin? || group.members.exists?(user_id: user.id) - else - false - end - end - - def group_icon(group) - if group.is_a?(String) - group = Group.find_by(path: group) - end - - if group && group.avatar.present? - group.avatar.url - else - image_path('no_group_avatar.png') - end - end - end -end diff --git a/app/helpers/gitlab/icons_helper.rb b/app/helpers/gitlab/icons_helper.rb deleted file mode 100644 index e815d237bb1..00000000000 --- a/app/helpers/gitlab/icons_helper.rb +++ /dev/null @@ -1,87 +0,0 @@ -module Gitlab - module IconsHelper - include FontAwesome::Rails::IconHelper - - # Creates an icon tag given icon name(s) and possible icon modifiers. - # - # Right now this method simply delegates directly to `fa_icon` from the - # font-awesome-rails gem, but should we ever use a different icon pack in the - # future we won't have to change hundreds of method calls. - def icon(names, options = {}) - fa_icon(names, options) - end - - def spinner(text = nil, visible = false) - css_class = 'loading' - css_class << ' hide' unless visible - - content_tag :div, class: css_class do - icon('spinner spin') + text - end - end - - def boolean_to_icon(value) - if value - icon('circle', class: 'cgreen') - else - icon('power-off', class: 'clgray') - end - end - - def public_icon - icon('globe fw') - end - - def internal_icon - icon('shield fw') - end - - def private_icon - icon('lock fw') - end - - def file_type_icon_class(type, mode, name) - if type == 'folder' - icon_class = 'folder' - elsif mode == '120000' - icon_class = 'share' - else - # Guess which icon to choose based on file extension. - # If you think a file extension is missing, feel free to add it on PR - - case File.extname(name).downcase - when '.pdf' - icon_class = 'file-pdf-o' - when '.jpg', '.jpeg', '.jif', '.jfif', - '.jp2', '.jpx', '.j2k', '.j2c', - '.png', '.gif', '.tif', '.tiff', - '.svg', '.ico', '.bmp' - icon_class = 'file-image-o' - when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip', - '.xz', '.rar', '.7z' - icon_class = 'file-archive-o' - when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac' - icon_class = 'file-audio-o' - when '.mp4', '.m4p', '.m4v', - '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', - '.mpg', '.mpeg', '.m2v', - '.avi', '.mkv', '.flv', '.ogv', '.mov', - '.3gp', '.3g2' - icon_class = 'file-video-o' - when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb' - icon_class = 'file-word-o' - when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm', - '.xlsb', '.xla', '.xlam', '.xll', '.xlw' - icon_class = 'file-excel-o' - when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm', - '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm' - icon_class = 'file-powerpoint-o' - else - icon_class = 'file-text-o' - end - end - - icon_class - end - end -end diff --git a/app/helpers/gitlab/issues_helper.rb b/app/helpers/gitlab/issues_helper.rb deleted file mode 100644 index 67238926555..00000000000 --- a/app/helpers/gitlab/issues_helper.rb +++ /dev/null @@ -1,90 +0,0 @@ -module Gitlab - module IssuesHelper - def issue_css_classes(issue) - classes = "issue" - classes << " closed" if issue.closed? - classes << " today" if issue.today? - classes - end - - # Returns an OpenStruct object suitable for use by options_from_collection_for_select - # to allow filtering issues by an unassigned User or Milestone - def unassigned_filter - # Milestone uses :title, Issue uses :name - OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned') - end - - def url_for_project_issues(project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.project_path - else - project.issues_tracker.project_url - end - end - - def url_for_new_issue(project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.new_issue_path - else - project.issues_tracker.new_issue_url - end - end - - def url_for_issue(issue_iid, project = @project, options = {}) - return '' if project.nil? - - if options[:only_path] - project.issues_tracker.issue_path(issue_iid) - else - project.issues_tracker.issue_url(issue_iid) - end - end - - def bulk_update_milestone_options - options_for_select([['None (backlog)', -1]]) + - options_from_collection_for_select(project_active_milestones, 'id', - 'title', params[:milestone_id]) - end - - def milestone_options(object) - options_from_collection_for_select(object.project.milestones.active, - 'id', 'title', object.milestone_id) - end - - def issue_box_class(item) - if item.respond_to?(:expired?) && item.expired? - 'issue-box-expired' - elsif item.respond_to?(:merged?) && item.merged? - 'issue-box-merged' - elsif item.closed? - 'issue-box-closed' - else - 'issue-box-open' - end - end - - def issue_to_atom(xml, issue) - xml.entry do - xml.id namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.link href: namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.title truncate(issue.title, length: 80) - xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end - end - - # Required for Gitlab::Markdown::IssueReferenceFilter - module_function :url_for_issue - end -end diff --git a/app/helpers/gitlab/labels_helper.rb b/app/helpers/gitlab/labels_helper.rb deleted file mode 100644 index aa16d71f42c..00000000000 --- a/app/helpers/gitlab/labels_helper.rb +++ /dev/null @@ -1,103 +0,0 @@ -module Gitlab - module LabelsHelper - include ActionView::Helpers::TagHelper - - # Link to a Label - # - # label - Label object to link to - # project - Project object which will be used as the context for the label's - # link. If omitted, defaults to `@project`, or the label's own - # project. - # block - An optional block that will be passed to `link_to`, forming the - # body of the link element. If omitted, defaults to - # `render_colored_label`. - # - # Examples: - # - # # Allow the generated link to use the label's own project - # link_to_label(label) - # - # # Force the generated link to use @project - # @project = Project.first - # link_to_label(label) - # - # # Force the generated link to use a provided project - # link_to_label(label, project: Project.last) - # - # # Customize link body with a block - # link_to_label(label) { "My Custom Label Text" } - # - # Returns a String - def link_to_label(label, project: nil, &block) - project ||= @project || label.project - link = namespace_project_issues_path(project.namespace, project, - label_name: label.name) - - if block_given? - link_to link, &block - else - link_to render_colored_label(label), link - end - end - - def project_label_names - @project.labels.pluck(:title) - end - - def render_colored_label(label) - label_color = label.color || Label::DEFAULT_COLOR - text_color = text_color_for_bg(label_color) - - # Intentionally not using content_tag here so that this method can be called - # by LabelReferenceFilter - span = %() + - escape_once(label.name) + '' - - span.html_safe - end - - def suggested_colors - [ - '#0033CC', - '#428BCA', - '#44AD8E', - '#A8D695', - '#5CB85C', - '#69D100', - '#004E00', - '#34495E', - '#7F8C8D', - '#A295D6', - '#5843AD', - '#8E44AD', - '#FFECDB', - '#AD4363', - '#D10069', - '#CC0033', - '#FF0000', - '#D9534F', - '#D1D100', - '#F0AD4E', - '#AD8D43' - ] - end - - def text_color_for_bg(bg_color) - r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex) - - if (r + g + b) > 500 - '#333333' - else - '#FFFFFF' - end - end - - def project_labels_options(project) - options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) - end - - # Required for Gitlab::Markdown::LabelReferenceFilter - module_function :render_colored_label, :text_color_for_bg, :escape_once - end -end diff --git a/app/helpers/gitlab/merge_requests_helper.rb b/app/helpers/gitlab/merge_requests_helper.rb deleted file mode 100644 index 361f6b2fdac..00000000000 --- a/app/helpers/gitlab/merge_requests_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Gitlab - module MergeRequestsHelper - def new_mr_path_from_push_event(event) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project) - ) - end - - def new_mr_path_for_fork_from_push_event(event) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project) - ) - end - - def new_mr_from_push_event(event, target_project) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_project.repository.root_ref - } - } - end - - def mr_css_classes(mr) - classes = "merge-request" - classes << " closed" if mr.closed? - classes << " merged" if mr.merged? - classes - end - - def ci_build_details_path(merge_request) - merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) - end - - def merge_path_description(merge_request, separator) - if merge_request.for_fork? - "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" - else - "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" - end - end - - def issues_sentence(issues) - issues.map { |i| "##{i.iid}" }.to_sentence - end - - def mr_change_branches_path(merge_request) - new_namespace_project_merge_request_path( - @project.namespace, @project, - merge_request: { - source_project_id: @merge_request.source_project_id, - target_project_id: @merge_request.target_project_id, - source_branch: @merge_request.source_branch, - target_branch: nil - } - ) - end - - def source_branch_with_namespace(merge_request) - if merge_request.for_fork? - namespace = link_to(merge_request.source_project_namespace, - project_path(merge_request.source_project)) - namespace + ":#{merge_request.source_branch}" - else - merge_request.source_branch - end - end - end -end diff --git a/app/helpers/gitlab/milestones_helper.rb b/app/helpers/gitlab/milestones_helper.rb deleted file mode 100644 index 116967d4946..00000000000 --- a/app/helpers/gitlab/milestones_helper.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Gitlab - module MilestonesHelper - def milestones_filter_path(opts = {}) - if @project - namespace_project_milestones_path(@project.namespace, @project, opts) - elsif @group - group_milestones_path(@group, opts) - else - dashboard_milestones_path(opts) - end - end - - def milestone_progress_bar(milestone) - options = { - class: 'progress-bar progress-bar-success', - style: "width: #{milestone.percent_complete}%;" - } - - content_tag :div, class: 'progress' do - content_tag :div, nil, options - end - end - - def projects_milestones_options - milestones = - if @project - @project.milestones - else - Milestone.where(project_id: @projects) - end.active - - grouped_milestones = Milestones::GroupService.new(milestones).execute - grouped_milestones.unshift(Milestone::None) - - options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) - end - end -end diff --git a/app/helpers/gitlab/namespaces_helper.rb b/app/helpers/gitlab/namespaces_helper.rb deleted file mode 100644 index b1caaac3f63..00000000000 --- a/app/helpers/gitlab/namespaces_helper.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Gitlab - module NamespacesHelper - def namespaces_options(selected = :current_user, scope = :default) - groups = current_user.owned_groups + current_user.masters_groups - users = [current_user.namespace] - - group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] - users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] - - options = [] - options << group_opts - options << users_opts - - if selected == :current_user && current_user.namespace - selected = current_user.namespace.id - end - - grouped_options_for_select(options, selected) - end - - def namespace_select_tag(id, opts = {}) - css_class = "ajax-namespace-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end - - def namespace_icon(namespace, size = 40) - if namespace.kind_of?(Group) - group_icon(namespace) - else - avatar_icon(namespace.owner.email, size) - end - end - end -end diff --git a/app/helpers/gitlab/nav_helper.rb b/app/helpers/gitlab/nav_helper.rb deleted file mode 100644 index 14106d70840..00000000000 --- a/app/helpers/gitlab/nav_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - module NavHelper - def nav_menu_collapsed? - cookies[:collapsed_nav] == 'true' - end - - def nav_sidebar_class - if nav_menu_collapsed? - "page-sidebar-collapsed" - else - "page-sidebar-expanded" - end - end - - def nav_header_class - if nav_menu_collapsed? - "header-collapsed" - else - "header-expanded" - end - end - end -end diff --git a/app/helpers/gitlab/notes_helper.rb b/app/helpers/gitlab/notes_helper.rb deleted file mode 100644 index 15076148b02..00000000000 --- a/app/helpers/gitlab/notes_helper.rb +++ /dev/null @@ -1,78 +0,0 @@ -module Gitlab - module NotesHelper - # Helps to distinguish e.g. commit notes in mr notes list - def note_for_main_target?(note) - (@noteable.class.name == note.noteable_type && !note.for_diff_line?) - end - - def note_target_fields(note) - hidden_field_tag(:target_type, note.noteable.class.name.underscore) + - hidden_field_tag(:target_id, note.noteable.id) - end - - def note_editable?(note) - note.editable? && can?(current_user, :admin_note, note) - end - - def link_to_commit_diff_line_note(note) - if note.for_commit_diff_line? - link_to( - "#{note.diff_file_name}:L#{note.diff_new_line}", - namespace_project_commit_path(@project.namespace, @project, - note.noteable, anchor: note.line_code) - ) - end - end - - def noteable_json(noteable) - { - id: noteable.id, - class: noteable.class.name, - resources: noteable.class.table_name, - project_id: noteable.project.id, - }.to_json - end - - def link_to_new_diff_note(line_code, line_type = nil) - discussion_id = Note.build_discussion_id( - @comments_target[:noteable_type], - @comments_target[:noteable_id] || @comments_target[:commit_id], - line_code - ) - - data = { - noteable_type: @comments_target[:noteable_type], - noteable_id: @comments_target[:noteable_id], - commit_id: @comments_target[:commit_id], - line_code: line_code, - discussion_id: discussion_id, - line_type: line_type - } - - button_tag(class: 'btn add-diff-note js-add-diff-note-button', - data: data, - title: 'Add a comment to this line') do - icon('comment-o') - end - end - - def link_to_reply_diff(note, line_type = nil) - return unless current_user - - data = { - noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - line_code: note.line_code, - discussion_id: note.discussion_id, - line_type: line_type - } - - button_tag class: 'btn reply-btn js-discussion-reply-button', - data: data, title: 'Add a reply' do - link_text = icon('comment') - link_text << ' Reply' - end - end - end -end diff --git a/app/helpers/gitlab/notifications_helper.rb b/app/helpers/gitlab/notifications_helper.rb deleted file mode 100644 index b6324044ab1..00000000000 --- a/app/helpers/gitlab/notifications_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Gitlab - module NotificationsHelper - include IconsHelper - - def notification_icon(notification) - if notification.disabled? - icon('volume-off', class: 'ns-mute') - elsif notification.participating? - icon('volume-down', class: 'ns-part') - elsif notification.watch? - icon('volume-up', class: 'ns-watch') - else - icon('circle-o', class: 'ns-default') - end - end - end -end diff --git a/app/helpers/gitlab/page_layout_helper.rb b/app/helpers/gitlab/page_layout_helper.rb deleted file mode 100644 index c89b2c2ba2c..00000000000 --- a/app/helpers/gitlab/page_layout_helper.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Gitlab - module PageLayoutHelper - def page_title(*titles) - @page_title ||= [] - - @page_title.push(*titles.compact) if titles.any? - - @page_title.join(" | ") - end - - def header_title(title = nil, title_url = nil) - if title - @header_title = title - @header_title_url = title_url - else - @header_title_url ? link_to(@header_title, @header_title_url) : @header_title - end - end - - def sidebar(name = nil) - if name - @sidebar = name - else - @sidebar - end - end - - def fluid_layout(enabled = false) - if @fluid_layout.nil? - @fluid_layout = enabled - else - @fluid_layout - end - end - end - -end diff --git a/app/helpers/gitlab/preferences_helper.rb b/app/helpers/gitlab/preferences_helper.rb deleted file mode 100644 index 3eac5d51acd..00000000000 --- a/app/helpers/gitlab/preferences_helper.rb +++ /dev/null @@ -1,67 +0,0 @@ -module Gitlab - # Helper methods for per-User preferences - module PreferencesHelper - COLOR_SCHEMES = { - 1 => 'white', - 2 => 'dark', - 3 => 'solarized-light', - 4 => 'solarized-dark', - 5 => 'monokai', - } - COLOR_SCHEMES.default = 'white' - - # Helper method to access the COLOR_SCHEMES - # - # The keys are the `color_scheme_ids` - # The values are the `name` of the scheme. - # - # The preview images are `name-scheme-preview.png` - # The stylesheets should use the css class `.name` - def color_schemes - COLOR_SCHEMES.freeze - end - - # Maps `dashboard` values to more user-friendly option text - DASHBOARD_CHOICES = { - projects: 'Your Projects (default)', - stars: 'Starred Projects' - }.with_indifferent_access.freeze - - # Returns an Array usable by a select field for more user-friendly option text - def dashboard_choices - defined = User.dashboards - - if defined.size != DASHBOARD_CHOICES.size - # Ensure that anyone adding new options updates this method too - raise RuntimeError, "`User` defines #{defined.size} dashboard choices," + - " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}." - else - defined.map do |key, _| - # Use `fetch` so `KeyError` gets raised when a key is missing - [DASHBOARD_CHOICES.fetch(key), key] - end - end - end - - def project_view_choices - [ - ['Readme (default)', :readme], - ['Activity view', :activity] - ] - end - - def user_application_theme - theme = Gitlab::Themes.by_id(current_user.try(:theme_id)) - theme.css_class - end - - def user_color_scheme_class - COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) - end - - def prefer_readme? - !current_user || - current_user.project_view == 'readme' - end - end -end diff --git a/app/helpers/gitlab/projects_helper.rb b/app/helpers/gitlab/projects_helper.rb deleted file mode 100644 index 8a8cd6048df..00000000000 --- a/app/helpers/gitlab/projects_helper.rb +++ /dev/null @@ -1,332 +0,0 @@ -module Gitlab - module ProjectsHelper - def remove_from_project_team_message(project, member) - if member.user - "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?" - else - "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?" - end - end - - def link_to_project(project) - link_to [project.namespace.becomes(Namespace), project] do - title = content_tag(:span, project.name, class: 'project-name') - - if project.namespace - namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') - title = namespace + title - end - - title - end - end - - def link_to_member(project, author, opts = {}) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } - opts = default_opts.merge(opts) - - return "(deleted)" unless author - - author_html = "" - - # Build avatar image tag - author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] - - # Build name span tag - author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] - - author_html = author_html.html_safe - - if opts[:name] - link_to(author_html, user_path(author), class: "author_link").html_safe - else - link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe - end - end - - def project_title(project) - if project.group - content_tag :span do - link_to( - simple_sanitize(project.group.name), group_path(project.group) - ) + ' / ' + - link_to(simple_sanitize(project.name), - project_path(project)) - end - else - owner = project.namespace.owner - content_tag :span do - link_to( - simple_sanitize(owner.name), user_path(owner) - ) + ' / ' + - link_to(simple_sanitize(project.name), - project_path(project)) - end - end - end - - def remove_project_message(project) - "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" - end - - def transfer_project_message(project) - "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" - end - - def project_nav_tabs - @nav_tabs ||= get_project_nav_tabs(@project, current_user) - end - - def project_nav_tab?(name) - project_nav_tabs.include? name - end - - def project_active_milestones - @project.milestones.active.order("due_date, title ASC") - end - - def project_for_deploy_key(deploy_key) - if deploy_key.projects.include?(@project) - @project - else - deploy_key.projects.find { |project| can?(current_user, :read_project, project) } - end - end - - def can_change_visibility_level?(project, current_user) - return false unless can?(current_user, :change_visibility_level, project) - - if project.forked? - project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE - else - true - end - end - - private - - def get_project_nav_tabs(project, current_user) - nav_tabs = [:home] - - if !project.empty_repo? && can?(current_user, :download_code, project) - nav_tabs << [:files, :commits, :network, :graphs] - end - - if project.repo_exists? && can?(current_user, :read_merge_request, project) - nav_tabs << :merge_requests - end - - if can?(current_user, :admin_project, project) - nav_tabs << :settings - end - - if can?(current_user, :read_issue, project) - nav_tabs << :issues - end - - if can?(current_user, :read_wiki, project) - nav_tabs << :wiki - end - - if can?(current_user, :read_project_snippet, project) - nav_tabs << :snippets - end - - if can?(current_user, :read_label, project) - nav_tabs << :labels - end - - if can?(current_user, :read_milestone, project) - nav_tabs << :milestones - end - - nav_tabs.flatten - end - - def git_user_name - if current_user - current_user.name - else - "Your name" - end - end - - def git_user_email - if current_user - current_user.email - else - "your@email.com" - end - end - - def repository_size(project = nil) - "#{(project || @project).repository_size} MB" - rescue - # In order to prevent 500 error - # when application cannot allocate memory - # to calculate repo size - just show 'Unknown' - 'unknown' - end - - def default_url_to_repo(project = nil) - project = project || @project - current_user ? project.url_to_repo : project.http_url_to_repo - end - - def default_clone_protocol - current_user ? "ssh" : "http" - end - - def project_last_activity(project) - if project.last_activity_at - time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') - else - "Never" - end - end - - def add_contribution_guide_path(project) - if project && !project.repository.contribution_guide - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "CONTRIBUTING.md", - commit_message: "Add contribution guide" - ) - end - end - - def add_changelog_path(project) - if project && !project.repository.changelog - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "CHANGELOG", - commit_message: "Add changelog" - ) - end - end - - def add_license_path(project) - if project && !project.repository.license - namespace_project_new_blob_path( - project.namespace, - project, - project.default_branch, - file_name: "LICENSE", - commit_message: "Add license" - ) - end - end - - def contribution_guide_path(project) - if project && contribution_guide = project.repository.contribution_guide - namespace_project_blob_path( - project.namespace, - project, - tree_join(project.default_branch, - contribution_guide.name) - ) - end - end - - def readme_path(project) - filename_path(project, :readme) - end - - def changelog_path(project) - filename_path(project, :changelog) - end - - def license_path(project) - filename_path(project, :license) - end - - def version_path(project) - filename_path(project, :version) - end - - def hidden_pass_url(original_url) - result = URI(original_url) - result.password = '*****' unless result.password.nil? - result - rescue - original_url - end - - def project_wiki_path_with_version(proj, page, version, is_newest) - url_params = is_newest ? {} : { version_id: version } - namespace_project_wiki_path(proj.namespace, proj, page, url_params) - end - - def project_status_css_class(status) - case status - when "started" - "active" - when "failed" - "danger" - when "finished" - "success" - end - end - - def user_max_access_in_project(user, project) - level = project.team.max_member_access(user) - - if level - Gitlab::Access.options_with_owner.key(level) - end - end - - def leave_project_message(project) - "Are you sure you want to leave \"#{project.name}\" project?" - end - - def new_readme_path - ref = @repository.root_ref if @repository - ref ||= 'master' - - namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md') - end - - def last_push_event - if current_user - current_user.recent_push(@project.id) - end - end - - def readme_cache_key - sha = @project.commit.try(:sha) || 'nil' - [@project.id, sha, "readme"].join('-') - end - - def round_commit_count(project) - count = project.commit_count - - if count > 10000 - '10000+' - elsif count > 5000 - '5000+' - elsif count > 1000 - '1000+' - else - count - end - end - - private - - def filename_path(project, filename) - if project && blob = project.repository.send(filename) - namespace_project_blob_path( - project.namespace, - project, - tree_join(project.default_branch, - blob.name) - ) - end - end - end -end diff --git a/app/helpers/gitlab/search_helper.rb b/app/helpers/gitlab/search_helper.rb deleted file mode 100644 index f9caf8f2431..00000000000 --- a/app/helpers/gitlab/search_helper.rb +++ /dev/null @@ -1,114 +0,0 @@ -module Gitlab - module SearchHelper - def search_autocomplete_opts(term) - return unless current_user - - resources_results = [ - groups_autocomplete(term), - projects_autocomplete(term) - ].flatten - - generic_results = project_autocomplete + default_autocomplete + help_autocomplete - generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") } - - [ - resources_results, - generic_results - ].flatten.uniq do |item| - item[:label] - end - end - - private - - # Autocomplete results for various settings pages - def default_autocomplete - [ - { label: "Profile settings", url: profile_path }, - { label: "SSH Keys", url: profile_keys_path }, - { label: "Dashboard", url: root_path }, - { label: "Admin Section", url: admin_root_path }, - ] - end - - # Autocomplete results for internal help pages - def help_autocomplete - [ - { label: "help: API Help", url: help_page_path("api", "README") }, - { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") }, - { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") }, - { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") }, - { label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") }, - { label: "help: SSH Keys Help", url: help_page_path("ssh", "README") }, - { label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") }, - { label: "help: Web Hooks Help", url: help_page_path("web_hooks", "web_hooks") }, - { label: "help: Workflow Help", url: help_page_path("workflow", "README") }, - ] - end - - # Autocomplete results for the current project, if it's defined - def project_autocomplete - if @project && @project.repository.exists? && @project.repository.root_ref - prefix = search_result_sanitize(@project.name_with_namespace) - ref = @ref || @project.repository.root_ref - - [ - { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, - { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, - { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, - { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, - { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, - { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, - { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, - ] - else - [] - end - end - - # Autocomplete results for the current user's groups - def groups_autocomplete(term, limit = 5) - current_user.authorized_groups.search(term).limit(limit).map do |group| - { - label: "group: #{search_result_sanitize(group.name)}", - url: group_path(group) - } - end - end - - # Autocomplete results for the current user's projects - def projects_autocomplete(term, limit = 5) - ProjectsFinder.new.execute(current_user).search_by_title(term). - sorted_by_stars.non_archived.limit(limit).map do |p| - { - label: "project: #{search_result_sanitize(p.name_with_namespace)}", - url: namespace_project_path(p.namespace, p) - } - end - end - - def search_result_sanitize(str) - Sanitize.clean(str) - end - - def search_filter_path(options={}) - exist_opts = { - search: params[:search], - project_id: params[:project_id], - group_id: params[:group_id], - scope: params[:scope] - } - - options = exist_opts.merge(options) - search_path(options) - end - - # Sanitize html generated after parsing markdown from issue description or comment - def search_md_sanitize(html) - sanitize(html, tags: %w(a p ol ul li pre code)) - end - end -end diff --git a/app/helpers/gitlab/selects_helper.rb b/app/helpers/gitlab/selects_helper.rb deleted file mode 100644 index d52d670a1cf..00000000000 --- a/app/helpers/gitlab/selects_helper.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Gitlab - module SelectsHelper - def users_select_tag(id, opts = {}) - css_class = "ajax-users-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - placeholder = opts[:placeholder] || 'Search for a user' - - null_user = opts[:null_user] || false - any_user = opts[:any_user] || false - email_user = opts[:email_user] || false - first_user = opts[:first_user] && current_user ? current_user.username : false - current_user = opts[:current_user] || false - project = opts[:project] || @project - - html = { - class: css_class, - 'data-placeholder' => placeholder, - 'data-null-user' => null_user, - 'data-any-user' => any_user, - 'data-email-user' => email_user, - 'data-first-user' => first_user, - 'data-current-user' => current_user - } - - unless opts[:scope] == :all - if project - html['data-project-id'] = project.id - elsif @group - html['data-group-id'] = @group.id - end - end - - hidden_field_tag(id, value, html) - end - - def groups_select_tag(id, opts = {}) - css_class = "ajax-groups-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end - end -end diff --git a/app/helpers/gitlab/snippets_helper.rb b/app/helpers/gitlab/snippets_helper.rb deleted file mode 100644 index aaf4d43f852..00000000000 --- a/app/helpers/gitlab/snippets_helper.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module SnippetsHelper - def lifetime_select_options - options = [ - ['forever', nil], - ['1 day', "#{Date.current + 1.day}"], - ['1 week', "#{Date.current + 1.week}"], - ['1 month', "#{Date.current + 1.month}"] - ] - options_for_select(options) - end - - def reliable_snippet_path(snippet) - if snippet.project_id? - namespace_project_snippet_path(snippet.project.namespace, - snippet.project, snippet) - else - snippet_path(snippet) - end - end - end -end diff --git a/app/helpers/gitlab/sorting_helper.rb b/app/helpers/gitlab/sorting_helper.rb deleted file mode 100644 index 29c63a0d129..00000000000 --- a/app/helpers/gitlab/sorting_helper.rb +++ /dev/null @@ -1,98 +0,0 @@ -module Gitlab - module SortingHelper - def sort_options_hash - { - sort_value_name => sort_title_name, - sort_value_recently_updated => sort_title_recently_updated, - sort_value_oldest_updated => sort_title_oldest_updated, - sort_value_recently_created => sort_title_recently_created, - sort_value_oldest_created => sort_title_oldest_created, - sort_value_milestone_soon => sort_title_milestone_soon, - sort_value_milestone_later => sort_title_milestone_later, - sort_value_largest_repo => sort_title_largest_repo, - sort_value_recently_signin => sort_title_recently_signin, - sort_value_oldest_signin => sort_title_oldest_signin, - } - end - - def sort_title_oldest_updated - 'Oldest updated' - end - - def sort_title_recently_updated - 'Recently updated' - end - - def sort_title_oldest_created - 'Oldest created' - end - - def sort_title_recently_created - 'Recently created' - end - - def sort_title_milestone_soon - 'Milestone due soon' - end - - def sort_title_milestone_later - 'Milestone due later' - end - - def sort_title_name - 'Name' - end - - def sort_title_largest_repo - 'Largest repository' - end - - def sort_title_recently_signin - 'Recent sign in' - end - - def sort_title_oldest_signin - 'Oldest sign in' - end - - def sort_value_oldest_updated - 'updated_asc' - end - - def sort_value_recently_updated - 'updated_desc' - end - - def sort_value_oldest_created - 'created_asc' - end - - def sort_value_recently_created - 'created_desc' - end - - def sort_value_milestone_soon - 'milestone_due_asc' - end - - def sort_value_milestone_later - 'milestone_due_desc' - end - - def sort_value_name - 'name_asc' - end - - def sort_value_largest_repo - 'repository_size_desc' - end - - def sort_value_recently_signin - 'recent_sign_in' - end - - def sort_value_oldest_signin - 'oldest_sign_in' - end - end -end diff --git a/app/helpers/gitlab/submodule_helper.rb b/app/helpers/gitlab/submodule_helper.rb deleted file mode 100644 index c0fbebcb1d9..00000000000 --- a/app/helpers/gitlab/submodule_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Gitlab - module SubmoduleHelper - include Gitlab::ShellAdapter - - # links to files listing for submodule if submodule is a project on this server - def submodule_links(submodule_item, ref = nil, repository = @repository) - url = repository.submodule_url_for(ref, submodule_item.path) - - return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/ - - namespace = $1 - project = $2 - project.chomp!('.git') - - if self_url?(url, namespace, project) - return namespace_project_path(namespace, project), - namespace_project_tree_path(namespace, project, - submodule_item.id) - elsif relative_self_url?(url) - relative_self_links(url, submodule_item.id) - elsif github_dot_com_url?(url) - standard_links('github.com', namespace, project, submodule_item.id) - elsif gitlab_dot_com_url?(url) - standard_links('gitlab.com', namespace, project, submodule_item.id) - else - return url, nil - end - end - - protected - - def github_dot_com_url?(url) - url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/ - end - - def gitlab_dot_com_url?(url) - url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ - end - - def self_url?(url, namespace, project) - return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', - project, '.git' ].join('') - url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) - end - - def relative_self_url?(url) - # (./)?(../repo.git) || (./)?(../../project/repo.git) ) - url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/ - end - - def standard_links(host, namespace, project, commit) - base = [ 'https://', host, '/', namespace, '/', project ].join('') - [base, [ base, '/tree/', commit ].join('')] - end - - def relative_self_links(url, commit) - # Map relative links to a namespace and project - # For example: - # ../bar.git -> same namespace, repo bar - # ../foo/bar.git -> namespace foo, repo bar - # ../../foo/bar/baz.git -> namespace bar, repo baz - components = url.split('/') - base = components.pop.gsub(/.git$/, '') - namespace = components.pop.gsub(/^\.\.$/, '') - - if namespace.empty? - namespace = @project.namespace.path - end - - [ - namespace_project_path(namespace, base), - namespace_project_tree_path(namespace, base, commit) - ] - end - end -end diff --git a/app/helpers/gitlab/tab_helper.rb b/app/helpers/gitlab/tab_helper.rb deleted file mode 100644 index 01d36ff84fc..00000000000 --- a/app/helpers/gitlab/tab_helper.rb +++ /dev/null @@ -1,133 +0,0 @@ -module Gitlab - module TabHelper - # Navigation link helper - # - # Returns an `li` element with an 'active' class if the supplied - # controller(s) and/or action(s) are currently active. The content of the - # element is the value passed to the block. - # - # options - The options hash used to determine if the element is "active" (default: {}) - # :controller - One or more controller names to check (optional). - # :action - One or more action names to check (optional). - # :path - A shorthand path, such as 'dashboard#index', to check (optional). - # :html_options - Extra options to be passed to the list element (optional). - # block - An optional block that will become the contents of the returned - # `li` element. - # - # When both :controller and :action are specified, BOTH must match in order - # to be marked as active. When only one is given, either can match. - # - # Examples - # - # # Assuming we're on TreeController#show - # - # # Controller matches, but action doesn't - # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Controller matches - # nav_link(controller: [:tree, :refs]) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Several paths - # nav_link(path: ['tree#show', 'profile#show']) { "Hello" } - # # => '
  • Hello
  • ' - # - # # Shorthand path - # nav_link(path: 'tree#show') { "Hello" } - # # => '
  • Hello
  • ' - # - # # Supplying custom options for the list element - # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } - # # => '
  • Hello
  • ' - # - # Returns a list item element String - def nav_link(options = {}, &block) - klass = active_nav_link?(options) ? 'active' : '' - - # Add our custom class into the html_options, which may or may not exist - # and which may or may not already have a :class key - o = options.delete(:html_options) || {} - o[:class] ||= '' - o[:class] += ' ' + klass - o[:class].strip! - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end - - def active_nav_link?(options) - if path = options.delete(:path) - unless path.respond_to?(:each) - path = [path] - end - - path.any? do |single_path| - current_path?(single_path) - end - elsif page = options.delete(:page) - unless page.respond_to?(:each) - page = [page] - end - - page.any? do |single_page| - current_page?(single_page) - end - else - c = options.delete(:controller) - a = options.delete(:action) - - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end - end - - def current_path?(path) - c, a, _ = path.split('#') - current_controller?(c) && current_action?(a) - end - - def project_tab_class - return "active" if current_page?(controller: "/projects", action: :edit, id: @project) - - if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name - "active" - end - end - - def branches_tab_class - if current_controller?(:protected_branches) || - current_controller?(:branches) || - current_page?(namespace_project_repository_path(@project.namespace, - @project)) - 'active' - end - end - - # Use nav_tab for save controller/action but different params - def nav_tab(key, value, &block) - o = {} - o[:class] = "" - - if value.nil? - o[:class] << " active" if params[key].blank? - else - o[:class] << " active" if params[key] == value - end - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end - end -end diff --git a/app/helpers/gitlab/tags_helper.rb b/app/helpers/gitlab/tags_helper.rb deleted file mode 100644 index d694b2c90ce..00000000000 --- a/app/helpers/gitlab/tags_helper.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gitlab - module TagsHelper - def tag_path(tag) - "/tags/#{tag}" - end - - def tag_list(project) - html = '' - project.tag_list.each do |tag| - html << link_to(tag, tag_path(tag)) - end - - html.html_safe - end - end -end diff --git a/app/helpers/gitlab/tree_helper.rb b/app/helpers/gitlab/tree_helper.rb deleted file mode 100644 index dc48ff0e6e2..00000000000 --- a/app/helpers/gitlab/tree_helper.rb +++ /dev/null @@ -1,89 +0,0 @@ -module Gitlab - module TreeHelper - # Sorts a repository's tree so that folders are before files and renders - # their corresponding partials - # - def render_tree(tree) - # Render Folders before Files/Submodules - folders, files, submodules = tree.trees, tree.blobs, tree.submodules - - tree = "" - - # Render folders if we have any - tree << render(partial: 'projects/tree/tree_item', collection: folders, - locals: { type: 'folder' }) if folders.present? - - # Render files if we have any - tree << render(partial: 'projects/tree/blob_item', collection: files, - locals: { type: 'file' }) if files.present? - - # Render submodules if we have any - tree << render(partial: 'projects/tree/submodule_item', - collection: submodules) if submodules.present? - - tree.html_safe - end - - def render_readme(readme) - render_markup(readme.name, readme.data) - end - - # Return an image icon depending on the file type and mode - # - # type - String type of the tree item; either 'folder' or 'file' - # mode - File unix mode - # name - File name - def tree_icon(type, mode, name) - icon("#{file_type_icon_class(type, mode, name)} fw") - end - - def tree_hex_class(content) - "file_#{hexdigest(content.name)}" - end - - # Simple shortcut to File.join - def tree_join(*args) - File.join(*args) - end - - def allowed_tree_edit?(project = nil, ref = nil) - project ||= @project - ref ||= @ref - return false unless project.repository.branch_names.include?(ref) - - ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) - end - - def tree_breadcrumbs(tree, max_links = 2) - if @path.present? - part_path = "" - parts = @path.split('/') - - yield('..', nil) if parts.count > max_links - - parts.each do |part| - part_path = File.join(part_path, part) unless part_path.empty? - part_path = part if part_path.empty? - - next unless parts.last(2).include?(part) if parts.count > max_links - yield(part, tree_join(@ref, part_path)) - end - end - end - - def up_dir_path - file = File.join(@path, "..") - tree_join(@ref, file) - end - - # returns the relative path of the first subdir that doesn't have only one directory descendant - def flatten_tree(tree) - subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) - if subtree.count == 1 && subtree.first.dir? - return tree_join(tree.name, flatten_tree(subtree.first)) - else - return tree.name - end - end - end -end diff --git a/app/helpers/gitlab/version_check_helper.rb b/app/helpers/gitlab/version_check_helper.rb deleted file mode 100644 index 46a12cc8c60..00000000000 --- a/app/helpers/gitlab/version_check_helper.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module VersionCheckHelper - def version_status_badge - if Rails.env.production? - image_tag VersionCheck.new.url - end - end - end -end diff --git a/app/helpers/gitlab/visibility_level_helper.rb b/app/helpers/gitlab/visibility_level_helper.rb deleted file mode 100644 index feba901f7d7..00000000000 --- a/app/helpers/gitlab/visibility_level_helper.rb +++ /dev/null @@ -1,97 +0,0 @@ -module Gitlab - module VisibilityLevelHelper - def visibility_level_color(level) - case level - when Gitlab::VisibilityLevel::PRIVATE - 'vs-private' - when Gitlab::VisibilityLevel::INTERNAL - 'vs-internal' - when Gitlab::VisibilityLevel::PUBLIC - 'vs-public' - end - end - - # Return the description for the +level+ argument. - # - # +level+ One of the Gitlab::VisibilityLevel constants - # +form_model+ Either a model object (Project, Snippet, etc.) or the name of - # a Project or Snippet class. - def visibility_level_description(level, form_model) - case form_model.is_a?(String) ? form_model : form_model.class.name - when 'PersonalSnippet', 'ProjectSnippet', 'Snippet' - snippet_visibility_level_description(level) - when 'Project' - project_visibility_level_description(level) - end - end - - def project_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "Project access must be granted explicitly for each user." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The project can be cloned by" - haml_concat "any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The project can be cloned" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def snippet_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "The snippet is visible only for me." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The snippet is visible for any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The snippet can be accessed" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def visibility_level_icon(level) - case level - when Gitlab::VisibilityLevel::PRIVATE - private_icon - when Gitlab::VisibilityLevel::INTERNAL - internal_icon - when Gitlab::VisibilityLevel::PUBLIC - public_icon - end - end - - def visibility_level_label(level) - Project.visibility_levels.key(level) - end - - def restricted_visibility_levels(show_all = false) - return [] if current_user.is_admin? && !show_all - current_application_settings.restricted_visibility_levels || [] - end - - def default_project_visibility - current_application_settings.default_project_visibility - end - - def default_snippet_visibility - current_application_settings.default_snippet_visibility - end - - def skip_level?(form_model, level) - form_model.is_a?(Project) && - form_model.forked? && - !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level) - end - end -end diff --git a/app/helpers/gitlab/wiki_helper.rb b/app/helpers/gitlab/wiki_helper.rb deleted file mode 100644 index 02a1daf0019..00000000000 --- a/app/helpers/gitlab/wiki_helper.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Gitlab - module WikiHelper - # Rails v4.1.9+ escapes all model IDs, converting slashes into %2F. The - # only way around this is to implement our own path generators. - def namespace_project_wiki_path(namespace, project, wiki_page, *args) - slug = - case wiki_page - when Symbol - wiki_page - when String - wiki_page - else - wiki_page.slug - end - namespace_project_path(namespace, project) + "/wikis/#{slug}" - end - - def edit_namespace_project_wiki_path(namespace, project, wiki_page, *args) - namespace_project_wiki_path(namespace, project, wiki_page) + '/edit' - end - - def history_namespace_project_wiki_path(namespace, project, wiki_page, *args) - namespace_project_wiki_path(namespace, project, wiki_page) + '/history' - end - end -end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb new file mode 100644 index 00000000000..1ebfd92f119 --- /dev/null +++ b/app/helpers/gitlab_markdown_helper.rb @@ -0,0 +1,196 @@ +require 'nokogiri' + +module GitlabMarkdownHelper + # Use this in places where you would normally use link_to(gfm(...), ...). + # + # It solves a problem occurring with nested links (i.e. + # "outer text gfm ref more outer text"). This will not be + # interpreted as intended. Browsers will parse something like + # "outer text gfm ref more outer text" (notice the last part is + # not linked any more). link_to_gfm corrects that. It wraps all parts to + # explicitly produce the correct linking behavior (i.e. + # "outer text gfm ref more outer text"). + def link_to_gfm(body, url, html_options = {}) + return "" if body.blank? + + escaped_body = if body =~ /\A\ at the beginning of a line", + "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___" + ].freeze + + # Returns a random markdown tip for use as a textarea placeholder + def random_markdown_tip + MARKDOWN_TIPS.sample + end + + private + + # Return +text+, truncated to +max_chars+ characters, excluding any HTML + # tags. + def truncate_visible(text, max_chars) + doc = Nokogiri::HTML.fragment(text) + content_length = 0 + truncated = false + + doc.traverse do |node| + if node.text? || node.content.empty? + if truncated + node.remove + next + end + + # Handle line breaks within a node + if node.content.strip.lines.length > 1 + node.content = "#{node.content.lines.first.chomp}..." + truncated = true + end + + num_remaining = max_chars - content_length + if node.content.length > num_remaining + node.content = node.content.truncate(num_remaining) + truncated = true + end + content_length += node.content.length + end + + truncated = truncate_if_block(node, truncated) + end + + doc.to_html + end + + # Used by #truncate_visible. If +node+ is the first block element, and the + # text hasn't already been truncated, then append "..." to the node contents + # and return true. Otherwise return false. + def truncate_if_block(node, truncated) + if node.element? && node.description.block? && !truncated + node.content = "#{node.content}..." if node.next_sibling + true + else + truncated + end + end + + # Returns the text necessary to reference `entity` across projects + # + # project - Project to reference + # entity - Object that responds to `to_reference` + # + # Examples: + # + # cross_project_reference(project, project.issues.first) + # # => 'namespace1/project1#123' + # + # cross_project_reference(project, project.merge_requests.first) + # # => 'namespace1/project1!345' + # + # Returns a String + def cross_project_reference(project, entity) + if entity.respond_to?(:to_reference) + "#{project.to_reference}#{entity.to_reference}" + else + '' + end + end +end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb new file mode 100644 index 00000000000..d0fae255a04 --- /dev/null +++ b/app/helpers/gitlab_routing_helper.rb @@ -0,0 +1,67 @@ +# Shorter routing method for project and project items +# Since update to rails 4.1.9 we are now allowed to use `/` in project routing +# so we use nested routing for project resources which include project and +# project namespace. To avoid writing long methods every time we define shortcuts for +# some of routing. +# +# For example instead of this: +# +# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) +# +# We can simply use shortcut: +# +# merge_request_path(merge_request) +# +module GitlabRoutingHelper + def project_path(project, *args) + namespace_project_path(project.namespace, project, *args) + end + + def activity_project_path(project, *args) + activity_namespace_project_path(project.namespace, project, *args) + end + + def edit_project_path(project, *args) + edit_namespace_project_path(project.namespace, project, *args) + end + + def issue_path(entity, *args) + namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_path(entity, *args) + namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) + end + + def milestone_path(entity, *args) + namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) + end + + def project_url(project, *args) + namespace_project_url(project.namespace, project, *args) + end + + def edit_project_url(project, *args) + edit_namespace_project_url(project.namespace, project, *args) + end + + def issue_url(entity, *args) + namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_url(entity, *args) + namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) + end + + def project_snippet_url(entity, *args) + namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) + end + + def toggle_subscription_path(entity, *args) + if entity.is_a?(Issue) + toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity) + else + toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity) + end + end +end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb new file mode 100644 index 00000000000..e1dda20de85 --- /dev/null +++ b/app/helpers/graph_helper.rb @@ -0,0 +1,16 @@ +module GraphHelper + def get_refs(repo, commit) + refs = "" + refs << commit.ref_names(repo).join(' ') + + # append note count + refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 + + refs + end + + def parents_zip_spaces(parents, parent_spaces) + ids = parents.map { |p| p.id } + ids.zip(parent_spaces) + end +end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb new file mode 100644 index 00000000000..82eebf4245b --- /dev/null +++ b/app/helpers/groups_helper.rb @@ -0,0 +1,42 @@ +module GroupsHelper + def remove_user_from_group_message(group, member) + if member.user + "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?" + else + "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?" + end + end + + def leave_group_message(group) + "Are you sure you want to leave \"#{group}\" group?" + end + + def should_user_see_group_roles?(user, group) + if user + user.is_admin? || group.members.exists?(user_id: user.id) + else + false + end + end + + def group_icon(group) + if group.is_a?(String) + group = Group.find_by(path: group) + end + + if group && group.avatar.present? + group.avatar.url + else + image_path('no_group_avatar.png') + end + end + + def group_title(group, name, url) + content_tag :span do + link_to( + simple_sanitize(group.name), group_path(group) + ) + ' · '.html_safe + + link_to(simple_sanitize(name), url) + end + end +end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb new file mode 100644 index 00000000000..1cf5b96481a --- /dev/null +++ b/app/helpers/icons_helper.rb @@ -0,0 +1,85 @@ +module IconsHelper + include FontAwesome::Rails::IconHelper + + # Creates an icon tag given icon name(s) and possible icon modifiers. + # + # Right now this method simply delegates directly to `fa_icon` from the + # font-awesome-rails gem, but should we ever use a different icon pack in the + # future we won't have to change hundreds of method calls. + def icon(names, options = {}) + fa_icon(names, options) + end + + def spinner(text = nil, visible = false) + css_class = 'loading' + css_class << ' hide' unless visible + + content_tag :div, class: css_class do + icon('spinner spin') + text + end + end + + def boolean_to_icon(value) + if value + icon('circle', class: 'cgreen') + else + icon('power-off', class: 'clgray') + end + end + + def public_icon + icon('globe fw') + end + + def internal_icon + icon('shield fw') + end + + def private_icon + icon('lock fw') + end + + def file_type_icon_class(type, mode, name) + if type == 'folder' + icon_class = 'folder' + elsif mode == '120000' + icon_class = 'share' + else + # Guess which icon to choose based on file extension. + # If you think a file extension is missing, feel free to add it on PR + + case File.extname(name).downcase + when '.pdf' + icon_class = 'file-pdf-o' + when '.jpg', '.jpeg', '.jif', '.jfif', + '.jp2', '.jpx', '.j2k', '.j2c', + '.png', '.gif', '.tif', '.tiff', + '.svg', '.ico', '.bmp' + icon_class = 'file-image-o' + when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip', + '.xz', '.rar', '.7z' + icon_class = 'file-archive-o' + when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac' + icon_class = 'file-audio-o' + when '.mp4', '.m4p', '.m4v', + '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', + '.mpg', '.mpeg', '.m2v', + '.avi', '.mkv', '.flv', '.ogv', '.mov', + '.3gp', '.3g2' + icon_class = 'file-video-o' + when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb' + icon_class = 'file-word-o' + when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm', + '.xlsb', '.xla', '.xlam', '.xll', '.xlw' + icon_class = 'file-excel-o' + when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm', + '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm' + icon_class = 'file-powerpoint-o' + else + icon_class = 'file-text-o' + end + end + + icon_class + end +end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb new file mode 100644 index 00000000000..6ddb37cd0dc --- /dev/null +++ b/app/helpers/issues_helper.rb @@ -0,0 +1,88 @@ +module IssuesHelper + def issue_css_classes(issue) + classes = "issue" + classes << " closed" if issue.closed? + classes << " today" if issue.today? + classes + end + + # Returns an OpenStruct object suitable for use by options_from_collection_for_select + # to allow filtering issues by an unassigned User or Milestone + def unassigned_filter + # Milestone uses :title, Issue uses :name + OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned') + end + + def url_for_project_issues(project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.project_path + else + project.issues_tracker.project_url + end + end + + def url_for_new_issue(project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.new_issue_path + else + project.issues_tracker.new_issue_url + end + end + + def url_for_issue(issue_iid, project = @project, options = {}) + return '' if project.nil? + + if options[:only_path] + project.issues_tracker.issue_path(issue_iid) + else + project.issues_tracker.issue_url(issue_iid) + end + end + + def bulk_update_milestone_options + options_for_select([['None (backlog)', -1]]) + + options_from_collection_for_select(project_active_milestones, 'id', + 'title', params[:milestone_id]) + end + + def milestone_options(object) + options_from_collection_for_select(object.project.milestones.active, + 'id', 'title', object.milestone_id) + end + + def issue_box_class(item) + if item.respond_to?(:expired?) && item.expired? + 'issue-box-expired' + elsif item.respond_to?(:merged?) && item.merged? + 'issue-box-merged' + elsif item.closed? + 'issue-box-closed' + else + 'issue-box-open' + end + end + + def issue_to_atom(xml, issue) + xml.entry do + xml.id namespace_project_issue_url(issue.project.namespace, + issue.project, issue) + xml.link href: namespace_project_issue_url(issue.project.namespace, + issue.project, issue) + xml.title truncate(issue.title, length: 80) + xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") + xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) + xml.author do |author| + xml.name issue.author_name + xml.email issue.author_email + end + xml.summary issue.title + end + end + + # Required for Gitlab::Markdown::IssueReferenceFilter + module_function :url_for_issue +end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb new file mode 100644 index 00000000000..8036303851b --- /dev/null +++ b/app/helpers/labels_helper.rb @@ -0,0 +1,101 @@ +module LabelsHelper + include ActionView::Helpers::TagHelper + + # Link to a Label + # + # label - Label object to link to + # project - Project object which will be used as the context for the label's + # link. If omitted, defaults to `@project`, or the label's own + # project. + # block - An optional block that will be passed to `link_to`, forming the + # body of the link element. If omitted, defaults to + # `render_colored_label`. + # + # Examples: + # + # # Allow the generated link to use the label's own project + # link_to_label(label) + # + # # Force the generated link to use @project + # @project = Project.first + # link_to_label(label) + # + # # Force the generated link to use a provided project + # link_to_label(label, project: Project.last) + # + # # Customize link body with a block + # link_to_label(label) { "My Custom Label Text" } + # + # Returns a String + def link_to_label(label, project: nil, &block) + project ||= @project || label.project + link = namespace_project_issues_path(project.namespace, project, + label_name: label.name) + + if block_given? + link_to link, &block + else + link_to render_colored_label(label), link + end + end + + def project_label_names + @project.labels.pluck(:title) + end + + def render_colored_label(label) + label_color = label.color || Label::DEFAULT_COLOR + text_color = text_color_for_bg(label_color) + + # Intentionally not using content_tag here so that this method can be called + # by LabelReferenceFilter + span = %() + + escape_once(label.name) + '' + + span.html_safe + end + + def suggested_colors + [ + '#0033CC', + '#428BCA', + '#44AD8E', + '#A8D695', + '#5CB85C', + '#69D100', + '#004E00', + '#34495E', + '#7F8C8D', + '#A295D6', + '#5843AD', + '#8E44AD', + '#FFECDB', + '#AD4363', + '#D10069', + '#CC0033', + '#FF0000', + '#D9534F', + '#D1D100', + '#F0AD4E', + '#AD8D43' + ] + end + + def text_color_for_bg(bg_color) + r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex) + + if (r + g + b) > 500 + '#333333' + else + '#FFFFFF' + end + end + + def project_labels_options(project) + options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) + end + + # Required for Gitlab::Markdown::LabelReferenceFilter + module_function :render_colored_label, :text_color_for_bg, :escape_once +end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb new file mode 100644 index 00000000000..f8169b4f288 --- /dev/null +++ b/app/helpers/merge_requests_helper.rb @@ -0,0 +1,74 @@ +module MergeRequestsHelper + def new_mr_path_from_push_event(event) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project) + ) + end + + def new_mr_path_for_fork_from_push_event(event) + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, event.project.forked_from_project) + ) + end + + def new_mr_from_push_event(event, target_project) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_project.repository.root_ref + } + } + end + + def mr_css_classes(mr) + classes = "merge-request" + classes << " closed" if mr.closed? + classes << " merged" if mr.merged? + classes + end + + def ci_build_details_path(merge_request) + merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) + end + + def merge_path_description(merge_request, separator) + if merge_request.for_fork? + "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}" + else + "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" + end + end + + def issues_sentence(issues) + issues.map { |i| "##{i.iid}" }.to_sentence + end + + def mr_change_branches_path(merge_request) + new_namespace_project_merge_request_path( + @project.namespace, @project, + merge_request: { + source_project_id: @merge_request.source_project_id, + target_project_id: @merge_request.target_project_id, + source_branch: @merge_request.source_branch, + target_branch: nil + } + ) + end + + def source_branch_with_namespace(merge_request) + if merge_request.for_fork? + namespace = link_to(merge_request.source_project_namespace, + project_path(merge_request.source_project)) + namespace + ":#{merge_request.source_branch}" + else + merge_request.source_branch + end + end +end diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb new file mode 100644 index 00000000000..132a893e532 --- /dev/null +++ b/app/helpers/milestones_helper.rb @@ -0,0 +1,36 @@ +module MilestonesHelper + def milestones_filter_path(opts = {}) + if @project + namespace_project_milestones_path(@project.namespace, @project, opts) + elsif @group + group_milestones_path(@group, opts) + else + dashboard_milestones_path(opts) + end + end + + def milestone_progress_bar(milestone) + options = { + class: 'progress-bar progress-bar-success', + style: "width: #{milestone.percent_complete}%;" + } + + content_tag :div, class: 'progress' do + content_tag :div, nil, options + end + end + + def projects_milestones_options + milestones = + if @project + @project.milestones + else + Milestone.where(project_id: @projects) + end.active + + grouped_milestones = Milestones::GroupService.new(milestones).execute + grouped_milestones.unshift(Milestone::None) + + options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) + end +end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb new file mode 100644 index 00000000000..b3132a1f3ba --- /dev/null +++ b/app/helpers/namespaces_helper.rb @@ -0,0 +1,36 @@ +module NamespacesHelper + def namespaces_options(selected = :current_user, scope = :default) + groups = current_user.owned_groups + current_user.masters_groups + users = [current_user.namespace] + + group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] + users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] + + options = [] + options << group_opts + options << users_opts + + if selected == :current_user && current_user.namespace + selected = current_user.namespace.id + end + + grouped_options_for_select(options, selected) + end + + def namespace_select_tag(id, opts = {}) + css_class = "ajax-namespace-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end + + def namespace_icon(namespace, size = 40) + if namespace.kind_of?(Group) + group_icon(namespace) + else + avatar_icon(namespace.owner.email, size) + end + end +end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb new file mode 100644 index 00000000000..9b1dd8b8e54 --- /dev/null +++ b/app/helpers/nav_helper.rb @@ -0,0 +1,21 @@ +module NavHelper + def nav_menu_collapsed? + cookies[:collapsed_nav] == 'true' + end + + def nav_sidebar_class + if nav_menu_collapsed? + "page-sidebar-collapsed" + else + "page-sidebar-expanded" + end + end + + def nav_header_class + if nav_menu_collapsed? + "header-collapsed" + else + "header-expanded" + end + end +end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb new file mode 100644 index 00000000000..5f0c921413a --- /dev/null +++ b/app/helpers/notes_helper.rb @@ -0,0 +1,76 @@ +module NotesHelper + # Helps to distinguish e.g. commit notes in mr notes list + def note_for_main_target?(note) + (@noteable.class.name == note.noteable_type && !note.for_diff_line?) + end + + def note_target_fields(note) + hidden_field_tag(:target_type, note.noteable.class.name.underscore) + + hidden_field_tag(:target_id, note.noteable.id) + end + + def note_editable?(note) + note.editable? && can?(current_user, :admin_note, note) + end + + def link_to_commit_diff_line_note(note) + if note.for_commit_diff_line? + link_to( + "#{note.diff_file_name}:L#{note.diff_new_line}", + namespace_project_commit_path(@project.namespace, @project, + note.noteable, anchor: note.line_code) + ) + end + end + + def noteable_json(noteable) + { + id: noteable.id, + class: noteable.class.name, + resources: noteable.class.table_name, + project_id: noteable.project.id, + }.to_json + end + + def link_to_new_diff_note(line_code, line_type = nil) + discussion_id = Note.build_discussion_id( + @comments_target[:noteable_type], + @comments_target[:noteable_id] || @comments_target[:commit_id], + line_code + ) + + data = { + noteable_type: @comments_target[:noteable_type], + noteable_id: @comments_target[:noteable_id], + commit_id: @comments_target[:commit_id], + line_code: line_code, + discussion_id: discussion_id, + line_type: line_type + } + + button_tag(class: 'btn add-diff-note js-add-diff-note-button', + data: data, + title: 'Add a comment to this line') do + icon('comment-o') + end + end + + def link_to_reply_diff(note, line_type = nil) + return unless current_user + + data = { + noteable_type: note.noteable_type, + noteable_id: note.noteable_id, + commit_id: note.commit_id, + line_code: note.line_code, + discussion_id: note.discussion_id, + line_type: line_type + } + + button_tag class: 'btn reply-btn js-discussion-reply-button', + data: data, title: 'Add a reply' do + link_text = icon('comment') + link_text << ' Reply' + end + end +end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb new file mode 100644 index 00000000000..2f8e64c375f --- /dev/null +++ b/app/helpers/notifications_helper.rb @@ -0,0 +1,15 @@ +module NotificationsHelper + include IconsHelper + + def notification_icon(notification) + if notification.disabled? + icon('volume-off', class: 'ns-mute') + elsif notification.participating? + icon('volume-down', class: 'ns-part') + elsif notification.watch? + icon('volume-up', class: 'ns-watch') + else + icon('circle-o', class: 'ns-default') + end + end +end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb new file mode 100644 index 00000000000..7f1b6a69926 --- /dev/null +++ b/app/helpers/preferences_helper.rb @@ -0,0 +1,44 @@ +# Helper methods for per-User preferences +module PreferencesHelper + # Maps `dashboard` values to more user-friendly option text + DASHBOARD_CHOICES = { + projects: 'Your Projects (default)', + stars: 'Starred Projects' + }.with_indifferent_access.freeze + + # Returns an Array usable by a select field for more user-friendly option text + def dashboard_choices + defined = User.dashboards + + if defined.size != DASHBOARD_CHOICES.size + # Ensure that anyone adding new options updates this method too + raise RuntimeError, "`User` defines #{defined.size} dashboard choices," + + " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}." + else + defined.map do |key, _| + # Use `fetch` so `KeyError` gets raised when a key is missing + [DASHBOARD_CHOICES.fetch(key), key] + end + end + end + + def project_view_choices + [ + ['Readme (default)', :readme], + ['Activity view', :activity] + ] + end + + def user_application_theme + Gitlab::Themes.for_user(current_user).css_class + end + + def user_color_scheme + Gitlab::ColorSchemes.for_user(current_user).css_class + end + + def prefer_readme? + !current_user || + current_user.project_view == 'readme' + end +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb new file mode 100644 index 00000000000..ab9b068de05 --- /dev/null +++ b/app/helpers/projects_helper.rb @@ -0,0 +1,330 @@ +module ProjectsHelper + def remove_from_project_team_message(project, member) + if member.user + "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?" + else + "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?" + end + end + + def link_to_project(project) + link_to [project.namespace.becomes(Namespace), project] do + title = content_tag(:span, project.name, class: 'project-name') + + if project.namespace + namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') + title = namespace + title + end + + title + end + end + + def link_to_member(project, author, opts = {}) + default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } + opts = default_opts.merge(opts) + + return "(deleted)" unless author + + author_html = "" + + # Build avatar image tag + author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + + # Build name span tag + author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] + + author_html = author_html.html_safe + + if opts[:name] + link_to(author_html, user_path(author), class: "author_link").html_safe + else + link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe + end + end + + def project_title(project) + if project.group + content_tag :span do + link_to( + simple_sanitize(project.group.name), group_path(project.group) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) + end + else + owner = project.namespace.owner + content_tag :span do + link_to( + simple_sanitize(owner.name), user_path(owner) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) + end + end + end + + def remove_project_message(project) + "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" + end + + def transfer_project_message(project) + "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" + end + + def project_nav_tabs + @nav_tabs ||= get_project_nav_tabs(@project, current_user) + end + + def project_nav_tab?(name) + project_nav_tabs.include? name + end + + def project_active_milestones + @project.milestones.active.order("due_date, title ASC") + end + + def project_for_deploy_key(deploy_key) + if deploy_key.projects.include?(@project) + @project + else + deploy_key.projects.find { |project| can?(current_user, :read_project, project) } + end + end + + def can_change_visibility_level?(project, current_user) + return false unless can?(current_user, :change_visibility_level, project) + + if project.forked? + project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE + else + true + end + end + + private + + def get_project_nav_tabs(project, current_user) + nav_tabs = [:home] + + if !project.empty_repo? && can?(current_user, :download_code, project) + nav_tabs << [:files, :commits, :network, :graphs] + end + + if project.repo_exists? && can?(current_user, :read_merge_request, project) + nav_tabs << :merge_requests + end + + if can?(current_user, :admin_project, project) + nav_tabs << :settings + end + + if can?(current_user, :read_issue, project) + nav_tabs << :issues + end + + if can?(current_user, :read_wiki, project) + nav_tabs << :wiki + end + + if can?(current_user, :read_project_snippet, project) + nav_tabs << :snippets + end + + if can?(current_user, :read_label, project) + nav_tabs << :labels + end + + if can?(current_user, :read_milestone, project) + nav_tabs << :milestones + end + + nav_tabs.flatten + end + + def git_user_name + if current_user + current_user.name + else + "Your name" + end + end + + def git_user_email + if current_user + current_user.email + else + "your@email.com" + end + end + + def repository_size(project = nil) + "#{(project || @project).repository_size} MB" + rescue + # In order to prevent 500 error + # when application cannot allocate memory + # to calculate repo size - just show 'Unknown' + 'unknown' + end + + def default_url_to_repo(project = nil) + project = project || @project + current_user ? project.url_to_repo : project.http_url_to_repo + end + + def default_clone_protocol + current_user ? "ssh" : "http" + end + + def project_last_activity(project) + if project.last_activity_at + time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') + else + "Never" + end + end + + def add_contribution_guide_path(project) + if project && !project.repository.contribution_guide + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "CONTRIBUTING.md", + commit_message: "Add contribution guide" + ) + end + end + + def add_changelog_path(project) + if project && !project.repository.changelog + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "CHANGELOG", + commit_message: "Add changelog" + ) + end + end + + def add_license_path(project) + if project && !project.repository.license + namespace_project_new_blob_path( + project.namespace, + project, + project.default_branch, + file_name: "LICENSE", + commit_message: "Add license" + ) + end + end + + def contribution_guide_path(project) + if project && contribution_guide = project.repository.contribution_guide + namespace_project_blob_path( + project.namespace, + project, + tree_join(project.default_branch, + contribution_guide.name) + ) + end + end + + def readme_path(project) + filename_path(project, :readme) + end + + def changelog_path(project) + filename_path(project, :changelog) + end + + def license_path(project) + filename_path(project, :license) + end + + def version_path(project) + filename_path(project, :version) + end + + def hidden_pass_url(original_url) + result = URI(original_url) + result.password = '*****' unless result.password.nil? + result + rescue + original_url + end + + def project_wiki_path_with_version(proj, page, version, is_newest) + url_params = is_newest ? {} : { version_id: version } + namespace_project_wiki_path(proj.namespace, proj, page, url_params) + end + + def project_status_css_class(status) + case status + when "started" + "active" + when "failed" + "danger" + when "finished" + "success" + end + end + + def user_max_access_in_project(user, project) + level = project.team.max_member_access(user) + + if level + Gitlab::Access.options_with_owner.key(level) + end + end + + def leave_project_message(project) + "Are you sure you want to leave \"#{project.name}\" project?" + end + + def new_readme_path + ref = @repository.root_ref if @repository + ref ||= 'master' + + namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md') + end + + def last_push_event + if current_user + current_user.recent_push(@project.id) + end + end + + def readme_cache_key + sha = @project.commit.try(:sha) || 'nil' + [@project.id, sha, "readme"].join('-') + end + + def round_commit_count(project) + count = project.commit_count + + if count > 10000 + '10000+' + elsif count > 5000 + '5000+' + elsif count > 1000 + '1000+' + else + count + end + end + + private + + def filename_path(project, filename) + if project && blob = project.repository.send(filename) + namespace_project_blob_path( + project.namespace, + project, + tree_join(project.default_branch, + blob.name) + ) + end + end +end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb new file mode 100644 index 00000000000..c31a556ff7b --- /dev/null +++ b/app/helpers/search_helper.rb @@ -0,0 +1,112 @@ +module SearchHelper + def search_autocomplete_opts(term) + return unless current_user + + resources_results = [ + groups_autocomplete(term), + projects_autocomplete(term) + ].flatten + + generic_results = project_autocomplete + default_autocomplete + help_autocomplete + generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") } + + [ + resources_results, + generic_results + ].flatten.uniq do |item| + item[:label] + end + end + + private + + # Autocomplete results for various settings pages + def default_autocomplete + [ + { label: "Profile settings", url: profile_path }, + { label: "SSH Keys", url: profile_keys_path }, + { label: "Dashboard", url: root_path }, + { label: "Admin Section", url: admin_root_path }, + ] + end + + # Autocomplete results for internal help pages + def help_autocomplete + [ + { label: "help: API Help", url: help_page_path("api", "README") }, + { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") }, + { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") }, + { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") }, + { label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") }, + { label: "help: SSH Keys Help", url: help_page_path("ssh", "README") }, + { label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") }, + { label: "help: Web Hooks Help", url: help_page_path("web_hooks", "web_hooks") }, + { label: "help: Workflow Help", url: help_page_path("workflow", "README") }, + ] + end + + # Autocomplete results for the current project, if it's defined + def project_autocomplete + if @project && @project.repository.exists? && @project.repository.root_ref + prefix = search_result_sanitize(@project.name_with_namespace) + ref = @ref || @project.repository.root_ref + + [ + { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, + { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, + { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, + { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, + { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, + { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, + ] + else + [] + end + end + + # Autocomplete results for the current user's groups + def groups_autocomplete(term, limit = 5) + current_user.authorized_groups.search(term).limit(limit).map do |group| + { + label: "group: #{search_result_sanitize(group.name)}", + url: group_path(group) + } + end + end + + # Autocomplete results for the current user's projects + def projects_autocomplete(term, limit = 5) + ProjectsFinder.new.execute(current_user).search_by_title(term). + sorted_by_stars.non_archived.limit(limit).map do |p| + { + label: "project: #{search_result_sanitize(p.name_with_namespace)}", + url: namespace_project_path(p.namespace, p) + } + end + end + + def search_result_sanitize(str) + Sanitize.clean(str) + end + + def search_filter_path(options={}) + exist_opts = { + search: params[:search], + project_id: params[:project_id], + group_id: params[:group_id], + scope: params[:scope] + } + + options = exist_opts.merge(options) + search_path(options) + end + + # Sanitize html generated after parsing markdown from issue description or comment + def search_md_sanitize(html) + sanitize(html, tags: %w(a p ol ul li pre code)) + end +end diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb new file mode 100644 index 00000000000..12fce8db701 --- /dev/null +++ b/app/helpers/selects_helper.rb @@ -0,0 +1,45 @@ +module SelectsHelper + def users_select_tag(id, opts = {}) + css_class = "ajax-users-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + placeholder = opts[:placeholder] || 'Search for a user' + + null_user = opts[:null_user] || false + any_user = opts[:any_user] || false + email_user = opts[:email_user] || false + first_user = opts[:first_user] && current_user ? current_user.username : false + current_user = opts[:current_user] || false + project = opts[:project] || @project + + html = { + class: css_class, + 'data-placeholder' => placeholder, + 'data-null-user' => null_user, + 'data-any-user' => any_user, + 'data-email-user' => email_user, + 'data-first-user' => first_user, + 'data-current-user' => current_user + } + + unless opts[:scope] == :all + if project + html['data-project-id'] = project.id + elsif @group + html['data-group-id'] = @group.id + end + end + + hidden_field_tag(id, value, html) + end + + def groups_select_tag(id, opts = {}) + css_class = "ajax-groups-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end +end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb new file mode 100644 index 00000000000..906cb12cd48 --- /dev/null +++ b/app/helpers/snippets_helper.rb @@ -0,0 +1,20 @@ +module SnippetsHelper + def lifetime_select_options + options = [ + ['forever', nil], + ['1 day', "#{Date.current + 1.day}"], + ['1 week', "#{Date.current + 1.week}"], + ['1 month', "#{Date.current + 1.month}"] + ] + options_for_select(options) + end + + def reliable_snippet_path(snippet) + if snippet.project_id? + namespace_project_snippet_path(snippet.project.namespace, + snippet.project, snippet) + else + snippet_path(snippet) + end + end +end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb new file mode 100644 index 00000000000..bb12d43f397 --- /dev/null +++ b/app/helpers/sorting_helper.rb @@ -0,0 +1,96 @@ +module SortingHelper + def sort_options_hash + { + sort_value_name => sort_title_name, + sort_value_recently_updated => sort_title_recently_updated, + sort_value_oldest_updated => sort_title_oldest_updated, + sort_value_recently_created => sort_title_recently_created, + sort_value_oldest_created => sort_title_oldest_created, + sort_value_milestone_soon => sort_title_milestone_soon, + sort_value_milestone_later => sort_title_milestone_later, + sort_value_largest_repo => sort_title_largest_repo, + sort_value_recently_signin => sort_title_recently_signin, + sort_value_oldest_signin => sort_title_oldest_signin, + } + end + + def sort_title_oldest_updated + 'Oldest updated' + end + + def sort_title_recently_updated + 'Recently updated' + end + + def sort_title_oldest_created + 'Oldest created' + end + + def sort_title_recently_created + 'Recently created' + end + + def sort_title_milestone_soon + 'Milestone due soon' + end + + def sort_title_milestone_later + 'Milestone due later' + end + + def sort_title_name + 'Name' + end + + def sort_title_largest_repo + 'Largest repository' + end + + def sort_title_recently_signin + 'Recent sign in' + end + + def sort_title_oldest_signin + 'Oldest sign in' + end + + def sort_value_oldest_updated + 'updated_asc' + end + + def sort_value_recently_updated + 'updated_desc' + end + + def sort_value_oldest_created + 'created_asc' + end + + def sort_value_recently_created + 'created_desc' + end + + def sort_value_milestone_soon + 'milestone_due_asc' + end + + def sort_value_milestone_later + 'milestone_due_desc' + end + + def sort_value_name + 'name_asc' + end + + def sort_value_largest_repo + 'repository_size_desc' + end + + def sort_value_recently_signin + 'recent_sign_in' + end + + def sort_value_oldest_signin + 'oldest_sign_in' + end +end diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb new file mode 100644 index 00000000000..b3f50ceebe4 --- /dev/null +++ b/app/helpers/submodule_helper.rb @@ -0,0 +1,74 @@ +module SubmoduleHelper + include Gitlab::ShellAdapter + + # links to files listing for submodule if submodule is a project on this server + def submodule_links(submodule_item, ref = nil, repository = @repository) + url = repository.submodule_url_for(ref, submodule_item.path) + + return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/ + + namespace = $1 + project = $2 + project.chomp!('.git') + + if self_url?(url, namespace, project) + return namespace_project_path(namespace, project), + namespace_project_tree_path(namespace, project, + submodule_item.id) + elsif relative_self_url?(url) + relative_self_links(url, submodule_item.id) + elsif github_dot_com_url?(url) + standard_links('github.com', namespace, project, submodule_item.id) + elsif gitlab_dot_com_url?(url) + standard_links('gitlab.com', namespace, project, submodule_item.id) + else + return url, nil + end + end + + protected + + def github_dot_com_url?(url) + url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/ + end + + def gitlab_dot_com_url?(url) + url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ + end + + def self_url?(url, namespace, project) + return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', + project, '.git' ].join('') + url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) + end + + def relative_self_url?(url) + # (./)?(../repo.git) || (./)?(../../project/repo.git) ) + url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/ + end + + def standard_links(host, namespace, project, commit) + base = [ 'https://', host, '/', namespace, '/', project ].join('') + [base, [ base, '/tree/', commit ].join('')] + end + + def relative_self_links(url, commit) + # Map relative links to a namespace and project + # For example: + # ../bar.git -> same namespace, repo bar + # ../foo/bar.git -> namespace foo, repo bar + # ../../foo/bar/baz.git -> namespace bar, repo baz + components = url.split('/') + base = components.pop.gsub(/.git$/, '') + namespace = components.pop.gsub(/^\.\.$/, '') + + if namespace.empty? + namespace = @project.namespace.path + end + + [ + namespace_project_path(namespace, base), + namespace_project_tree_path(namespace, base, commit) + ] + end +end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb new file mode 100644 index 00000000000..0e7d8065ac7 --- /dev/null +++ b/app/helpers/tab_helper.rb @@ -0,0 +1,131 @@ +module TabHelper + # Navigation link helper + # + # Returns an `li` element with an 'active' class if the supplied + # controller(s) and/or action(s) are currently active. The content of the + # element is the value passed to the block. + # + # options - The options hash used to determine if the element is "active" (default: {}) + # :controller - One or more controller names to check (optional). + # :action - One or more action names to check (optional). + # :path - A shorthand path, such as 'dashboard#index', to check (optional). + # :html_options - Extra options to be passed to the list element (optional). + # block - An optional block that will become the contents of the returned + # `li` element. + # + # When both :controller and :action are specified, BOTH must match in order + # to be marked as active. When only one is given, either can match. + # + # Examples + # + # # Assuming we're on TreeController#show + # + # # Controller matches, but action doesn't + # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Controller matches + # nav_link(controller: [:tree, :refs]) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Several paths + # nav_link(path: ['tree#show', 'profile#show']) { "Hello" } + # # => '
  • Hello
  • ' + # + # # Shorthand path + # nav_link(path: 'tree#show') { "Hello" } + # # => '
  • Hello
  • ' + # + # # Supplying custom options for the list element + # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" } + # # => '
  • Hello
  • ' + # + # Returns a list item element String + def nav_link(options = {}, &block) + klass = active_nav_link?(options) ? 'active' : '' + + # Add our custom class into the html_options, which may or may not exist + # and which may or may not already have a :class key + o = options.delete(:html_options) || {} + o[:class] ||= '' + o[:class] += ' ' + klass + o[:class].strip! + + if block_given? + content_tag(:li, capture(&block), o) + else + content_tag(:li, nil, o) + end + end + + def active_nav_link?(options) + if path = options.delete(:path) + unless path.respond_to?(:each) + path = [path] + end + + path.any? do |single_path| + current_path?(single_path) + end + elsif page = options.delete(:page) + unless page.respond_to?(:each) + page = [page] + end + + page.any? do |single_page| + current_page?(single_page) + end + else + c = options.delete(:controller) + a = options.delete(:action) + + if c && a + # When given both options, make sure BOTH are true + current_controller?(*c) && current_action?(*a) + else + # Otherwise check EITHER option + current_controller?(*c) || current_action?(*a) + end + end + end + + def current_path?(path) + c, a, _ = path.split('#') + current_controller?(c) && current_action?(a) + end + + def project_tab_class + return "active" if current_page?(controller: "/projects", action: :edit, id: @project) + + if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name + "active" + end + end + + def branches_tab_class + if current_controller?(:protected_branches) || + current_controller?(:branches) || + current_page?(namespace_project_repository_path(@project.namespace, + @project)) + 'active' + end + end + + # Use nav_tab for save controller/action but different params + def nav_tab(key, value, &block) + o = {} + o[:class] = "" + + if value.nil? + o[:class] << " active" if params[key].blank? + else + o[:class] << " active" if params[key] == value + end + + if block_given? + content_tag(:li, capture(&block), o) + else + content_tag(:li, nil, o) + end + end +end diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb new file mode 100644 index 00000000000..fb85544df2d --- /dev/null +++ b/app/helpers/tags_helper.rb @@ -0,0 +1,14 @@ +module TagsHelper + def tag_path(tag) + "/tags/#{tag}" + end + + def tag_list(project) + html = '' + project.tag_list.each do |tag| + html << link_to(tag, tag_path(tag)) + end + + html.html_safe + end +end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb new file mode 100644 index 00000000000..03a49e119b8 --- /dev/null +++ b/app/helpers/tree_helper.rb @@ -0,0 +1,88 @@ +module TreeHelper + # Sorts a repository's tree so that folders are before files and renders + # their corresponding partials + # + # contents - A Grit::Tree object for the current tree + def render_tree(tree) + # Render Folders before Files/Submodules + folders, files, submodules = tree.trees, tree.blobs, tree.submodules + + tree = "" + + # Render folders if we have any + tree << render(partial: 'projects/tree/tree_item', collection: folders, + locals: { type: 'folder' }) if folders.present? + + # Render files if we have any + tree << render(partial: 'projects/tree/blob_item', collection: files, + locals: { type: 'file' }) if files.present? + + # Render submodules if we have any + tree << render(partial: 'projects/tree/submodule_item', + collection: submodules) if submodules.present? + + tree.html_safe + end + + def render_readme(readme) + render_markup(readme.name, readme.data) + end + + # Return an image icon depending on the file type and mode + # + # type - String type of the tree item; either 'folder' or 'file' + # mode - File unix mode + # name - File name + def tree_icon(type, mode, name) + icon("#{file_type_icon_class(type, mode, name)} fw") + end + + def tree_hex_class(content) + "file_#{hexdigest(content.name)}" + end + + # Simple shortcut to File.join + def tree_join(*args) + File.join(*args) + end + + def allowed_tree_edit?(project = nil, ref = nil) + project ||= @project + ref ||= @ref + return false unless project.repository.branch_names.include?(ref) + + ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) + end + + def tree_breadcrumbs(tree, max_links = 2) + if @path.present? + part_path = "" + parts = @path.split('/') + + yield('..', nil) if parts.count > max_links + + parts.each do |part| + part_path = File.join(part_path, part) unless part_path.empty? + part_path = part if part_path.empty? + + next unless parts.last(2).include?(part) if parts.count > max_links + yield(part, tree_join(@ref, part_path)) + end + end + end + + def up_dir_path + file = File.join(@path, "..") + tree_join(@ref, file) + end + + # returns the relative path of the first subdir that doesn't have only one directory descendant + def flatten_tree(tree) + subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) + if subtree.count == 1 && subtree.first.dir? + return tree_join(tree.name, flatten_tree(subtree.first)) + else + return tree.name + end + end +end diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb new file mode 100644 index 00000000000..f64d730b448 --- /dev/null +++ b/app/helpers/version_check_helper.rb @@ -0,0 +1,7 @@ +module VersionCheckHelper + def version_status_badge + if Rails.env.production? + image_tag VersionCheck.new.url + end + end +end diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb new file mode 100644 index 00000000000..b52cd23aba2 --- /dev/null +++ b/app/helpers/visibility_level_helper.rb @@ -0,0 +1,95 @@ +module VisibilityLevelHelper + def visibility_level_color(level) + case level + when Gitlab::VisibilityLevel::PRIVATE + 'vs-private' + when Gitlab::VisibilityLevel::INTERNAL + 'vs-internal' + when Gitlab::VisibilityLevel::PUBLIC + 'vs-public' + end + end + + # Return the description for the +level+ argument. + # + # +level+ One of the Gitlab::VisibilityLevel constants + # +form_model+ Either a model object (Project, Snippet, etc.) or the name of + # a Project or Snippet class. + def visibility_level_description(level, form_model) + case form_model.is_a?(String) ? form_model : form_model.class.name + when 'PersonalSnippet', 'ProjectSnippet', 'Snippet' + snippet_visibility_level_description(level) + when 'Project' + project_visibility_level_description(level) + end + end + + def project_visibility_level_description(level) + capture_haml do + haml_tag :span do + case level + when Gitlab::VisibilityLevel::PRIVATE + haml_concat "Project access must be granted explicitly for each user." + when Gitlab::VisibilityLevel::INTERNAL + haml_concat "The project can be cloned by" + haml_concat "any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + haml_concat "The project can be cloned" + haml_concat "without any" + haml_concat "authentication." + end + end + end + end + + def snippet_visibility_level_description(level) + capture_haml do + haml_tag :span do + case level + when Gitlab::VisibilityLevel::PRIVATE + haml_concat "The snippet is visible only for me." + when Gitlab::VisibilityLevel::INTERNAL + haml_concat "The snippet is visible for any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + haml_concat "The snippet can be accessed" + haml_concat "without any" + haml_concat "authentication." + end + end + end + end + + def visibility_level_icon(level) + case level + when Gitlab::VisibilityLevel::PRIVATE + private_icon + when Gitlab::VisibilityLevel::INTERNAL + internal_icon + when Gitlab::VisibilityLevel::PUBLIC + public_icon + end + end + + def visibility_level_label(level) + Project.visibility_levels.key(level) + end + + def restricted_visibility_levels(show_all = false) + return [] if current_user.is_admin? && !show_all + current_application_settings.restricted_visibility_levels || [] + end + + def default_project_visibility + current_application_settings.default_project_visibility + end + + def default_snippet_visibility + current_application_settings.default_snippet_visibility + end + + def skip_level?(form_model, level) + form_model.is_a?(Project) && + form_model.forked? && + !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level) + end +end diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb index 2b650bc6eac..aedb0889185 100644 --- a/app/mailers/base_mailer.rb +++ b/app/mailers/base_mailer.rb @@ -1,6 +1,6 @@ class BaseMailer < ActionMailer::Base - add_template_helper Gitlab::ApplicationHelper - add_template_helper Gitlab::GitlabMarkdownHelper + add_template_helper ApplicationHelper + add_template_helper GitlabMarkdownHelper attr_accessor :current_user helper_method :current_user, :can? diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 38afb49c78c..f196ffd53f3 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -8,8 +8,8 @@ class Notify < BaseMailer include Emails::Profile include Emails::Groups - add_template_helper Gitlab::MergeRequestsHelper - add_template_helper Gitlab::EmailsHelper + add_template_helper MergeRequestsHelper + add_template_helper EmailsHelper def test_email(recipient_email, subject, body) mail(to: recipient_email, @@ -100,7 +100,7 @@ class Notify < BaseMailer def mail_thread(model, headers = {}) if @project - headers['X-GitLab-Project'] = @project.name + headers['X-GitLab-Project'] = @project.name headers['X-GitLab-Project-Id'] = @project.id headers['X-GitLab-Project-Path'] = @project.path_with_namespace end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index c5d8be5d681..95a455b5dd7 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -1,7 +1,7 @@ module Gitlab class UrlBuilder include Gitlab::Application.routes.url_helpers - include Gitlab::GitlabRoutingHelper + include GitlabRoutingHelper def initialize(type) @type = type diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb index aa60011685b..35f115f7f4a 100644 --- a/spec/lib/ci/ansi2html_spec.rb +++ b/spec/lib/ci/ansi2html_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ansi2html do +describe Ci::Ansi2html do it "prints non-ansi as-is" do Ansi2html::convert("Hello").should == 'Hello' diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index ed3d4e84054..efc05676676 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe GitlabCiYamlProcessor do - +describe Ci::GitlabCiYamlProcessor do + describe "#builds_for_ref" do let (:type) { 'test' } diff --git a/spec/lib/ci/upgrader_spec.rb b/spec/lib/ci/upgrader_spec.rb deleted file mode 100644 index 40a98307ad2..00000000000 --- a/spec/lib/ci/upgrader_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe Upgrader do - let(:upgrader) { Upgrader.new } - let(:current_version) { GitlabCi::VERSION } - - describe 'current_version_raw' do - it { upgrader.current_version_raw.should == current_version } - end - - describe 'latest_version?' do - it 'should be true if newest version' do - upgrader.stub(latest_version_raw: current_version) - upgrader.latest_version?.should be_true - end - end - - describe 'latest_version_raw' do - it 'should be latest version for GitlabCI 3' do - allow(upgrader).to receive(:current_version_raw).and_return('3.0.0') - expect(upgrader.latest_version_raw).to eq('v3.2.0') - end - - it 'should get the latest version from tags' do - allow(upgrader).to receive(:fetch_git_tags).and_return([ - '1b5bee25b51724214c7a3307ef94027ab93ec982 refs/tags/v7.8.1', - '424cb42e35947fa304ef83eb211ffc657e31aef3 refs/tags/v7.8.1^{}', - '498e5ba63be1bb99e30c6e720902d864aac4413c refs/tags/v7.9.0.rc1', - '96aaf45ae93bd43e8b3f5d4d353d64d3cbe1e63b refs/tags/v7.9.0.rc1^{}', - '94aaf45ae93bd43e8b3fad4a353d64d3cbe1e62b refs/tags/v7.1.0', - '96aaf45ae93ba13e8b3f5d4d353d64d3cbe1e251 refs/tags/v7.1.0^{}', - '29359d64442bf54b4ca1d8b439fd9e5f9cd83252 refs/tags/v7.10.0', - '4d9213a6378bff43a69ae099702fb81e29335e7a refs/tags/v7.10.0^{}', - '1d93e1626bda93622ca7a2ae2825e2e94dabf3c6 refs/tags/v7.12.0', - '0188a9d1c2efdc52bfad36ad303686be997de713 refs/tags/v7.12.0^{}']) - expect(upgrader.latest_version_raw).to eq("v7.12.0") - end - end -end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 34e00d5b3c0..cc30b9e83f1 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe CreateCommitService do +describe Ci::CreateCommitService do let(:service) { CreateCommitService.new } let(:project) { FactoryGirl.create(:project) } - + describe :execute do context 'valid params' do - let(:commit) do + let(:commit) do service.execute(project, ref: 'refs/heads/master', before: '00000000', @@ -73,7 +73,7 @@ describe CreateCommitService do commits: commits, ci_yaml_file: gitlab_ci_yaml ) - + commit.builds.first.name.should == "staging" end diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index 31614968d55..c4b62e4fa9e 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateProjectService do +describe Ci::CreateProjectService do let(:service) { CreateProjectService.new } let(:current_user) { double.as_null_object } let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index 41db01c2235..c874697c456 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateTriggerRequestService do +describe Ci::CreateTriggerRequestService do let(:service) { CreateTriggerRequestService.new } let(:project) { FactoryGirl.create :project } let(:trigger) { FactoryGirl.create :trigger, project: project } diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index f7b9bf58127..b6ad262152d 100644 --- a/spec/services/ci/event_service_spec.rb +++ b/spec/services/ci/event_service_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe EventService do +describe Ci::EventService do let (:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } let (:user) { double(username: "root", id: 1) } before do Event.destroy_all end - + describe :remove_project do it "creates event" do EventService.new.remove_project(user, project) @@ -31,4 +31,4 @@ describe EventService do Event.last.description.should == "User \"root\" updated projects settings" end end -end \ No newline at end of file +end diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index 4c7094146bb..dadc919bae1 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ImageForBuildService do +describe Ci::ImageForBuildService do let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:project) } let(:commit) { FactoryGirl.create(:commit, project: project, ref: 'master') } diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index b5af777dd1d..6d0ae76a241 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe RegisterBuildService do +describe Ci::RegisterBuildService do let!(:service) { RegisterBuildService.new } let!(:project) { FactoryGirl.create :project } let!(:commit) { FactoryGirl.create :commit, project: project } diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index 2bb153942e8..6f882e6fdad 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WebHookService do +describe Ci::WebHookService do let (:project) { FactoryGirl.create :project } let (:commit) { FactoryGirl.create :commit, project: project } let (:build) { FactoryGirl.create :build, commit: commit } -- cgit v1.2.1 From fefa98b21a228163bf7ab250ed5f5352eb60497a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 15:08:01 +0200 Subject: Make rspec start --- app/models/project_services/ci/hip_chat_service.rb | 2 +- app/models/project_services/ci/mail_service.rb | 2 +- app/models/project_services/ci/slack_service.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb index 68acf71251e..0e6e97394bc 100644 --- a/app/models/project_services/ci/hip_chat_service.rb +++ b/app/models/project_services/ci/hip_chat_service.rb @@ -13,7 +13,7 @@ # module Ci - class HipChatService < Service + class HipChatService < Ci::Service prop_accessor :hipchat_token, :hipchat_room, :hipchat_server boolean_accessor :notify_only_broken_builds validates :hipchat_token, presence: true, if: :activated? diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index 3619a50fa96..1bd2f33612b 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -13,7 +13,7 @@ # module Ci - class MailService < Service + class MailService < Ci::Service delegate :email_recipients, :email_recipients=, :email_add_pusher, :email_add_pusher=, :email_only_broken_builds, :email_only_broken_builds=, to: :project, prefix: false diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb index c9a7f865a25..76db573dc17 100644 --- a/app/models/project_services/ci/slack_service.rb +++ b/app/models/project_services/ci/slack_service.rb @@ -13,7 +13,7 @@ # module Ci - class SlackService < Service + class SlackService < Ci::Service prop_accessor :webhook boolean_accessor :notify_only_broken_builds validates :webhook, presence: true, if: :activated? -- cgit v1.2.1 From 870f553bf3ec63d9f137afd0b67e8a338a745027 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 15:32:20 +0200 Subject: Fix spinach exception --- Gemfile | 1 + Gemfile.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3c88322f637..8a0ab5068a9 100644 --- a/Gemfile +++ b/Gemfile @@ -136,6 +136,7 @@ gem "httparty", '~> 0.13.3' # Colored output to console gem "colored", '~> 1.2' +gem "colorize", '~> 0.5.8' # GitLab settings gem 'settingslogic', '~> 2.0.9' diff --git a/Gemfile.lock b/Gemfile.lock index 2c0d376f238..86f6dd29699 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,7 +116,7 @@ GEM execjs coffee-script-source (1.9.1.1) colored (1.2) - colorize (0.7.7) + colorize (0.5.8) connection_pool (2.2.0) coveralls (0.8.2) json (~> 1.8) @@ -801,6 +801,7 @@ DEPENDENCIES charlock_holmes (~> 0.6.9.4) coffee-rails (~> 4.1.0) colored (~> 1.2) + colorize (~> 0.5.8) coveralls (~> 0.8.2) creole (~> 0.3.6) d3_rails (~> 3.5.5) -- cgit v1.2.1 From d516b02eb4e40aa0c5c3f4a1d5d957f5657c2426 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 15:41:21 +0200 Subject: Fix brakeman --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- app/controllers/ci/admin/builds_controller.rb | 12 +++++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 8a0ab5068a9..9de460b45c1 100644 --- a/Gemfile +++ b/Gemfile @@ -216,7 +216,7 @@ gem 'virtus', '~> 1.0.1' group :development do gem "foreman" - gem 'brakeman', require: false + gem 'brakeman', '3.0.1', require: false gem "annotate", "~> 2.6.0" gem "letter_opener", '~> 1.1.2' diff --git a/Gemfile.lock b/Gemfile.lock index 86f6dd29699..f252d33c9ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,14 +72,14 @@ GEM bootstrap-sass (3.3.5) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.2.19) - brakeman (3.0.5) + brakeman (3.0.1) erubis (~> 2.6) fastercsv (~> 1.5) haml (>= 3.0, < 5.0) highline (~> 1.6.20) multi_json (~> 1.2) ruby2ruby (~> 2.1.1) - ruby_parser (~> 3.7.0) + ruby_parser (~> 3.5.0) sass (~> 3.0) terminal-table (~> 1.4) browser (1.0.0) @@ -606,7 +606,7 @@ GEM ruby2ruby (2.1.4) ruby_parser (~> 3.1) sexp_processor (~> 4.0) - ruby_parser (3.7.1) + ruby_parser (3.5.0) sexp_processor (~> 4.1) rubyntlm (0.5.2) rubypants (0.2.0) @@ -791,7 +791,7 @@ DEPENDENCIES better_errors (~> 1.0.1) binding_of_caller (~> 0.7.2) bootstrap-sass (~> 3.0) - brakeman + brakeman (= 3.0.1) browser (~> 1.0.0) byebug cal-heatmap-rails (~> 0.0.1) diff --git a/app/controllers/ci/admin/builds_controller.rb b/app/controllers/ci/admin/builds_controller.rb index 8fc776dd98e..38abfdeafbf 100644 --- a/app/controllers/ci/admin/builds_controller.rb +++ b/app/controllers/ci/admin/builds_controller.rb @@ -4,9 +4,15 @@ module Ci @scope = params[:scope] @builds = Ci::Build.order('created_at DESC').page(params[:page]).per(30) - if ["pending", "running"].include? @scope - @builds = @builds.send(@scope) - end + @builds = + case @scope + when "pending" + @builds.pending + when "running" + @builds.running + else + @builds + end end end end -- cgit v1.2.1 From 1f0fa156b5c955bdaf8b0b2dca8002b9b22d2304 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 15:46:21 +0200 Subject: Copy ci yml config before tests --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ddf4e31204a..3a4cc0d4abc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,6 +5,7 @@ before_script: - which ruby - gem install bundler --no-ri --no-rdoc - cp config/gitlab.yml.example config/gitlab.yml + - cp config/gitlab_ci.yml.example config/gitlab_ci.yml - touch log/application.log - touch log/test.log - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" -- cgit v1.2.1 From 81f9ee48b161496cfd7c033e10dcecc52c9b50be Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 16:08:09 +0200 Subject: Fix mysql migration --- db/migrate/limits_to_mysql.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb index 60e65914f48..73605d4c5e3 100644 --- a/db/migrate/limits_to_mysql.rb +++ b/db/migrate/limits_to_mysql.rb @@ -8,7 +8,7 @@ class LimitsToMysql < ActiveRecord::Migration change_column :notes, :st_diff, :text, limit: 2147483647 # CI - change_column :builds, :trace, :text, limit: 1073741823 - change_column :commits, :push_data, :text, limit: 16777215 + change_column :ci_builds, :trace, :text, limit: 1073741823 + change_column :ci_commits, :push_data, :text, limit: 16777215 end end -- cgit v1.2.1 From 44261a5d9fd5b78f8a44fe330e2386525f4c3437 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 9 Sep 2015 17:36:01 +0300 Subject: integration with gitlab auth --- app/controllers/ci/application_controller.rb | 61 +++++++------------------- app/controllers/ci/builds_controller.rb | 2 +- app/controllers/ci/commits_controller.rb | 2 +- app/controllers/ci/projects_controller.rb | 11 +++-- app/controllers/ci/user_sessions_controller.rb | 10 ----- app/helpers/ci/commits_helper.rb | 2 +- app/models/ability.rb | 1 + app/models/ci/user.rb | 32 +------------- app/views/ci/builds/_build.html.haml | 2 +- app/views/ci/builds/show.html.haml | 12 ++--- app/views/ci/commits/_commit.html.haml | 2 +- app/views/ci/commits/show.html.haml | 4 +- app/views/ci/projects/_gl_projects.html.haml | 2 +- app/views/ci/projects/gitlab.html.haml | 4 -- app/views/ci/projects/show.html.haml | 2 +- app/views/ci/user_sessions/show.html.haml | 2 +- app/views/layouts/ci/_info.html.haml | 2 +- app/views/layouts/ci/_nav.html.haml | 6 +-- app/views/layouts/ci/project.html.haml | 2 +- lib/ci/api/projects.rb | 10 ++--- spec/models/ci/user_spec.rb | 50 --------------------- 21 files changed, 51 insertions(+), 170 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 95390d09737..e5c99066a68 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -1,5 +1,5 @@ module Ci - class ApplicationController < ActionController::Base + class ApplicationController < ::ApplicationController def self.railtie_helpers_paths "app/helpers/ci" end @@ -9,49 +9,19 @@ module Ci rescue_from Ci::Network::UnauthorizedError, with: :invalid_token before_filter :default_headers #before_filter :check_config + helper_method :gl_project protect_from_forgery - helper_method :current_user - before_filter :reset_cache - private - def current_user - @current_user ||= session[:ci_current_user] - end - - def sign_in(user) - session[:ci_current_user] = user - end - - def sign_out - reset_session - end - - def authenticate_user! - unless current_user - redirect_to new_ci_user_sessions_path - return - end - end - - def authenticate_admin! - unless current_user && current_user.is_admin - redirect_to new_ci_user_sessions_path - return - end - end - def authenticate_public_page! unless project.public unless current_user - redirect_to(new_ci_user_sessions_path(state: generate_oauth_state(request.fullpath))) and return + redirect_to(new_user_sessions_path) and return end - unless current_user.can_access_project?(project.gitlab_id) - page_404 and return - end + return access_denied! unless can?(current_user, :read_project, gl_project) end end @@ -62,19 +32,23 @@ module Ci end def authorize_access_project! - unless current_user.can_access_project?(@project.gitlab_id) + unless can?(current_user, :read_project, gl_project) return page_404 end end - def authorize_project_developer! - unless current_user.has_developer_access?(@project.gitlab_id) + def authorize_manage_builds! + unless can?(current_user, :manage_builds, gl_project) return page_404 end end + def authenticate_admin! + return render_404 unless current_user.is_admin? + end + def authorize_manage_project! - unless current_user.can_manage_project?(@project.gitlab_id) + unless can?(current_user, :manage_project, gl_project) return page_404 end end @@ -83,13 +57,6 @@ module Ci render file: "#{Rails.root}/public/404.html", status: 404, layout: false end - # Reset user cache every day for security purposes - def reset_cache - if current_user && current_user.sync_at < (Time.zone.now - 24.hours) - current_user.reset_cache - end - end - def default_headers headers['X-Frame-Options'] = 'DENY' headers['X-XSS-Protection'] = '1; mode=block' @@ -129,5 +96,9 @@ module Ci reset_session redirect_to ci_root_path end + + def gl_project + ::Project.find(@project.gitlab_id) + end end end diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb index eeff3f1e0a0..28fad3671f7 100644 --- a/app/controllers/ci/builds_controller.rb +++ b/app/controllers/ci/builds_controller.rb @@ -5,7 +5,7 @@ module Ci before_filter :project before_filter :authorize_access_project!, except: [:status, :show] before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel] - before_filter :authorize_project_developer!, only: [:retry, :cancel] + before_filter :authorize_manage_builds!, only: [:retry, :cancel] before_filter :build, except: [:show] def show diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb index 9f74a2fd807..bad9075dde6 100644 --- a/app/controllers/ci/commits_controller.rb +++ b/app/controllers/ci/commits_controller.rb @@ -4,7 +4,7 @@ module Ci before_filter :authenticate_public_page!, only: :show before_filter :project before_filter :authorize_access_project!, except: [:status, :show, :cancel] - before_filter :authorize_project_developer!, only: [:cancel] + before_filter :authorize_manage_builds!, only: [:cancel] before_filter :commit, only: :show def show diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 6ff7fc9f77a..80a5e602171 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -21,12 +21,15 @@ module Ci @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i @page = @offset == 0 ? 1 : (@offset / @limit + 1) - current_user.reset_cache if params[:reset_cache] + @gl_projects = current_user.authorized_projects + @gl_projects = @gl_projects.where("name LIKE %?%", params[:search]) if params[:search] + @gl_projects = @gl_projects.page(@page).per(@limit) - @gl_projects = current_user.gitlab_projects(params[:search], @page, @limit) @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date @total_count = @gl_projects.size - @gl_projects.reject! { |gl_project| @projects.map(&:gitlab_id).include?(gl_project.id) } + + @gl_projects = @gl_projects.where.not(id: @projects.map(&:gitlab_id)) + respond_to do |format| format.json do pager_json("ci/projects/gitlab", @total_count) @@ -52,7 +55,7 @@ module Ci def create project_data = OpenStruct.new(JSON.parse(params["project"])) - unless current_user.can_manage_project?(project_data.id) + unless can?(current_user, :manage_project, ::Project.find(project_data.id)) return redirect_to ci_root_path, alert: 'You have to have at least master role to enable CI for this project' end diff --git a/app/controllers/ci/user_sessions_controller.rb b/app/controllers/ci/user_sessions_controller.rb index 82134c1f7ba..818e1fcdea1 100644 --- a/app/controllers/ci/user_sessions_controller.rb +++ b/app/controllers/ci/user_sessions_controller.rb @@ -10,11 +10,6 @@ module Ci end def auth - unless is_oauth_state_valid?(params[:state]) - redirect_to new_ci_user_sessions_path - return - end - redirect_to client.auth_code.authorize_url({ redirect_uri: callback_ci_user_sessions_url, state: params[:state] @@ -22,11 +17,6 @@ module Ci end def callback - unless is_oauth_state_valid?(params[:state]) - redirect_to new_ci_user_sessions_path - return - end - token = client.auth_code.get_token(params[:code], redirect_uri: callback_ci_user_sessions_url).token @user_session = Ci::UserSession.new diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb index 0479bc10594..748d12138b1 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -16,7 +16,7 @@ module Ci end def commit_link(commit) - link_to(commit.short_sha, ci_project_ref_commit_path(commit.project, commit.ref, commit.sha)) + link_to(commit.short_sha, ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)) end def truncate_first_line(message, length = 50) diff --git a/app/models/ability.rb b/app/models/ability.rb index f8e5afa9b01..a020b24a550 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -149,6 +149,7 @@ class Ability :admin_merge_request, :create_merge_request, :create_wiki, + :manage_builds, :push_code ] end diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb index 49ec7126fc1..f7e2eaae1f1 100644 --- a/app/models/ci/user.rb +++ b/app/models/ci/user.rb @@ -39,43 +39,13 @@ module Ci @sync_at = Time.now end - def can_access_project?(project_gitlab_id) - !!project_info(project_gitlab_id) - end - - # Indicate if user has developer access or higher - def has_developer_access?(project_gitlab_id) - data = project_info(project_gitlab_id) - - return false unless data && data["permissions"] - - permissions = data["permissions"] - - if permissions["project_access"] && permissions["project_access"]["access_level"] >= DEVELOPER_ACCESS - return true - end - - if permissions["group_access"] && permissions["group_access"]["access_level"] >= DEVELOPER_ACCESS - return true - end - end - - def can_manage_project?(project_gitlab_id) - Rails.cache.fetch(cache_key('manage', project_gitlab_id, sync_at)) do - !!Ci::Network.new.project_hooks(authenticate_options, project_gitlab_id) - end - end - def authorized_runners Ci::Runner.specific.includes(:runner_projects). where(Ci::RunnerProject.table_name => { project_id: authorized_projects } ) end def authorized_projects - Ci::Project.where(gitlab_id: gitlab_projects.map(&:id)).select do |project| - # This is slow: it makes request to GitLab for each project to verify manage permission - can_manage_project?(project.gitlab_id) - end + Ci::Project.where(gitlab_id: current_user.authorized_projects.map(&:id)) end def authenticate_options diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml index 54ca1102b5c..da306c9f020 100644 --- a/app/views/ci/builds/_build.html.haml +++ b/app/views/ci/builds/_build.html.haml @@ -35,7 +35,7 @@ #{build.coverage}% %td - - if defined?(controls) && current_user && current_user.has_developer_access?(@project.gitlab_id) + - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project) .pull-right - if build.active? = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index 0bef67d8a20..a698176c3ed 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -1,10 +1,10 @@ %h4.page-title - = link_to @project.name, @project + = link_to ci_project_path(@project) @ = @commit.short_sha %p - = link_to ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) do + = link_to ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) do ← Back to project commit %hr #up-build-trace @@ -12,7 +12,7 @@ %ul.nav.nav-tabs.append-bottom-10 - @commit.builds_without_retry_sorted.each do |build| %li{class: ('active' if build == @build) } - = link_to ci_build_url(build) do + = link_to ci_project_build_url(@project, build) do %i{class: build_icon_css_class(build)} %span Build ##{build.id} @@ -84,7 +84,7 @@ .build-widget %h4.title Build - - if current_user && current_user.has_developer_access?(@project.gitlab_id) + - if current_user && can?(current_user, :manage_builds, gl_project) .pull-right - if @build.active? = link_to "Cancel", cancel_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-danger' @@ -161,7 +161,7 @@ - @builds.each_with_index do |build, i| %tr.build.alert{class: build_status_alert_class(build)} %td - = link_to ci_build_url(build) do + = link_to ci_project_build_url(@project, build) do %span ##{build.id} %td - if build.name @@ -173,4 +173,4 @@ :javascript - new CiBuild("#{ci_build_url(@build)}", "#{@build.status}") + new CiBuild("#{ci_project_build_url(@project, @build)}", "#{@build.status}") diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index a955a5b6479..c1b1988d147 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -7,7 +7,7 @@ %td.build-link - = link_to ci_project_ref_commit_path(commit.project, commit.ref, commit.sha) do + = link_to ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) do %strong #{commit.short_sha} %td.build-message diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 832cc6a1bae..9b597b45aa5 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -33,10 +33,10 @@ %span.attr-name Created at: #{@commit.created_at.to_s(:short)} -- if current_user && current_user.has_developer_access?(@project.gitlab_id) +- if current_user && can?(current_user, :manage_builds, gl_project) .pull-right - if @commit.builds.running_or_pending.any? - = link_to "Cancel", cancel_ci_project_ref_commit_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger' + = link_to "Cancel", cancel_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger' - if @commit.yaml_errors.present? diff --git a/app/views/ci/projects/_gl_projects.html.haml b/app/views/ci/projects/_gl_projects.html.haml index 6ed19e13887..f63d8246e96 100644 --- a/app/views/ci/projects/_gl_projects.html.haml +++ b/app/views/ci/projects/_gl_projects.html.haml @@ -11,5 +11,5 @@ Added - else = form_tag ci_projects_path do - = hidden_field_tag :project, project.to_h.to_json + = hidden_field_tag :project, project.to_json = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm' diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml index bd55b1f12e7..0344676680e 100644 --- a/app/views/ci/projects/gitlab.html.haml +++ b/app/views/ci/projects/gitlab.html.haml @@ -4,10 +4,6 @@ Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink}) - if params[:search].present? by keyword: "#{params[:search]}", - #{time_ago_in_words(current_user.sync_at)} ago. - = link_to gitlab_ci_projects_path(reset_cache: true, search: params[:search]), class: 'sync-now btn btn-sm btn-default reset-cache' do - %i.fa.fa-refresh - Sync now %br .pull-right diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml index 27899591391..b79ab957ba8 100644 --- a/app/views/ci/projects/show.html.haml +++ b/app/views/ci/projects/show.html.haml @@ -1,6 +1,6 @@ = render 'ci/shared/guide' unless @project.setup_finished? -- if current_user && current_user.can_manage_project?(@project.gitlab_id) && !@project.any_runners? +- if current_user && can?(current_user, :manage_project, gl_project) && !@project.any_runners? .alert.alert-danger Builds for this project wont be served unless you configure runners on = link_to "Runners page", ci_project_runners_path(@project) diff --git a/app/views/ci/user_sessions/show.html.haml b/app/views/ci/user_sessions/show.html.haml index 43f64a429b2..0710e205618 100644 --- a/app/views/ci/user_sessions/show.html.haml +++ b/app/views/ci/user_sessions/show.html.haml @@ -2,7 +2,7 @@ %h3 Hi, #{@user.name} - - if @user.is_admin + - if @user.is_admin? %span.label.label-success Admin .profile-block diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml index bce3ce77031..9bc23cfad05 100644 --- a/app/views/layouts/ci/_info.html.haml +++ b/app/views/layouts/ci/_info.html.haml @@ -5,5 +5,5 @@ - if notice .alert.alert-info= notice - - if current_user && current_user.is_admin && Ci::Runner.count.zero? + - if current_user && current_user.is_admin? && Ci::Runner.count.zero? = render 'ci/shared/no_runners' diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml index babd14ca2d3..4a0e45a0217 100644 --- a/app/views/layouts/ci/_nav.html.haml +++ b/app/views/layouts/ci/_nav.html.haml @@ -9,7 +9,7 @@ .collapse.navbar-collapse %ul.nav.navbar-nav - - if current_user && current_user.is_admin + - if current_user && current_user.is_admin? %li = link_to ci_admin_projects_path do Admin @@ -19,12 +19,12 @@ %ul.nav.navbar-nav.pull-right - if current_user %li - = link_to ci_user_sessions_path do + = link_to new_user_session_path do .profile-holder = image_tag user_avatar_url(current_user, 64), class: 'avatar s32', alt: '' %span= current_user.name %li - = link_to ci_user_sessions_path, class: "logout", method: :delete do + = link_to destroy_user_session_path, class: "logout", method: :delete do %i.fa.fa-signout Logout - else diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index 763a7fc0b02..9549485d095 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -16,7 +16,7 @@ = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' ) %hr .container - - if current_user && current_user.can_manage_project?(@project.gitlab_id) + - if current_user && can?(current_user, :manage_project, gl_project) .row .col-md-2.append-bottom-20 = render 'layouts/ci/nav_project' diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index f9b4937c033..bdcacecf6ab 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -66,7 +66,7 @@ module Ci get ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless current_user.can_access_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :read_project, gl_project) present project, with: Entities::Project end @@ -118,7 +118,7 @@ module Ci put ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :manage_project, gl_project) attrs = attributes_for_keys [:name, :gitlab_id, :path, :gitlab_url, :default_ref, :ssh_url_to_repo] @@ -144,7 +144,7 @@ module Ci delete ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :manage_project, gl_project) project.destroy end @@ -160,7 +160,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :manage_project, gl_project) options = { project_id: project.id, @@ -188,7 +188,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :manage_project, gl_project) options = { project_id: project.id, diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb index d1b87988b74..c4d7b3ccae5 100644 --- a/spec/models/ci/user_spec.rb +++ b/spec/models/ci/user_spec.rb @@ -2,56 +2,6 @@ require 'spec_helper' describe Ci::User do - describe "has_developer_access?" do - before do - @user = User.new({}) - end - - let(:project_with_owner_access) do - { - "name" => "gitlab-shell", - "permissions" => { - "project_access" => { - "access_level"=> 10, - "notification_level" => 3 - }, - "group_access" => { - "access_level" => 50, - "notification_level" => 3 - } - } - } - end - - let(:project_with_reporter_access) do - { - "name" => "gitlab-shell", - "permissions" => { - "project_access" => { - "access_level" => 20, - "notification_level" => 3 - }, - "group_access" => { - "access_level" => 10, - "notification_level" => 3 - } - } - } - end - - it "returns false for reporter" do - @user.stub(:project_info).and_return(project_with_reporter_access) - - @user.has_developer_access?(1).should be_false - end - - it "returns true for owner" do - @user.stub(:project_info).and_return(project_with_owner_access) - - @user.has_developer_access?(1).should be_true - end - end - describe "authorized_projects" do let (:user) { User.new({}) } -- cgit v1.2.1 From d7501c7bb914151ac77a9c07342a7d129724cb6c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 10 Sep 2015 09:25:41 +0100 Subject: Fix sudo_gitlab helper --- lib/tasks/gitlab/check.rake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index bcd25ccbaf3..b8eb13a4fea 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -882,10 +882,8 @@ namespace :gitlab do "doc/install/installation.md in section \"#{section}\"" end - def sudo_gitlab(command, env = nil) - cmd = "sudo -u #{gitlab_user} -H #{command}" - cmd.prepend "#{env} " if env - cmd + def sudo_gitlab(command) + "sudo -u #{gitlab_user} -H #{command}" end def gitlab_user -- cgit v1.2.1 From 381180bc27b7c4f0d54ca4a1599ad5d857a2086e Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 10 Sep 2015 13:42:41 +0300 Subject: remove gitlab_ci config --- app/helpers/ci/routes_helper.rb | 6 +-- app/helpers/ci/triggers_helper.rb | 2 +- app/mailers/ci/notify.rb | 8 ++-- app/models/ci/application_setting.rb | 4 +- app/models/ci/build.rb | 2 +- app/models/ci/project.rb | 2 +- app/views/ci/projects/gitlab.html.haml | 1 - app/views/ci/projects/index.html.haml | 5 --- config/gitlab_ci.yml.example | 68 -------------------------------- config/gitlab_ci.yml.example.development | 19 --------- config/initializers/1_settings.rb | 24 ++++++++++- config/initializers/3_ci_settings.rb | 61 ---------------------------- config/initializers/4_ci_app.rb | 2 +- 13 files changed, 36 insertions(+), 168 deletions(-) delete mode 100644 config/gitlab_ci.yml.example delete mode 100644 config/gitlab_ci.yml.example.development delete mode 100644 config/initializers/3_ci_settings.rb diff --git a/app/helpers/ci/routes_helper.rb b/app/helpers/ci/routes_helper.rb index f22d5023db5..299a96edee4 100644 --- a/app/helpers/ci/routes_helper.rb +++ b/app/helpers/ci/routes_helper.rb @@ -5,9 +5,9 @@ module Ci def default_url_options { - host: Ci::Settings.gitlab_ci['host'], - protocol: Ci::Settings.gitlab_ci['https'] ? "https" : "http", - port: Ci::Settings.gitlab_ci['port'] + host: Settings.gitlab['host'], + protocol: Settings.gitlab['https'] ? "https" : "http", + port: Settings.gitlab['port'] } end end diff --git a/app/helpers/ci/triggers_helper.rb b/app/helpers/ci/triggers_helper.rb index caff54c3520..0d62bbf36b8 100644 --- a/app/helpers/ci/triggers_helper.rb +++ b/app/helpers/ci/triggers_helper.rb @@ -1,7 +1,7 @@ module Ci module TriggersHelper def build_trigger_url(project_id, ref_name) - "#{Ci::Settings.gitlab_ci.url}/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" + "#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" end end end diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb index 44e490e9b36..6dcc118ac05 100644 --- a/app/mailers/ci/notify.rb +++ b/app/mailers/ci/notify.rb @@ -5,10 +5,10 @@ module Ci add_template_helper Ci::ApplicationHelper add_template_helper Ci::GitlabHelper - default_url_options[:host] = GitlabCi.config.gitlab_ci.host - default_url_options[:protocol] = GitlabCi.config.gitlab_ci.protocol - default_url_options[:port] = GitlabCi.config.gitlab_ci.port if GitlabCi.config.gitlab_ci_on_non_standard_port? - default_url_options[:script_name] = GitlabCi.config.gitlab_ci.relative_url_root + default_url_options[:host] = Gitlab.config.gitlab.host + default_url_options[:protocol] = Gitlab.config.gitlab.protocol + default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port? + default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root default from: GitlabCi.config.gitlab_ci.email_from diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 0ea2452e392..0cf496f7d81 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -19,8 +19,8 @@ module Ci def self.create_from_defaults create( - all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'], - add_pusher: Ci::Settings.gitlab_ci['add_pusher'], + all_broken_builds: Settings.gitlab_ci['all_broken_builds'], + add_pusher: Settings.gitlab_ci['add_pusher'], ) end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 64e7a600672..8096d4fa5ae 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -244,7 +244,7 @@ module Ci def dir_to_trace File.join( - Ci::Settings.gitlab_ci.builds_path, + Settings.gitlab_ci.builds_path, created_at.utc.strftime("%Y_%m"), project.id.to_s ) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index dceca7a275a..9c9198302f6 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -211,7 +211,7 @@ module Ci end def gitlab_url - File.join(GitlabCi.config.gitlab_server.url, path) + File.join(Gitlab.config.gitlab.url, path) end def setup_finished? diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml index 0344676680e..690b6cf3cdb 100644 --- a/app/views/ci/projects/gitlab.html.haml +++ b/app/views/ci/projects/gitlab.html.haml @@ -1,7 +1,6 @@ - if @offset == 0 .clearfix.light .pull-left.fetch-status - Fetched from GitLab (#{link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink}) - if params[:search].present? by keyword: "#{params[:search]}", %br diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 69b6c8b4d6d..99d07329af0 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -8,14 +8,9 @@ .projects %p.fetch-status.light %i.fa.fa-refresh.fa-spin - Please wait while we fetch from GitLab (#{GitlabCi.config.gitlab_server.url}) :coffeescript $.get '#{gitlab_ci_projects_path}', (data) -> $(".projects").html data.html - $('.projects').on 'click', '.reset-cache', -> - $.get '#{gitlab_ci_projects_path}', { reset_cache: true }, (data) -> - $(".projects").html data.html - false CiPager.init "#{gitlab_ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false - else diff --git a/config/gitlab_ci.yml.example b/config/gitlab_ci.yml.example deleted file mode 100644 index dd33daa5578..00000000000 --- a/config/gitlab_ci.yml.example +++ /dev/null @@ -1,68 +0,0 @@ -# If you change this file in a Merge Request, please also create -# a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests - -defaults: &defaults - gitlab_server: - url: 'https://gitlab.example.com/' # Replace with your gitlab server url - app_id: '' - app_secret: '' - - ## Gitlab CI settings - gitlab_ci: - ## Web server settings - host: localhost - port: 80 - https: false - - ## Email settings - # Email address used in the "From" field in mails sent by GitLab-CI - email_from: gitlab-ci@localhost - - # Email address of your support contact (default: same as email_from) - support_email: support@localhost - - # Default project notifications settings: - # - # Send emails only on broken builds (default: true) - # all_broken_builds: true - # - # Add pusher to recipients list (default: false) - # add_pusher: true - - # The location where build traces are stored (default: builds/). Relative paths are relative to Rails.root - # builds_path: builds/ - - ## Backup settings - backup: - path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) - # keep_time: 604800 # default: 0 (forever) (in seconds) - # upload: - # # Fog storage connection settings, see http://fog.io/storage/ . - # connection: - # provider: AWS - # region: eu-west-1 - # aws_access_key_id: AKIAKIAKI - # aws_secret_access_key: 'secret123' - # # The remote 'directory' to store your backups. For S3, this would be the bucket name. - # remote_directory: 'my.s3.bucket' - # # Use multipart uploads when file size reaches 100MB, see - # # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html - # multipart_chunk_size: 104857600 - - -development: - <<: *defaults - -test: - <<: *defaults - gitlab_server: - url: 'http://demo.gitlab.com/' - app_id: 'id' - app_secret: 'secret' - gitlab_ci: - host: localhost - port: 80 - https: false - -production: - <<: *defaults diff --git a/config/gitlab_ci.yml.example.development b/config/gitlab_ci.yml.example.development deleted file mode 100644 index d23c4daf464..00000000000 --- a/config/gitlab_ci.yml.example.development +++ /dev/null @@ -1,19 +0,0 @@ -development: - gitlab_server: - url: 'http://localhost:3000' - app_id: '' - app_secret: '' - - gitlab_ci: - host: localhost - port: 9000 - https: false -test: - gitlab_server: - url: 'http://demo.gitlab.com/' - app_id: '' - app_secret: '' - gitlab_ci: - host: localhost - port: 80 - https: false diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 689c3f3049d..3893bd45cf5 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -18,7 +18,19 @@ class Settings < Settingslogic host.start_with?('www.') ? host[4..-1] : host end - private + def build_gitlab_ci_url + if gitlab_on_standard_port? + custom_port = nil + else + custom_port = ":#{gitlab.port}" + end + [ gitlab.protocol, + "://", + gitlab.host, + custom_port, + gitlab.relative_url_root + ].join('') + end def build_gitlab_shell_ssh_path_prefix if gitlab_shell.ssh_port != 22 @@ -160,6 +172,16 @@ Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitla Settings.gitlab['restricted_signup_domains'] ||= [] Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] + +# +# CI +# +Settings['gitlab_ci'] ||= Settingslogic.new({}) +Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? +Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? +Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) +Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root + '/ci') + # # Reply by email # diff --git a/config/initializers/3_ci_settings.rb b/config/initializers/3_ci_settings.rb deleted file mode 100644 index 5cdff48d316..00000000000 --- a/config/initializers/3_ci_settings.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Ci - class Settings < Settingslogic - source "#{Rails.root}/config/gitlab_ci.yml" - namespace Rails.env - - class << self - def gitlab_ci_on_non_standard_port? - ![443, 80].include?(gitlab_ci.port.to_i) - end - - private - - def build_gitlab_ci_url - if gitlab_ci_on_non_standard_port? - custom_port = ":#{gitlab_ci.port}" - else - custom_port = nil - end - [ gitlab_ci.protocol, - "://", - gitlab_ci.host, - custom_port, - gitlab_ci.relative_url_root - ].join('') - end - end - end -end - - -# -# GitlabCi -# -Ci::Settings['gitlab_ci'] ||= Settingslogic.new({}) -Ci::Settings.gitlab_ci['https'] = false if Ci::Settings.gitlab_ci['https'].nil? -Ci::Settings.gitlab_ci['host'] ||= 'localhost' -Ci::Settings.gitlab_ci['port'] ||= Ci::Settings.gitlab_ci.https ? 443 : 80 -Ci::Settings.gitlab_ci['relative_url_root'] ||= (ENV['RAILS_RELATIVE_URL_ROOT'] || '') + '/ci' -Ci::Settings.gitlab_ci['protocol'] ||= Ci::Settings.gitlab_ci.https ? "https" : "http" -Ci::Settings.gitlab_ci['email_from'] ||= "gitlab-ci@#{Ci::Settings.gitlab_ci.host}" -Ci::Settings.gitlab_ci['support_email'] ||= Ci::Settings.gitlab_ci.email_from -Ci::Settings.gitlab_ci['all_broken_builds'] = true if Ci::Settings.gitlab_ci['all_broken_builds'].nil? -Ci::Settings.gitlab_ci['add_pusher'] = false if Ci::Settings.gitlab_ci['add_pusher'].nil? -Ci::Settings.gitlab_ci['url'] ||= Ci::Settings.send(:build_gitlab_ci_url) -Ci::Settings.gitlab_ci['builds_path'] = File.expand_path(Ci::Settings.gitlab_ci['builds_path'] || "builds/", Rails.root + '/ci') - -# Compatibility with old config -Ci::Settings['gitlab_server_urls'] ||= Ci::Settings['allowed_gitlab_urls'] - -# -# Backup -# -Ci::Settings['backup'] ||= Settingslogic.new({}) -Ci::Settings.backup['keep_time'] ||= 0 -Ci::Settings.backup['path'] = File.expand_path(Ci::Settings.backup['path'] || "tmp/backups/", Rails.root) -Ci::Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) -# Convert upload connection settings to use symbol keys, to make Fog happy -if Ci::Settings.backup['upload']['connection'] - Ci::Settings.backup['upload']['connection'] = Hash[Ci::Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }] -end -Ci::Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 diff --git a/config/initializers/4_ci_app.rb b/config/initializers/4_ci_app.rb index 60a30bf3bb7..cac8edb32bf 100644 --- a/config/initializers/4_ci_app.rb +++ b/config/initializers/4_ci_app.rb @@ -5,6 +5,6 @@ module GitlabCi REGISTRATION_TOKEN = SecureRandom.hex(10) def self.config - Ci::Settings + Settings end end -- cgit v1.2.1 From 10b3c85e2c7c3e794a0c79d33b1a7bc2b1b6b7c8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 15:47:15 +0200 Subject: Fix some issues with ci models specs --- app/models/ci/web_hook.rb | 4 +-- spec/factories/ci/web_hook.rb | 4 +-- spec/models/ci/project_spec.rb | 56 ++++++++++++++++++------------------ spec/models/ci/runner_spec.rb | 16 +++++------ spec/models/ci/service_spec.rb | 6 ++-- spec/models/ci/trigger_spec.rb | 6 ++-- spec/models/ci/user_spec.rb | 14 ++++----- spec/models/ci/web_hook_spec.rb | 2 +- spec/support/setup_builds_storage.rb | 2 +- 9 files changed, 55 insertions(+), 55 deletions(-) diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb index 4b8c65a1a65..8f03b0625da 100644 --- a/app/models/ci/web_hook.rb +++ b/app/models/ci/web_hook.rb @@ -12,10 +12,10 @@ module Ci class WebHook < ActiveRecord::Base extend Ci::Model - + include HTTParty - belongs_to :project, class_name: 'Ci::WebHook' + belongs_to :project, class_name: 'Ci::Project' # HTTParty timeout default_timeout 10 diff --git a/spec/factories/ci/web_hook.rb b/spec/factories/ci/web_hook.rb index 1fde5805c94..40d878ecb3c 100644 --- a/spec/factories/ci/web_hook.rb +++ b/spec/factories/ci/web_hook.rb @@ -1,6 +1,6 @@ FactoryGirl.define do factory :ci_web_hook, class: Ci::WebHook do - sequence(:url) { Faker::Internet.uri('http') } - project + sequence(:url) { FFaker::Internet.uri('http') } + project factory: :ci_project end end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index aa76b99154b..3f61545da14 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -28,7 +28,7 @@ require 'spec_helper' describe Project do - subject { FactoryGirl.build :project } + subject { FactoryGirl.build :ci_project } it { should have_many(:commits) } @@ -38,36 +38,36 @@ describe Project do describe 'before_validation' do it 'should set an random token if none provided' do - project = FactoryGirl.create :project_without_token + project = FactoryGirl.create :ci_project_without_token project.token.should_not == "" end it 'should not set an random toke if one provided' do - project = FactoryGirl.create :project + project = FactoryGirl.create :ci_project project.token.should == "iPWx6WM4lhHNedGfBpPJNP" end end describe "ordered_by_last_commit_date" do it "returns ordered projects" do - newest_project = FactoryGirl.create :project - oldest_project = FactoryGirl.create :project - project_without_commits = FactoryGirl.create :project + newest_project = FactoryGirl.create :ci_project + oldest_project = FactoryGirl.create :ci_project + project_without_commits = FactoryGirl.create :ci_project - FactoryGirl.create :commit, committed_at: 1.hour.ago, project: newest_project - FactoryGirl.create :commit, committed_at: 2.hour.ago, project: oldest_project + FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: newest_project + FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: oldest_project Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits] end end context :valid_project do - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } context :project_with_commit_and_builds do before do - commit = FactoryGirl.create(:commit, project: project) - FactoryGirl.create(:build, commit: commit) + commit = FactoryGirl.create(:ci_commit, project: project) + FactoryGirl.create(:ci_build, commit: commit) end it { project.status.should == 'pending' } @@ -78,45 +78,45 @@ describe Project do describe '#email_notification?' do it do - project = FactoryGirl.create :project, email_add_pusher: true + project = FactoryGirl.create :ci_project, email_add_pusher: true project.email_notification?.should == true end it do - project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: 'test tesft' + project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'test tesft' project.email_notification?.should == true end it do - project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: '' + project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: '' project.email_notification?.should == false end end describe '#broken_or_success?' do it { - project = FactoryGirl.create :project, email_add_pusher: true + project = FactoryGirl.create :ci_project, email_add_pusher: true project.stub(:broken?).and_return(true) project.stub(:success?).and_return(true) project.broken_or_success?.should == true } it { - project = FactoryGirl.create :project, email_add_pusher: true + project = FactoryGirl.create :ci_project, email_add_pusher: true project.stub(:broken?).and_return(true) project.stub(:success?).and_return(false) project.broken_or_success?.should == true } it { - project = FactoryGirl.create :project, email_add_pusher: true + project = FactoryGirl.create :ci_project, email_add_pusher: true project.stub(:broken?).and_return(false) project.stub(:success?).and_return(true) project.broken_or_success?.should == true } it { - project = FactoryGirl.create :project, email_add_pusher: true + project = FactoryGirl.create :ci_project, email_add_pusher: true project.stub(:broken?).and_return(false) project.stub(:success?).and_return(false) project.broken_or_success?.should == false @@ -127,7 +127,7 @@ describe Project do let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } let(:parsed_project) { Project.parse(project_dump) } - + it { parsed_project.should be_valid } it { parsed_project.should be_kind_of(Project) } it { parsed_project.name.should eq("GitLab / api.gitlab.org") } @@ -140,7 +140,7 @@ describe Project do end describe :repo_url_with_auth do - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } subject { project.repo_url_with_auth } it { should be_a(String) } @@ -152,7 +152,7 @@ describe Project do end describe :search do - let!(:project) { FactoryGirl.create(:project, name: "foo") } + let!(:project) { FactoryGirl.create(:ci_project, name: "foo") } it { Project.search('fo').should include(project) } it { Project.search('bar').should be_empty } @@ -160,25 +160,25 @@ describe Project do describe :any_runners do it "there are no runners available" do - project = FactoryGirl.create(:project) + project = FactoryGirl.create(:ci_project) project.any_runners?.should be_false end it "there is a specific runner" do - project = FactoryGirl.create(:project) - project.runners << FactoryGirl.create(:specific_runner) + project = FactoryGirl.create(:ci_project) + project.runners << FactoryGirl.create(:ci_specific_runner) project.any_runners?.should be_true end it "there is a shared runner" do - project = FactoryGirl.create(:project, shared_runners_enabled: true) - FactoryGirl.create(:shared_runner) + project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) + FactoryGirl.create(:ci_shared_runner) project.any_runners?.should be_true end it "there is a shared runner, but they are prohibited to use" do - project = FactoryGirl.create(:project) - FactoryGirl.create(:shared_runner) + project = FactoryGirl.create(:ci_project) + FactoryGirl.create(:ci_shared_runner) project.any_runners?.should be_false end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 8677d86aa02..62a719e499b 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -27,7 +27,7 @@ describe Ci::Runner do end it 'should return the token if it does not have a description' do - runner = FactoryGirl.create(:runner) + runner = FactoryGirl.create(:ci_runner) expect(runner.display_name).to eq runner.description end @@ -38,8 +38,8 @@ describe Ci::Runner do end describe :assign_to do - let!(:project) { FactoryGirl.create :project } - let!(:shared_runner) { FactoryGirl.create(:shared_runner) } + let!(:project) { FactoryGirl.create :ci_project } + let!(:shared_runner) { FactoryGirl.create(:ci_shared_runner) } before { shared_runner.assign_to(project) } @@ -50,9 +50,9 @@ describe Ci::Runner do describe "belongs_to_one_project?" do it "returns false if there are two projects runner assigned to" do - runner = FactoryGirl.create(:specific_runner) - project = FactoryGirl.create(:project) - project1 = FactoryGirl.create(:project) + runner = FactoryGirl.create(:ci_specific_runner) + project = FactoryGirl.create(:ci_project) + project1 = FactoryGirl.create(:ci_project) project.runners << runner project1.runners << runner @@ -60,8 +60,8 @@ describe Ci::Runner do end it "returns true" do - runner = FactoryGirl.create(:specific_runner) - project = FactoryGirl.create(:project) + runner = FactoryGirl.create(:ci_specific_runner) + project = FactoryGirl.create(:ci_project) project.runners << runner runner.belongs_to_one_project?.should be_true diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 5a90229ec43..97e2798dbd5 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -29,9 +29,9 @@ describe Ci::Service do end describe "Testable" do - let (:project) { FactoryGirl.create :project } - let (:commit) { FactoryGirl.create :commit, project: project } - let (:build) { FactoryGirl.create :build, commit: commit } + let (:project) { FactoryGirl.create :ci_project } + let (:commit) { FactoryGirl.create :ci_commit, project: project } + let (:build) { FactoryGirl.create :ci_build, commit: commit } before do @service.stub( diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 7c928f9d9dc..6833baccc8f 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,16 +1,16 @@ require 'spec_helper' describe Ci::Trigger do - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } describe 'before_validation' do it 'should set an random token if none provided' do - trigger = FactoryGirl.create :trigger_without_token, project: project + trigger = FactoryGirl.create :ci_trigger_without_token, project: project trigger.token.should_not be_nil end it 'should not set an random token if one provided' do - trigger = FactoryGirl.create :trigger, project: project + trigger = FactoryGirl.create :ci_trigger, project: project trigger.token.should == 'token' end end diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb index c4d7b3ccae5..93338ad5732 100644 --- a/spec/models/ci/user_spec.rb +++ b/spec/models/ci/user_spec.rb @@ -6,8 +6,8 @@ describe Ci::User do let (:user) { User.new({}) } before do - FactoryGirl.create :project, gitlab_id: 1 - FactoryGirl.create :project, gitlab_id: 2 + FactoryGirl.create :ci_project, gitlab_id: 1 + FactoryGirl.create :ci_project, gitlab_id: 2 gitlab_project = OpenStruct.new({id: 1}) gitlab_project1 = OpenStruct.new({id: 2}) User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) @@ -28,17 +28,17 @@ describe Ci::User do describe "authorized_runners" do it "returns authorized runners" do - project = FactoryGirl.create :project, gitlab_id: 1 - project1 = FactoryGirl.create :project, gitlab_id: 2 + project = FactoryGirl.create :ci_project, gitlab_id: 1 + project1 = FactoryGirl.create :ci_project, gitlab_id: 2 gitlab_project = OpenStruct.new({id: 1}) gitlab_project1 = OpenStruct.new({id: 2}) User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) User.any_instance.stub(:can_manage_project?).and_return(true) user = User.new({}) - runner = FactoryGirl.create :specific_runner - runner1 = FactoryGirl.create :specific_runner - runner2 = FactoryGirl.create :specific_runner + runner = FactoryGirl.create :ci_specific_runner + runner1 = FactoryGirl.create :ci_specific_runner + runner2 = FactoryGirl.create :ci_specific_runner project.runners << runner project1.runners << runner1 diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index 4211576ce5e..d5c1d49eec6 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -34,7 +34,7 @@ describe Ci::WebHook do describe "execute" do before(:each) do - @web_hook = FactoryGirl.create(:web_hook) + @web_hook = FactoryGirl.create(:ci_web_hook) @project = @web_hook.project @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb index cafc8dee918..1f3b12bb8d2 100644 --- a/spec/support/setup_builds_storage.rb +++ b/spec/support/setup_builds_storage.rb @@ -5,7 +5,7 @@ RSpec.configure do |config| config.before(:each) do FileUtils.mkdir_p(builds_path) - Ci::Settings.gitlab_ci['builds_path'] = builds_path + Settings.gitlab_ci['builds_path'] = builds_path end config.after(:suite) do -- cgit v1.2.1 From 59c3a3239f338fc48d1a3707c4bd4e4aaa8c03df Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 15:52:52 +0200 Subject: Fix commit specs --- app/models/ci/trigger.rb | 4 +- spec/factories/ci/builds.rb | 2 +- spec/factories/ci/trigger_requests.rb | 2 +- spec/models/ci/build_spec.rb | 90 ++++++++-------- spec/models/ci/commit_spec.rb | 116 +++++++++++---------- spec/models/ci/mail_service_spec.rb | 36 +++---- spec/models/ci/network_spec.rb | 26 ++--- .../ci/project_services/hip_chat_message_spec.rb | 8 +- .../ci/project_services/hip_chat_service_spec.rb | 6 +- .../ci/project_services/slack_message_spec.rb | 44 ++++---- .../ci/project_services/slack_service_spec.rb | 8 +- spec/models/ci/project_spec.rb | 86 +++++++-------- spec/models/ci/runner_spec.rb | 10 +- spec/models/ci/service_spec.rb | 6 +- spec/models/ci/trigger_spec.rb | 4 +- spec/models/ci/user_spec.rb | 18 ++-- spec/models/ci/variable_spec.rb | 8 +- spec/models/ci/web_hook_spec.rb | 30 +++--- 18 files changed, 254 insertions(+), 250 deletions(-) diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 84eab91e8ba..fe224b7dc70 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -13,10 +13,10 @@ module Ci class Trigger < ActiveRecord::Base extend Ci::Model - + acts_as_paranoid - belongs_to :project, class_name: 'Ci::Trigger' + belongs_to :project, class_name: 'Ci::Project' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' validates_presence_of :token diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 35a84b1e6eb..3fe9a89ad1b 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -37,7 +37,7 @@ FactoryGirl.define do } end - factory :not_started_build do + factory :ci_not_started_build do started_at nil finished_at nil end diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb index da8b7342fcf..db053c610cd 100644 --- a/spec/factories/ci/trigger_requests.rb +++ b/spec/factories/ci/trigger_requests.rb @@ -1,7 +1,7 @@ # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do - factory :ci_trigger_request do + factory :ci_trigger_request, class: Ci::TriggerRequest do factory :ci_trigger_request_with_variables do variables do { diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index d1e58438f7b..b62c5862c0c 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -30,14 +30,14 @@ describe Ci::Build do let(:commit) { FactoryGirl.create :ci_commit, project: project } let(:build) { FactoryGirl.create :ci_build, commit: commit } - it { should belong_to(:commit) } - it { should validate_presence_of :status } + it { is_expected.to belong_to(:commit) } + it { is_expected.to validate_presence_of :status } - it { should respond_to :success? } - it { should respond_to :failed? } - it { should respond_to :running? } - it { should respond_to :pending? } - it { should respond_to :trace_html } + it { is_expected.to respond_to :success? } + it { is_expected.to respond_to :failed? } + it { is_expected.to respond_to :running? } + it { is_expected.to respond_to :pending? } + it { is_expected.to respond_to :trace_html } describe :first_pending do let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday } @@ -45,8 +45,8 @@ describe Ci::Build do before { first; second } subject { Ci::Build.first_pending } - it { should be_a(Ci::Build) } - it('returns with the first pending build') { should eq(first) } + it { is_expected.to be_a(Ci::Build) } + it('returns with the first pending build') { is_expected.to eq(first) } end describe :create_from do @@ -69,14 +69,14 @@ describe Ci::Build do context 'without started_at' do before { build.started_at = nil } - it { should be_falsey } + it { is_expected.to be_falsey } end %w(running success failed).each do |status| context "if build status is #{status}" do before { build.status = status } - it { should be_truthy } + it { is_expected.to be_truthy } end end @@ -84,7 +84,7 @@ describe Ci::Build do context "if build status is #{status}" do before { build.status = status } - it { should be_falsey } + it { is_expected.to be_falsey } end end end @@ -96,7 +96,7 @@ describe Ci::Build do context "if build.status is #{state}" do before { build.status = state } - it { should be_truthy } + it { is_expected.to be_truthy } end end @@ -104,7 +104,7 @@ describe Ci::Build do context "if build.status is #{state}" do before { build.status = state } - it { should be_falsey } + it { is_expected.to be_falsey } end end end @@ -116,7 +116,7 @@ describe Ci::Build do context "if build.status is #{state}" do before { build.status = state } - it { should be_truthy } + it { is_expected.to be_truthy } end end @@ -124,7 +124,7 @@ describe Ci::Build do context "if build.status is #{state}" do before { build.status = state } - it { should be_falsey } + it { is_expected.to be_falsey } end end end @@ -138,13 +138,13 @@ describe Ci::Build do context 'and build.status is success' do before { build.status = 'success' } - it { should be_falsey } + it { is_expected.to be_falsey } end context 'and build.status is failed' do before { build.status = 'failed' } - it { should be_falsey } + it { is_expected.to be_falsey } end end @@ -154,13 +154,13 @@ describe Ci::Build do context 'and build.status is success' do before { build.status = 'success' } - it { should be_falsey } + it { is_expected.to be_falsey } end context 'and build.status is failed' do before { build.status = 'failed' } - it { should be_truthy } + it { is_expected.to be_truthy } end end end @@ -168,27 +168,27 @@ describe Ci::Build do describe :trace do subject { build.trace_html } - it { should be_empty } + it { is_expected.to be_empty } context 'if build.trace contains text' do let(:text) { 'example output' } before { build.trace = text } - it { should include(text) } - it { should have_at_least(text.length).items } + it { is_expected.to include(text) } + it { is_expected.to have_at_least(text.length).items } end end describe :timeout do subject { build.timeout } - it { should eq(commit.project.timeout) } + it { is_expected.to eq(commit.project.timeout) } end describe :duration do subject { build.duration } - it { should eq(120.0) } + it { is_expected.to eq(120.0) } context 'if the building process has not started yet' do before do @@ -196,7 +196,7 @@ describe Ci::Build do build.finished_at = nil end - it { should be_nil } + it { is_expected.to be_nil } end context 'if the building process has started' do @@ -205,8 +205,8 @@ describe Ci::Build do build.finished_at = nil end - it { should be_a(Float) } - it { should > 0.0 } + it { is_expected.to be_a(Float) } + it { is_expected.to be > 0.0 } end end @@ -221,86 +221,86 @@ describe Ci::Build do } subject { build.options } - it { should eq(options) } + it { is_expected.to eq(options) } end describe :ref do subject { build.ref } - it { should eq(commit.ref) } + it { is_expected.to eq(commit.ref) } end describe :sha do subject { build.sha } - it { should eq(commit.sha) } + it { is_expected.to eq(commit.sha) } end describe :short_sha do subject { build.short_sha } - it { should eq(commit.short_sha) } + it { is_expected.to eq(commit.short_sha) } end describe :before_sha do subject { build.before_sha } - it { should eq(commit.before_sha) } + it { is_expected.to eq(commit.before_sha) } end describe :allow_git_fetch do subject { build.allow_git_fetch } - it { should eq(project.allow_git_fetch) } + it { is_expected.to eq(project.allow_git_fetch) } end describe :project do subject { build.project } - it { should eq(commit.project) } + it { is_expected.to eq(commit.project) } end describe :project_id do subject { build.project_id } - it { should eq(commit.project_id) } + it { is_expected.to eq(commit.project_id) } end describe :project_name do subject { build.project_name } - it { should eq(project.name) } + it { is_expected.to eq(project.name) } end describe :repo_url do subject { build.repo_url } - it { should eq(project.repo_url_with_auth) } + it { is_expected.to eq(project.repo_url_with_auth) } end describe :extract_coverage do context 'valid content & regex' do subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } - it { should eq(98.29) } + it { is_expected.to eq(98.29) } end context 'valid content & bad regex' do subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } - it { should be_nil } + it { is_expected.to be_nil } end context 'no coverage content & regex' do subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } - it { should be_nil } + it { is_expected.to be_nil } end context 'multiple results in content & regex' do subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } - it { should eq(98.29) } + it { is_expected.to eq(98.29) } end end @@ -314,7 +314,7 @@ describe Ci::Build do ] } - it { should eq(variables) } + it { is_expected.to eq(variables) } context 'and secure variables' do let(:secure_variables) { @@ -327,7 +327,7 @@ describe Ci::Build do build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') end - it { should eq(variables + secure_variables) } + it { is_expected.to eq(variables + secure_variables) } context 'and trigger variables' do let(:trigger) { FactoryGirl.create :ci_trigger, project: project } @@ -342,7 +342,7 @@ describe Ci::Build do build.trigger_request = trigger_request end - it { should eq(variables + secure_variables + trigger_variables) } + it { is_expected.to eq(variables + secure_variables + trigger_variables) } end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 6d5b0597e13..586c9dc23a7 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -21,18 +21,18 @@ describe Ci::Commit do let(:project) { FactoryGirl.create :ci_project } let(:commit) { FactoryGirl.create :ci_commit, project: project } let(:commit_with_project) { FactoryGirl.create :ci_commit, project: project } - let(:config_processor) { GitlabCiYamlProcessor.new(gitlab_ci_yaml) } + let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) } - it { should belong_to(:project) } - it { should have_many(:builds) } - it { should validate_presence_of :before_sha } - it { should validate_presence_of :sha } - it { should validate_presence_of :ref } - it { should validate_presence_of :push_data } + it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:builds) } + it { is_expected.to validate_presence_of :before_sha } + it { is_expected.to validate_presence_of :sha } + it { is_expected.to validate_presence_of :ref } + it { is_expected.to validate_presence_of :push_data } - it { should respond_to :git_author_name } - it { should respond_to :git_author_email } - it { should respond_to :short_sha } + it { is_expected.to respond_to :git_author_name } + it { is_expected.to respond_to :git_author_email } + it { is_expected.to respond_to :short_sha } describe :last_build do subject { commit.last_build } @@ -41,8 +41,8 @@ describe Ci::Commit do @second = FactoryGirl.create :ci_build, commit: commit end - it { should be_a(Ci::Build) } - it('returns with the most recently created build') { should eq(@second) } + it { is_expected.to be_a(Ci::Build) } + it('returns with the most recently created build') { is_expected.to eq(@second) } end describe :retry do @@ -67,8 +67,8 @@ describe Ci::Commit do email_recipients: '' commit = FactoryGirl.create :ci_commit, project: project expected = 'commit_pusher_email' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == [expected] + allow(commit).to receive(:push_data) { { user_email: expected } } + expect(commit.project_recipients).to eq([expected]) end it 'should return commit_pusher_email and additional recipients' do @@ -77,8 +77,8 @@ describe Ci::Commit do email_recipients: 'rec1 rec2' commit = FactoryGirl.create :ci_commit, project: project expected = 'commit_pusher_email' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == ['rec1', 'rec2', expected] + allow(commit).to receive(:push_data) { { user_email: expected } } + expect(commit.project_recipients).to eq(['rec1', 'rec2', expected]) end it 'should return recipients' do @@ -86,7 +86,7 @@ describe Ci::Commit do email_add_pusher: false, email_recipients: 'rec1 rec2' commit = FactoryGirl.create :ci_commit, project: project - commit.project_recipients.should == ['rec1', 'rec2'] + expect(commit.project_recipients).to eq(['rec1', 'rec2']) end it 'should return unique recipients only' do @@ -95,8 +95,8 @@ describe Ci::Commit do email_recipients: 'rec1 rec1 rec2' commit = FactoryGirl.create :ci_commit, project: project expected = 'rec2' - commit.stub(:push_data) { { user_email: expected } } - commit.project_recipients.should == ['rec1', 'rec2'] + allow(commit).to receive(:push_data) { { user_email: expected } } + expect(commit.project_recipients).to eq(['rec1', 'rec2']) end end end @@ -108,7 +108,7 @@ describe Ci::Commit do commit.valid_commit_sha end - it('commit errors should not be empty') { commit.errors.should_not be_empty } + it('commit errors should not be empty') { expect(commit.errors).not_to be_empty } end end @@ -116,55 +116,59 @@ describe Ci::Commit do subject { commit_with_project.compare? } context 'if commit.before_sha are not nil' do - it { should be_true } + it { is_expected.to be_truthy } end end describe :short_sha do subject { commit.short_before_sha } - it { should have(8).items } - it { commit.before_sha.should start_with(subject) } + it 'has 8 items' do + expect(subject.size).to eq(8) + end + it { expect(commit.before_sha).to start_with(subject) } end describe :short_sha do subject { commit.short_sha } - it { should have(8).items } - it { commit.sha.should start_with(subject) } + it 'has 8 items' do + expect(subject.size).to eq(8) + end + it { expect(commit.sha).to start_with(subject) } end describe :create_next_builds do before do - commit.stub(:config_processor).and_return(config_processor) + allow(commit).to receive(:config_processor).and_return(config_processor) end it "creates builds for next type" do - commit.create_builds.should be_true + expect(commit.create_builds).to be_truthy commit.builds.reload - commit.builds.size.should == 2 + expect(commit.builds.size).to eq(2) - commit.create_next_builds(nil).should be_true + expect(commit.create_next_builds(nil)).to be_truthy commit.builds.reload - commit.builds.size.should == 4 + expect(commit.builds.size).to eq(4) - commit.create_next_builds(nil).should be_true + expect(commit.create_next_builds(nil)).to be_truthy commit.builds.reload - commit.builds.size.should == 5 + expect(commit.builds.size).to eq(5) - commit.create_next_builds(nil).should be_false + expect(commit.create_next_builds(nil)).to be_falsey end end describe :create_builds do before do - commit.stub(:config_processor).and_return(config_processor) + allow(commit).to receive(:config_processor).and_return(config_processor) end it 'creates builds' do - commit.create_builds.should be_true + expect(commit.create_builds).to be_truthy commit.builds.reload - commit.builds.size.should == 2 + expect(commit.builds.size).to eq(2) end context 'for build triggers' do @@ -172,29 +176,29 @@ describe Ci::Commit do let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger } it 'creates builds' do - commit.create_builds(trigger_request).should be_true + expect(commit.create_builds(trigger_request)).to be_truthy commit.builds.reload - commit.builds.size.should == 2 + expect(commit.builds.size).to eq(2) end it 'rebuilds commit' do - commit.create_builds.should be_true + expect(commit.create_builds).to be_truthy commit.builds.reload - commit.builds.size.should == 2 + expect(commit.builds.size).to eq(2) - commit.create_builds(trigger_request).should be_true + expect(commit.create_builds(trigger_request)).to be_truthy commit.builds.reload - commit.builds.size.should == 4 + expect(commit.builds.size).to eq(4) end it 'creates next builds' do - commit.create_builds(trigger_request).should be_true + expect(commit.create_builds(trigger_request)).to be_truthy commit.builds.reload - commit.builds.size.should == 2 + expect(commit.builds.size).to eq(2) - commit.create_next_builds(trigger_request).should be_true + expect(commit.create_next_builds(trigger_request)).to be_truthy commit.builds.reload - commit.builds.size.should == 4 + expect(commit.builds.size).to eq(4) end context 'for [ci skip]' do @@ -204,11 +208,11 @@ describe Ci::Commit do end it 'rebuilds commit' do - commit.status.should == 'skipped' - commit.create_builds(trigger_request).should be_true + expect(commit.status).to eq('skipped') + expect(commit.create_builds(trigger_request)).to be_truthy commit.builds.reload - commit.builds.size.should == 2 - commit.status.should == 'pending' + expect(commit.builds.size).to eq(2) + expect(commit.status).to eq('pending') end end end @@ -222,13 +226,13 @@ describe Ci::Commit do build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60 build1 = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120 - commit.finished_at.to_i.should == build.finished_at.to_i + expect(commit.finished_at.to_i).to eq(build.finished_at.to_i) end it "returns nil if there is no finished build" do build = FactoryGirl.create :ci_not_started_build, commit: commit - commit.finished_at.should be_nil + expect(commit.finished_at).to be_nil end end @@ -239,26 +243,26 @@ describe Ci::Commit do it "calculates average when there are two builds with coverage" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit - commit.coverage.should == "35.00" + expect(commit.coverage).to eq("35.00") end it "calculates average when there are two builds with coverage and one with nil" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit FactoryGirl.create :ci_build, commit: commit - commit.coverage.should == "35.00" + expect(commit.coverage).to eq("35.00") end it "calculates average when there are two builds with coverage and one is retried" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, commit: commit FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit - commit.coverage.should == "35.00" + expect(commit.coverage).to eq("35.00") end it "calculates average when there is one build without coverage" do FactoryGirl.create :ci_build, commit: commit - commit.coverage.should be_nil + expect(commit.coverage).to be_nil end end end diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index 4830d98bdf8..e3f326d783b 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -16,7 +16,7 @@ require 'spec_helper' describe Ci::MailService do describe "Associations" do - it { should belong_to :project } + it { is_expected.to belong_to :project } end describe "Validations" do @@ -36,7 +36,7 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) end @@ -47,8 +47,8 @@ describe Ci::MailService do end def should_email(email) - Notify.should_receive(:build_fail_email).with(build.id, email) - Notify.should_not_receive(:build_success_email).with(build.id, email) + expect(Notify).to receive(:build_fail_email).with(build.id, email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) end end @@ -58,7 +58,7 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) end @@ -69,8 +69,8 @@ describe Ci::MailService do end def should_email(email) - Notify.should_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) + expect(Notify).to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -85,7 +85,7 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) end @@ -97,8 +97,8 @@ describe Ci::MailService do end def should_email(email) - Notify.should_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) + expect(Notify).to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -113,7 +113,7 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) end @@ -125,8 +125,8 @@ describe Ci::MailService do end def should_email(email) - Notify.should_not_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -141,14 +141,14 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) build end it do - mail.can_test?.should == true + expect(mail.can_test?).to eq(true) end end @@ -163,7 +163,7 @@ describe Ci::MailService do let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } before do - mail.stub( + allow(mail).to receive_messages( project: project ) end @@ -176,8 +176,8 @@ describe Ci::MailService do end def should_email(email) - Notify.should_not_receive(:build_success_email).with(build.id, email) - Notify.should_not_receive(:build_fail_email).with(build.id, email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end end diff --git a/spec/models/ci/network_spec.rb b/spec/models/ci/network_spec.rb index b80adba5b08..551eb08ab33 100644 --- a/spec/models/ci/network_spec.rb +++ b/spec/models/ci/network_spec.rb @@ -9,21 +9,21 @@ describe Network do context 'on success' do before do response = double - response.stub(:code) { 200 } - network.class.stub(:put) { response } + allow(response).to receive(:code) { 200 } + allow(network.class).to receive(:put) { response } end - it { should be_true } + it { is_expected.to be_truthy } end context 'on failure' do before do response = double - response.stub(:code) { 404 } - network.class.stub(:put) { response } + allow(response).to receive(:code) { 404 } + allow(network.class).to receive(:put) { response } end - it { should be_nil } + it { is_expected.to be_nil } end end @@ -34,21 +34,21 @@ describe Network do context 'on success' do let(:parsed_response) { 'parsed' } before do - response.stub(:code) { 200 } - response.stub(:parsed_response) { parsed_response } - network.class.stub(:delete) { response } + allow(response).to receive(:code) { 200 } + allow(response).to receive(:parsed_response) { parsed_response } + allow(network.class).to receive(:delete) { response } end - it { should equal(parsed_response) } + it { is_expected.to equal(parsed_response) } end context 'on failure' do before do - response.stub(:code) { 404 } - network.class.stub(:delete) { response } + allow(response).to receive(:code) { 404 } + allow(network.class).to receive(:delete) { response } end - it { should be_nil } + it { is_expected.to be_nil } end end end diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb index 3571cb94793..7318898b3b4 100644 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -18,7 +18,7 @@ describe Ci::HipChatMessage do build.update(status: "success") expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_false + expect( subject.notify? ).to be_falsey expect( subject.to_s ).to match(/Build '[^']+' #\d+/) expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) end @@ -29,7 +29,7 @@ describe Ci::HipChatMessage do build.update(status: "failed") expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_true + expect( subject.notify? ).to be_truthy expect( subject.to_s ).to match(/Build '[^']+' #\d+/) expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) end @@ -50,7 +50,7 @@ describe Ci::HipChatMessage do commit.reload expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_false + expect( subject.notify? ).to be_falsey expect( subject.to_s ).to match(/Commit #\d+/) expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) end @@ -65,7 +65,7 @@ describe Ci::HipChatMessage do second_build.update(status: "failed") expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_true + expect( subject.notify? ).to be_truthy expect( subject.to_s ).to match(/Commit #\d+/) expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) end diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index 71dba8fc358..5d1c6c0900b 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -24,8 +24,8 @@ describe Ci::HipChatService do subject.active = true end - it { should validate_presence_of :hipchat_room } - it { should validate_presence_of :hipchat_token } + it { is_expected.to validate_presence_of :hipchat_room } + it { is_expected.to validate_presence_of :hipchat_token } end end @@ -39,7 +39,7 @@ describe Ci::HipChatService do let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } before do - service.stub( + allow(service).to receive_messages( project: project, project_id: project.id, notify_only_broken_builds: false, diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index 4a7284fe460..8d3bf86ae7a 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -19,11 +19,11 @@ describe Ci::SlackMessage do it 'returns a message with succeeded build' do build.update(status: "success") - subject.color.should == color - subject.fallback.should include('Build') - subject.fallback.should include("\##{build.id}") - subject.fallback.should include('succeeded') - subject.attachments.first[:fields].should be_empty + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Build') + expect(subject.fallback).to include("\##{build.id}") + expect(subject.fallback).to include('succeeded') + expect(subject.attachments.first[:fields]).to be_empty end end @@ -33,11 +33,11 @@ describe Ci::SlackMessage do it 'returns a message with failed build' do build.update(status: "failed") - subject.color.should == color - subject.fallback.should include('Build') - subject.fallback.should include("\##{build.id}") - subject.fallback.should include('failed') - subject.attachments.first[:fields].should be_empty + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Build') + expect(subject.fallback).to include("\##{build.id}") + expect(subject.fallback).to include('failed') + expect(subject.attachments.first[:fields]).to be_empty end end end @@ -53,11 +53,11 @@ describe Ci::SlackMessage do commit.builds.update_all(status: "success") commit.reload - subject.color.should == color - subject.fallback.should include('Commit') - subject.fallback.should include("\##{commit.id}") - subject.fallback.should include('succeeded') - subject.attachments.first[:fields].should be_empty + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Commit') + expect(subject.fallback).to include("\##{commit.id}") + expect(subject.fallback).to include('succeeded') + expect(subject.attachments.first[:fields]).to be_empty end end @@ -71,13 +71,13 @@ describe Ci::SlackMessage do first_build.update(status: "success") second_build.update(status: "failed") - subject.color.should == color - subject.fallback.should include('Commit') - subject.fallback.should include("\##{commit.id}") - subject.fallback.should include('failed') - subject.attachments.first[:fields].size.should == 1 - subject.attachments.first[:fields].first[:title].should == second_build.name - subject.attachments.first[:fields].first[:value].should include("\##{second_build.id}") + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Commit') + expect(subject.fallback).to include("\##{commit.id}") + expect(subject.fallback).to include('failed') + expect(subject.attachments.first[:fields].size).to eq(1) + expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name) + expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}") end end end diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index 952349a9def..3f064bffc89 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -16,7 +16,7 @@ require 'spec_helper' describe Ci::SlackService do describe "Associations" do - it { should belong_to :project } + it { is_expected.to belong_to :project } end describe "Validations" do @@ -25,7 +25,7 @@ describe Ci::SlackService do subject.active = true end - it { should validate_presence_of :webhook } + it { is_expected.to validate_presence_of :webhook } end end @@ -38,7 +38,7 @@ describe Ci::SlackService do let(:notify_only_broken_builds) { false } before do - slack.stub( + allow(slack).to receive_messages( project: project, project_id: project.id, webhook: webhook_url, @@ -52,7 +52,7 @@ describe Ci::SlackService do slack.execute(build) SlackNotifierWorker.drain - WebMock.should have_requested(:post, webhook_url).once + expect(WebMock).to have_requested(:post, webhook_url).once end end end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 3f61545da14..5b2a8d09974 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -30,21 +30,21 @@ require 'spec_helper' describe Project do subject { FactoryGirl.build :ci_project } - it { should have_many(:commits) } + it { is_expected.to have_many(:commits) } - it { should validate_presence_of :name } - it { should validate_presence_of :timeout } - it { should validate_presence_of :default_ref } + it { is_expected.to validate_presence_of :name } + it { is_expected.to validate_presence_of :timeout } + it { is_expected.to validate_presence_of :default_ref } describe 'before_validation' do it 'should set an random token if none provided' do project = FactoryGirl.create :ci_project_without_token - project.token.should_not == "" + expect(project.token).not_to eq("") end it 'should not set an random toke if one provided' do project = FactoryGirl.create :ci_project - project.token.should == "iPWx6WM4lhHNedGfBpPJNP" + expect(project.token).to eq("iPWx6WM4lhHNedGfBpPJNP") end end @@ -57,7 +57,7 @@ describe Project do FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: newest_project FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: oldest_project - Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits] + expect(Project.ordered_by_last_commit_date).to eq([newest_project, oldest_project, project_without_commits]) end end @@ -70,56 +70,56 @@ describe Project do FactoryGirl.create(:ci_build, commit: commit) end - it { project.status.should == 'pending' } - it { project.last_commit.should be_kind_of(Commit) } - it { project.human_status.should == 'pending' } + it { expect(project.status).to eq('pending') } + it { expect(project.last_commit).to be_kind_of(Commit) } + it { expect(project.human_status).to eq('pending') } end end describe '#email_notification?' do it do project = FactoryGirl.create :ci_project, email_add_pusher: true - project.email_notification?.should == true + expect(project.email_notification?).to eq(true) end it do project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'test tesft' - project.email_notification?.should == true + expect(project.email_notification?).to eq(true) end it do project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: '' - project.email_notification?.should == false + expect(project.email_notification?).to eq(false) end end describe '#broken_or_success?' do it { project = FactoryGirl.create :ci_project, email_add_pusher: true - project.stub(:broken?).and_return(true) - project.stub(:success?).and_return(true) - project.broken_or_success?.should == true + allow(project).to receive(:broken?).and_return(true) + allow(project).to receive(:success?).and_return(true) + expect(project.broken_or_success?).to eq(true) } it { project = FactoryGirl.create :ci_project, email_add_pusher: true - project.stub(:broken?).and_return(true) - project.stub(:success?).and_return(false) - project.broken_or_success?.should == true + allow(project).to receive(:broken?).and_return(true) + allow(project).to receive(:success?).and_return(false) + expect(project.broken_or_success?).to eq(true) } it { project = FactoryGirl.create :ci_project, email_add_pusher: true - project.stub(:broken?).and_return(false) - project.stub(:success?).and_return(true) - project.broken_or_success?.should == true + allow(project).to receive(:broken?).and_return(false) + allow(project).to receive(:success?).and_return(true) + expect(project.broken_or_success?).to eq(true) } it { project = FactoryGirl.create :ci_project, email_add_pusher: true - project.stub(:broken?).and_return(false) - project.stub(:success?).and_return(false) - project.broken_or_success?.should == false + allow(project).to receive(:broken?).and_return(false) + allow(project).to receive(:success?).and_return(false) + expect(project.broken_or_success?).to eq(false) } end @@ -128,14 +128,14 @@ describe Project do let(:parsed_project) { Project.parse(project_dump) } - it { parsed_project.should be_valid } - it { parsed_project.should be_kind_of(Project) } - it { parsed_project.name.should eq("GitLab / api.gitlab.org") } - it { parsed_project.gitlab_id.should eq(189) } - it { parsed_project.gitlab_url.should eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } + it { expect(parsed_project).to be_valid } + it { expect(parsed_project).to be_kind_of(Project) } + it { expect(parsed_project.name).to eq("GitLab / api.gitlab.org") } + it { expect(parsed_project.gitlab_id).to eq(189) } + it { expect(parsed_project.gitlab_url).to eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } it "parses plain hash" do - Project.parse(project_dump).name.should eq("GitLab / api.gitlab.org") + expect(Project.parse(project_dump).name).to eq("GitLab / api.gitlab.org") end end @@ -143,43 +143,43 @@ describe Project do let(:project) { FactoryGirl.create :ci_project } subject { project.repo_url_with_auth } - it { should be_a(String) } - it { should end_with(".git") } - it { should start_with(project.gitlab_url[0..6]) } - it { should include(project.token) } - it { should include('gitlab-ci-token') } - it { should include(project.gitlab_url[7..-1]) } + it { is_expected.to be_a(String) } + it { is_expected.to end_with(".git") } + it { is_expected.to start_with(project.gitlab_url[0..6]) } + it { is_expected.to include(project.token) } + it { is_expected.to include('gitlab-ci-token') } + it { is_expected.to include(project.gitlab_url[7..-1]) } end describe :search do let!(:project) { FactoryGirl.create(:ci_project, name: "foo") } - it { Project.search('fo').should include(project) } - it { Project.search('bar').should be_empty } + it { expect(Project.search('fo')).to include(project) } + it { expect(Project.search('bar')).to be_empty } end describe :any_runners do it "there are no runners available" do project = FactoryGirl.create(:ci_project) - project.any_runners?.should be_false + expect(project.any_runners?).to be_falsey end it "there is a specific runner" do project = FactoryGirl.create(:ci_project) project.runners << FactoryGirl.create(:ci_specific_runner) - project.any_runners?.should be_true + expect(project.any_runners?).to be_truthy end it "there is a shared runner" do project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) FactoryGirl.create(:ci_shared_runner) - project.any_runners?.should be_true + expect(project.any_runners?).to be_truthy end it "there is a shared runner, but they are prohibited to use" do project = FactoryGirl.create(:ci_project) FactoryGirl.create(:ci_shared_runner) - project.any_runners?.should be_false + expect(project.any_runners?).to be_falsey end end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 62a719e499b..c6130b69964 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -43,9 +43,9 @@ describe Ci::Runner do before { shared_runner.assign_to(project) } - it { shared_runner.should be_specific } - it { shared_runner.projects.should == [project] } - it { shared_runner.only_for?(project).should be_true } + it { expect(shared_runner).to be_specific } + it { expect(shared_runner.projects).to eq([project]) } + it { expect(shared_runner.only_for?(project)).to be_truthy } end describe "belongs_to_one_project?" do @@ -56,7 +56,7 @@ describe Ci::Runner do project.runners << runner project1.runners << runner - runner.belongs_to_one_project?.should be_false + expect(runner.belongs_to_one_project?).to be_falsey end it "returns true" do @@ -64,7 +64,7 @@ describe Ci::Runner do project = FactoryGirl.create(:ci_project) project.runners << runner - runner.belongs_to_one_project?.should be_true + expect(runner.belongs_to_one_project?).to be_truthy end end end diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 97e2798dbd5..8c4df391555 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -17,7 +17,7 @@ require 'spec_helper' describe Ci::Service do describe "Associations" do - it { should belong_to :project } + it { is_expected.to belong_to :project } end describe "Mass assignment" do @@ -34,7 +34,7 @@ describe Ci::Service do let (:build) { FactoryGirl.create :ci_build, commit: commit } before do - @service.stub( + allow(@service).to receive_messages( project: project ) build @@ -42,7 +42,7 @@ describe Ci::Service do end describe :can_test do - it { @testable.should == true } + it { expect(@testable).to eq(true) } end end end diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 6833baccc8f..19c14ef2da2 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -6,12 +6,12 @@ describe Ci::Trigger do describe 'before_validation' do it 'should set an random token if none provided' do trigger = FactoryGirl.create :ci_trigger_without_token, project: project - trigger.token.should_not be_nil + expect(trigger.token).not_to be_nil end it 'should not set an random token if one provided' do trigger = FactoryGirl.create :ci_trigger, project: project - trigger.token.should == 'token' + expect(trigger.token).to eq('token') end end end diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb index 93338ad5732..df42d4ddb8e 100644 --- a/spec/models/ci/user_spec.rb +++ b/spec/models/ci/user_spec.rb @@ -10,19 +10,19 @@ describe Ci::User do FactoryGirl.create :ci_project, gitlab_id: 2 gitlab_project = OpenStruct.new({id: 1}) gitlab_project1 = OpenStruct.new({id: 2}) - User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + allow_any_instance_of(User).to receive(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) end it "returns projects" do - User.any_instance.stub(:can_manage_project?).and_return(true) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(true) - user.authorized_projects.count.should == 2 + expect(user.authorized_projects.count).to eq(2) end it "empty list if user miss manage permission" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - user.authorized_projects.count.should == 0 + expect(user.authorized_projects.count).to eq(0) end end @@ -32,8 +32,8 @@ describe Ci::User do project1 = FactoryGirl.create :ci_project, gitlab_id: 2 gitlab_project = OpenStruct.new({id: 1}) gitlab_project1 = OpenStruct.new({id: 2}) - User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) - User.any_instance.stub(:can_manage_project?).and_return(true) + allow_any_instance_of(User).to receive(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(true) user = User.new({}) runner = FactoryGirl.create :ci_specific_runner @@ -43,8 +43,8 @@ describe Ci::User do project.runners << runner project1.runners << runner1 - user.authorized_runners.should include(runner, runner1) - user.authorized_runners.should_not include(runner2) + expect(user.authorized_runners).to include(runner, runner1) + expect(user.authorized_runners).not_to include(runner2) end end end diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 447512bf6df..97a3d0081f4 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -14,7 +14,7 @@ require 'spec_helper' describe Ci::Variable do - subject { Variable.new } + subject { Ci::Variable.new } let(:secret_value) { 'secret' } @@ -24,15 +24,15 @@ describe Ci::Variable do describe :value do it 'stores the encrypted value' do - subject.encrypted_value.should_not be_nil + expect(subject.encrypted_value).not_to be_nil end it 'stores an iv for value' do - subject.encrypted_value_iv.should_not be_nil + expect(subject.encrypted_value_iv).not_to be_nil end it 'stores a salt for value' do - subject.encrypted_value_salt.should_not be_nil + expect(subject.encrypted_value_salt).not_to be_nil end it 'fails to decrypt if iv is incorrect' do diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index d5c1d49eec6..bb58f645caf 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -13,22 +13,22 @@ require 'spec_helper' describe Ci::WebHook do describe "Associations" do - it { should belong_to :project } + it { is_expected.to belong_to :project } end describe "Validations" do - it { should validate_presence_of(:url) } + it { is_expected.to validate_presence_of(:url) } context "url format" do - it { should allow_value("http://example.com").for(:url) } - it { should allow_value("https://excample.com").for(:url) } - it { should allow_value("http://test.com/api").for(:url) } - it { should allow_value("http://test.com/api?key=abc").for(:url) } - it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } + it { is_expected.to allow_value("http://example.com").for(:url) } + it { is_expected.to allow_value("https://excample.com").for(:url) } + it { is_expected.to allow_value("http://test.com/api").for(:url) } + it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) } + it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) } - it { should_not allow_value("example.com").for(:url) } - it { should_not allow_value("ftp://example.com").for(:url) } - it { should_not allow_value("herp-and-derp").for(:url) } + it { is_expected.not_to allow_value("example.com").for(:url) } + it { is_expected.not_to allow_value("ftp://example.com").for(:url) } + it { is_expected.not_to allow_value("herp-and-derp").for(:url) } end end @@ -43,22 +43,22 @@ describe Ci::WebHook do it "POSTs to the web hook URL" do @web_hook.execute(@data) - WebMock.should have_requested(:post, @web_hook.url).once + expect(WebMock).to have_requested(:post, @web_hook.url).once end it "POSTs the data as JSON" do json = @data.to_json @web_hook.execute(@data) - WebMock.should have_requested(:post, @web_hook.url).with(body: json).once + expect(WebMock).to have_requested(:post, @web_hook.url).with(body: json).once end it "catches exceptions" do - WebHook.should_receive(:post).and_raise("Some HTTP Post error") + expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") - lambda { + expect { @web_hook.execute(@data) - }.should raise_error + }.to raise_error end end end -- cgit v1.2.1 From bf8013f1a4c5d6274d0b03f55098e3b4d1da3f4d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 15:56:33 +0200 Subject: Fix most of project specs --- spec/factories/ci/runners.rb | 4 ++-- spec/models/ci/project_spec.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb index fec56b438fa..db759eca9ac 100644 --- a/spec/factories/ci/runners.rb +++ b/spec/factories/ci/runners.rb @@ -27,11 +27,11 @@ FactoryGirl.define do platform "darwin" - factory :shared_runner do + factory :ci_shared_runner do is_shared true end - factory :specific_runner do + factory :ci_specific_runner do is_shared false end end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 5b2a8d09974..1be276a9ef8 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -27,7 +27,7 @@ require 'spec_helper' -describe Project do +describe Ci::Project do subject { FactoryGirl.build :ci_project } it { is_expected.to have_many(:commits) } @@ -57,7 +57,7 @@ describe Project do FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: newest_project FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: oldest_project - expect(Project.ordered_by_last_commit_date).to eq([newest_project, oldest_project, project_without_commits]) + expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_project, oldest_project, project_without_commits]) end end @@ -71,7 +71,7 @@ describe Project do end it { expect(project.status).to eq('pending') } - it { expect(project.last_commit).to be_kind_of(Commit) } + it { expect(project.last_commit).to be_kind_of(Ci::Commit) } it { expect(project.human_status).to eq('pending') } end end @@ -125,17 +125,17 @@ describe Project do describe 'Project.parse' do let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - let(:parsed_project) { Project.parse(project_dump) } + let(:parsed_project) { Ci::Project.parse(project_dump) } it { expect(parsed_project).to be_valid } - it { expect(parsed_project).to be_kind_of(Project) } + it { expect(parsed_project).to be_kind_of(Ci::Project) } it { expect(parsed_project.name).to eq("GitLab / api.gitlab.org") } it { expect(parsed_project.gitlab_id).to eq(189) } it { expect(parsed_project.gitlab_url).to eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } it "parses plain hash" do - expect(Project.parse(project_dump).name).to eq("GitLab / api.gitlab.org") + expect(Ci::Project.parse(project_dump).name).to eq("GitLab / api.gitlab.org") end end @@ -154,8 +154,8 @@ describe Project do describe :search do let!(:project) { FactoryGirl.create(:ci_project, name: "foo") } - it { expect(Project.search('fo')).to include(project) } - it { expect(Project.search('bar')).to be_empty } + it { expect(Ci::Project.search('fo')).to include(project) } + it { expect(Ci::Project.search('bar')).to be_empty } end describe :any_runners do -- cgit v1.2.1 From 9d93c567b39bc4f3e6737f1db5b1c6c2b1d59654 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 16:04:06 +0200 Subject: Fix part of CI api tests --- spec/requests/ci/api/builds_spec.rb | 62 ++++++++++----------- spec/requests/ci/api/commits_spec.rb | 20 +++---- spec/requests/ci/api/forks_spec.rb | 8 +-- spec/requests/ci/api/projects_spec.rb | 102 +++++++++++++++++----------------- spec/requests/ci/api/runners_spec.rb | 36 ++++++------ spec/requests/ci/api/triggers_spec.rb | 34 ++++++------ spec/requests/ci/builds_spec.rb | 10 ++-- spec/requests/ci/commits_spec.rb | 8 +-- spec/support/stub_gitlab_calls.rb | 4 +- 9 files changed, 142 insertions(+), 142 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7da212da83a..c416ca98e1f 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' describe Ci::API::API do include ApiHelpers - let(:runner) { FactoryGirl.create(:runner, tag_list: ["mysql", "ruby"]) } - let(:project) { FactoryGirl.create(:project) } + let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } + let(:project) { FactoryGirl.create(:ci_project) } describe "Builds API for runners" do - let(:shared_runner) { FactoryGirl.create(:runner, token: "SharedRunner") } - let(:shared_project) { FactoryGirl.create(:project, name: "SharedProject") } + let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } + let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } before do FactoryGirl.create :runner_project, project_id: project.id, runner_id: runner.id @@ -16,92 +16,92 @@ describe Ci::API::API do describe "POST /builds/register" do it "should start a build" do - commit = FactoryGirl.create(:commit, project: project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds build = commit.builds.first post api("/builds/register"), token: runner.token, info: {platform: :darwin} - response.status.should == 201 - json_response['sha'].should == build.sha - runner.reload.platform.should == "darwin" + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(build.sha) + expect(runner.reload.platform).to eq("darwin") end it "should return 404 error if no pending build found" do post api("/builds/register"), token: runner.token - response.status.should == 404 + expect(response.status).to eq(404) end it "should return 404 error if no builds for specific runner" do - commit = FactoryGirl.create(:commit, project: shared_project) - FactoryGirl.create(:build, commit: commit, status: 'pending' ) + commit = FactoryGirl.create(:ci_commit, project: shared_project) + FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) post api("/builds/register"), token: runner.token - response.status.should == 404 + expect(response.status).to eq(404) end it "should return 404 error if no builds for shared runner" do - commit = FactoryGirl.create(:commit, project: project) - FactoryGirl.create(:build, commit: commit, status: 'pending' ) + commit = FactoryGirl.create(:ci_commit, project: project) + FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) post api("/builds/register"), token: shared_runner.token - response.status.should == 404 + expect(response.status).to eq(404) end it "returns options" do - commit = FactoryGirl.create(:commit, project: project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds post api("/builds/register"), token: runner.token, info: {platform: :darwin} - response.status.should == 201 - json_response["options"].should == {"image" => "ruby:2.1", "services" => ["postgres"]} + expect(response.status).to eq(201) + expect(json_response["options"]).to eq({"image" => "ruby:2.1", "services" => ["postgres"]}) end it "returns variables" do - commit = FactoryGirl.create(:commit, project: project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") post api("/builds/register"), token: runner.token, info: {platform: :darwin} - response.status.should == 201 - json_response["variables"].should == [ + expect(response.status).to eq(201) + expect(json_response["variables"]).to eq([ {"key" => "DB_NAME", "value" => "postgres", "public" => true}, {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, - ] + ]) end it "returns variables for triggers" do - trigger = FactoryGirl.create(:trigger, project: project) - commit = FactoryGirl.create(:commit, project: project) + trigger = FactoryGirl.create(:ci_trigger, project: project) + commit = FactoryGirl.create(:ci_commit, project: project) - trigger_request = FactoryGirl.create(:trigger_request_with_variables, commit: commit, trigger: trigger) + trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds(trigger_request) project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") post api("/builds/register"), token: runner.token, info: {platform: :darwin} - response.status.should == 201 - json_response["variables"].should == [ + expect(response.status).to eq(201) + expect(json_response["variables"]).to eq([ {"key" => "DB_NAME", "value" => "postgres", "public" => true}, {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, {"key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false}, - ] + ]) end end describe "PUT /builds/:id" do - let(:commit) { FactoryGirl.create(:commit, project: project)} - let(:build) { FactoryGirl.create(:build, commit: commit, runner_id: runner.id) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project)} + let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } it "should update a running build" do build.run! put api("/builds/#{build.id}"), token: runner.token - response.status.should == 200 + expect(response.status).to eq(200) end it 'Should not override trace information when no trace is given' do diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index 99021dd681d..2ead68e2290 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Ci::API::API, 'Commits' do include ApiHelpers - let(:project) { FactoryGirl.create(:project) } - let(:commit) { FactoryGirl.create(:commit, project: project) } + let(:project) { FactoryGirl.create(:ci_project) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:options) { { @@ -19,10 +19,10 @@ describe Ci::API::API, 'Commits' do it "should return commits per project" do get api("/commits"), options - response.status.should == 200 - json_response.count.should == 1 - json_response.first["project_id"].should == project.id - json_response.first["sha"].should == commit.sha + expect(response.status).to eq(200) + expect(json_response.count).to eq(1) + expect(json_response.first["project_id"]).to eq(project.id) + expect(json_response.first["sha"]).to eq(commit.sha) end end @@ -51,15 +51,15 @@ describe Ci::API::API, 'Commits' do it "should create a build" do post api("/commits"), options.merge(data: data) - response.status.should == 201 - json_response['sha'].should == "da1560886d4f094c3e6c9ef40349f7d38b5d27d7" + expect(response.status).to eq(201) + expect(json_response['sha']).to eq("da1560886d4f094c3e6c9ef40349f7d38b5d27d7") end it "should return 400 error if no data passed" do post api("/commits"), options - response.status.should == 400 - json_response['message'].should == "400 (Bad request) \"data\" not given" + expect(response.status).to eq(400) + expect(json_response['message']).to eq("400 (Bad request) \"data\" not given") end end end diff --git a/spec/requests/ci/api/forks_spec.rb b/spec/requests/ci/api/forks_spec.rb index 74efc0c30be..27b9d045c8c 100644 --- a/spec/requests/ci/api/forks_spec.rb +++ b/spec/requests/ci/api/forks_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::API::API do include ApiHelpers - let(:project) { FactoryGirl.create(:project) } + let(:project) { FactoryGirl.create(:ci_project) } let(:gitlab_url) { GitlabCi.config.gitlab_server.url } let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } @@ -41,8 +41,8 @@ describe Ci::API::API do it "should create a project with valid data" do post api("/forks"), options - response.status.should == 201 - json_response['name'].should == "Gitlab.org / Underscore" + expect(response.status).to eq(201) + expect(json_response['name']).to eq("Gitlab.org / Underscore") end end @@ -53,7 +53,7 @@ describe Ci::API::API do it "should error with invalid data" do post api("/forks"), options - response.status.should == 400 + expect(response.status).to eq(400) end end end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 65cfc909b48..bca2c48c752 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -20,34 +20,34 @@ describe Ci::API::API do context "requests for scoped projects" do # NOTE: These ids are tied to the actual projects on demo.gitlab.com describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:project, name: "gitlab-ci", gitlab_id: 4) } + let!(:project1) { FactoryGirl.create(:ci_project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:ci_project, name: "gitlab-ci", gitlab_id: 4) } it "should return all projects on the CI instance" do get api("/projects"), options - response.status.should == 200 - json_response.count.should == 2 - json_response.first["id"].should == project1.id - json_response.last["id"].should == project2.id + expect(response.status).to eq(200) + expect(json_response.count).to eq(2) + expect(json_response.first["id"]).to eq(project1.id) + expect(json_response.last["id"]).to eq(project2.id) end end describe "GET /projects/owned" do # NOTE: This user doesn't own any of these projects on demo.gitlab.com - let!(:project1) { FactoryGirl.create(:project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:project, name: "random-project", gitlab_id: 9898) } + let!(:project1) { FactoryGirl.create(:ci_project, name: "gitlabhq", gitlab_id: 3) } + let!(:project2) { FactoryGirl.create(:ci_project, name: "random-project", gitlab_id: 9898) } it "should return all projects on the CI instance" do get api("/projects/owned"), options - response.status.should == 200 - json_response.count.should == 0 + expect(response.status).to eq(200) + expect(json_response.count).to eq(0) end end end describe "POST /projects/:project_id/webhooks" do - let!(:project) { FactoryGirl.create(:project) } + let!(:project) { FactoryGirl.create(:ci_project) } context "Valid Webhook URL" do let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } @@ -58,19 +58,19 @@ describe Ci::API::API do it "should create webhook for specified project" do post api("/projects/#{project.id}/webhooks"), options - response.status.should == 201 - json_response["url"].should == webhook[:web_hook] + expect(response.status).to eq(201) + expect(json_response["url"]).to eq(webhook[:web_hook]) end it "fails to create webhook for non existsing project" do post api("/projects/non-existant-id/webhooks"), options - response.status.should == 404 + expect(response.status).to eq(404) end it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) post api("/projects/#{project.id}/webhooks"), options - response.status.should == 401 + expect(response.status).to eq(401) end end @@ -83,39 +83,39 @@ describe Ci::API::API do it "fails to create webhook for not valid url" do post api("/projects/#{project.id}/webhooks"), options - response.status.should == 400 + expect(response.status).to eq(400) end end context "Missed web_hook parameter" do it "fails to create webhook for not provided url" do post api("/projects/#{project.id}/webhooks"), options - response.status.should == 400 + expect(response.status).to eq(400) end end end describe "GET /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } + let!(:project) { FactoryGirl.create(:ci_project) } context "with an existing project" do it "should retrieve the project info" do get api("/projects/#{project.id}"), options - response.status.should == 200 - json_response['id'].should == project.id + expect(response.status).to eq(200) + expect(json_response['id']).to eq(project.id) end end context "with a non-existing project" do it "should return 404 error if project not found" do get api("/projects/non_existent_id"), options - response.status.should == 404 + expect(response.status).to eq(404) end end end describe "PUT /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } + let!(:project) { FactoryGirl.create(:ci_project) } let!(:project_info) { {name: "An updated name!" } } before do @@ -124,41 +124,41 @@ describe Ci::API::API do it "should update a specific project's information" do put api("/projects/#{project.id}"), options - response.status.should == 200 - json_response["name"].should == project_info[:name] + expect(response.status).to eq(200) + expect(json_response["name"]).to eq(project_info[:name]) end it "fails to update a non-existing project" do put api("/projects/non-existant-id"), options - response.status.should == 404 + expect(response.status).to eq(404) end it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) put api("/projects/#{project.id}"), options - response.status.should == 401 + expect(response.status).to eq(401) end end describe "DELETE /projects/:id" do - let!(:project) { FactoryGirl.create(:project) } + let!(:project) { FactoryGirl.create(:ci_project) } it "should delete a specific project" do delete api("/projects/#{project.id}"), options - response.status.should == 200 + expect(response.status).to eq(200) expect { project.reload }.to raise_error end it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) delete api("/projects/#{project.id}"), options - response.status.should == 401 + expect(response.status).to eq(401) end it "is getting not found error" do delete api("/projects/not-existing_id"), options - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -181,8 +181,8 @@ describe Ci::API::API do it "should create a project with valid data" do post api("/projects"), options - response.status.should == 201 - json_response['name'].should == project_info[:name] + expect(response.status).to eq(201) + expect(json_response['name']).to eq(project_info[:name]) end end @@ -193,58 +193,58 @@ describe Ci::API::API do it "should error with invalid data" do post api("/projects"), options - response.status.should == 400 + expect(response.status).to eq(400) end end describe "POST /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:project) } - let(:runner) { FactoryGirl.create(:runner) } + let(:project) { FactoryGirl.create(:ci_project) } + let(:runner) { FactoryGirl.create(:ci_runner) } it "should add the project to the runner" do post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 201 + expect(response.status).to eq(201) project.reload - project.runners.first.id.should == runner.id + expect(project.runners.first.id).to eq(runner.id) end it "should fail if it tries to link a non-existing project or runner" do post api("/projects/#{project.id}/runners/non-existing"), options - response.status.should == 404 + expect(response.status).to eq(404) post api("/projects/non-existing/runners/#{runner.id}"), options - response.status.should == 404 + expect(response.status).to eq(404) end it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 401 + expect(response.status).to eq(401) end end describe "DELETE /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:project) } - let(:runner) { FactoryGirl.create(:runner) } + let(:project) { FactoryGirl.create(:ci_project) } + let(:runner) { FactoryGirl.create(:ci_runner) } before do post api("/projects/#{project.id}/runners/#{runner.id}"), options end it "should remove the project from the runner" do - project.runners.should be_present + expect(project.runners).to be_present delete api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 200 + expect(response.status).to eq(200) project.reload - project.runners.should be_empty + expect(project.runners).to be_empty end it "non-manager is not authorized" do - User.any_instance.stub(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) post api("/projects/#{project.id}/runners/#{runner.id}"), options - response.status.should == 401 + expect(response.status).to eq(401) end end end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 3faebd40bae..61ea3be870d 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -19,15 +19,15 @@ describe Ci::API::API do } before do - 5.times { FactoryGirl.create(:runner) } + 5.times { FactoryGirl.create(:ci_runner) } end it "should retrieve a list of all runners" do get api("/runners"), options - response.status.should == 200 - json_response.count.should == 5 - json_response.last.should have_key("id") - json_response.last.should have_key("token") + expect(response.status).to eq(200) + expect(json_response.count).to eq(5) + expect(json_response.last).to have_key("id") + expect(json_response.last).to have_key("token") end end @@ -35,49 +35,49 @@ describe Ci::API::API do describe "should create a runner if token provided" do before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } - it { response.status.should == 201 } + it { expect(response.status).to eq(201) } end describe "should create a runner with description" do before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } - it { response.status.should == 201 } - it { Runner.first.description.should == "server.hostname" } + it { expect(response.status).to eq(201) } + it { expect(Runner.first.description).to eq("server.hostname") } end describe "should create a runner with tags" do before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } - it { response.status.should == 201 } - it { Runner.first.tag_list.sort.should == ["tag1", "tag2"] } + it { expect(response.status).to eq(201) } + it { expect(Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) } end describe "should create a runner if project token provided" do - let(:project) { FactoryGirl.create(:project) } + let(:project) { FactoryGirl.create(:ci_project) } before { post api("/runners/register"), token: project.token } - it { response.status.should == 201 } - it { project.runners.size.should == 1 } + it { expect(response.status).to eq(201) } + it { expect(project.runners.size).to eq(1) } end it "should return 403 error if token is invalid" do post api("/runners/register"), token: 'invalid' - response.status.should == 403 + expect(response.status).to eq(403) end it "should return 400 error if no token" do post api("/runners/register") - response.status.should == 400 + expect(response.status).to eq(400) end end describe "DELETE /runners/delete" do - let!(:runner) { FactoryGirl.create(:runner) } + let!(:runner) { FactoryGirl.create(:ci_runner) } before { delete api("/runners/delete"), token: runner.token } - it { response.status.should == 200 } - it { Runner.count.should == 0 } + it { expect(response.status).to eq(200) } + it { expect(Runner.count).to eq(0) } end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 5da40a69991..56757c8f8c7 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,9 +5,9 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } - let!(:project) { FactoryGirl.create(:project) } - let!(:project2) { FactoryGirl.create(:project) } - let!(:trigger) { FactoryGirl.create(:trigger, project: project, token: trigger_token) } + let!(:project) { FactoryGirl.create(:ci_project) } + let!(:project2) { FactoryGirl.create(:ci_project) } + let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) { { token: trigger_token @@ -17,36 +17,36 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do post api("/projects/#{project.id}/refs/master/trigger") - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return not found if project is not found' do post api('/projects/0/refs/master/trigger'), options - response.status.should == 404 + expect(response.status).to eq(404) end it 'should return unauthorized if token is for different project' do post api("/projects/#{project2.id}/refs/master/trigger"), options - response.status.should == 401 + expect(response.status).to eq(401) end end context 'Have a commit' do before do - @commit = FactoryGirl.create(:commit, project: project) + @commit = FactoryGirl.create(:ci_commit, project: project) end it 'should create builds' do post api("/projects/#{project.id}/refs/master/trigger"), options - response.status.should == 201 + expect(response.status).to eq(201) @commit.builds.reload - @commit.builds.size.should == 2 + expect(@commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do post api("/projects/#{project.id}/refs/other-branch/trigger"), options - response.status.should == 400 - json_response['message'].should == 'No builds created' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('No builds created') end context 'Validates variables' do @@ -56,21 +56,21 @@ describe Ci::API::API do it 'should validate variables to be a hash' do post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') - response.status.should == 400 - json_response['message'].should == 'variables needs to be a hash' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: {key: %w(1 2)}) - response.status.should == 400 - json_response['message'].should == 'variables needs to be a map of key-valued strings' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) - response.status.should == 201 + expect(response.status).to eq(201) @commit.builds.reload - @commit.builds.first.trigger_request.variables.should == variables + expect(@commit.builds.first.trigger_request.variables).to eq(variables) end end end diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb index 73d540e372a..0d7650ef582 100644 --- a/spec/requests/ci/builds_spec.rb +++ b/spec/requests/ci/builds_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe "Builds" do before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit + @project = FactoryGirl.create :ci_project + @commit = FactoryGirl.create :ci_commit, project: @project + @build = FactoryGirl.create :ci_build, commit: @commit end describe "GET /:project/builds/:id/status.json" do @@ -12,7 +12,7 @@ describe "Builds" do get status_project_build_path(@project, @build), format: :json end - it { response.status.should == 200 } - it { response.body.should include(@build.sha) } + it { expect(response.status).to eq(200) } + it { expect(response.body).to include(@build.sha) } end end diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb index e9d8366c41a..fe7bd2de3e7 100644 --- a/spec/requests/ci/commits_spec.rb +++ b/spec/requests/ci/commits_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe "Commits" do before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project + @project = FactoryGirl.create :ci_project + @commit = FactoryGirl.create :ci_commit, project: @project end describe "GET /:project/refs/:ref_name/commits/:id/status.json" do @@ -11,7 +11,7 @@ describe "Commits" do get status_project_ref_commit_path(@project, @commit.ref, @commit.sha), format: :json end - it { response.status.should == 200 } - it { response.body.should include(@commit.sha) } + it { expect(response.status).to eq(200) } + it { expect(response.body).to include(@commit.sha) } end end diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 931ef963c0f..51425e3095c 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -16,7 +16,7 @@ module StubGitlabCalls private def gitlab_url - GitlabCi.config.gitlab_server.url + Gitlab.config.gitlab.url end def stub_session @@ -52,7 +52,7 @@ module StubGitlabCalls def stub_projects f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) - + stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). with(:headers => {'Content-Type'=>'application/json'}). to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) -- cgit v1.2.1 From f5af4efabd410f6140fb0ac16f7787bb2a5b457f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 10 Sep 2015 17:05:43 +0300 Subject: fix runners page --- app/mailers/ci/notify.rb | 2 +- app/views/ci/admin/runners/index.html.haml | 3 ++- app/views/ci/runners/_runner.html.haml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb index 6dcc118ac05..4462da0d7d2 100644 --- a/app/mailers/ci/notify.rb +++ b/app/mailers/ci/notify.rb @@ -10,7 +10,7 @@ module Ci default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port? default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root - default from: GitlabCi.config.gitlab_ci.email_from + default from: Gitlab.config.gitlab.email_from # Just send email with 3 seconds delay def self.delay diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index f1ab3399dcc..d578ff922ad 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -47,5 +47,6 @@ %th Last contact %th - = render @runners + - @runners.each do |runner| + = render "ci/admin/runners/runner", runner: runner = paginate @runners diff --git a/app/views/ci/runners/_runner.html.haml b/app/views/ci/runners/_runner.html.haml index 7ead5736bb1..ef8622e2807 100644 --- a/app/views/ci/runners/_runner.html.haml +++ b/app/views/ci/runners/_runner.html.haml @@ -3,7 +3,7 @@ = runner_status_icon(runner) %span.monospace - if @runners.include?(runner) - = link_to runner.short_sha, [:ci, @project, runner] + = link_to runner.short_sha, ci_project_runner_path(@project, runner) %small =link_to edit_ci_project_runner_path(@project, runner) do %i.fa.fa-edit.btn -- cgit v1.2.1 From 762a23f268e003407f2e8c7c1f436f655a75e6cc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 16:37:04 +0200 Subject: Remove CI css that exists in GitLab --- app/assets/stylesheets/ci/application.scss | 23 +- app/assets/stylesheets/ci/generic/avatar.scss | 29 - app/assets/stylesheets/ci/generic/buttons.scss | 7 - app/assets/stylesheets/ci/generic/callout.scss | 45 -- app/assets/stylesheets/ci/generic/common.scss | 189 ----- app/assets/stylesheets/ci/generic/forms.scss | 28 - app/assets/stylesheets/ci/generic/tables.scss | 20 - app/assets/stylesheets/ci/generic/typography.scss | 63 -- app/assets/stylesheets/ci/generic/xterm.scss | 904 ---------------------- app/assets/stylesheets/ci/main/fonts.scss | 2 - app/assets/stylesheets/ci/main/layout.scss | 18 - app/assets/stylesheets/ci/main/mixins.scss | 31 - app/assets/stylesheets/ci/main/variables.scss | 44 -- app/assets/stylesheets/ci/sections/navbar.scss | 2 +- app/assets/stylesheets/ci/xterm.scss | 904 ++++++++++++++++++++++ app/views/layouts/ci/_nav.html.haml | 2 +- app/views/layouts/ci/admin.html.haml | 13 +- app/views/layouts/ci/application.html.haml | 3 +- app/views/layouts/ci/empty.html.haml | 3 +- app/views/layouts/ci/project.html.haml | 19 +- 20 files changed, 943 insertions(+), 1406 deletions(-) delete mode 100644 app/assets/stylesheets/ci/generic/avatar.scss delete mode 100644 app/assets/stylesheets/ci/generic/buttons.scss delete mode 100644 app/assets/stylesheets/ci/generic/callout.scss delete mode 100644 app/assets/stylesheets/ci/generic/common.scss delete mode 100644 app/assets/stylesheets/ci/generic/forms.scss delete mode 100644 app/assets/stylesheets/ci/generic/tables.scss delete mode 100644 app/assets/stylesheets/ci/generic/typography.scss delete mode 100644 app/assets/stylesheets/ci/generic/xterm.scss delete mode 100644 app/assets/stylesheets/ci/main/fonts.scss delete mode 100644 app/assets/stylesheets/ci/main/layout.scss delete mode 100644 app/assets/stylesheets/ci/main/mixins.scss delete mode 100644 app/assets/stylesheets/ci/main/variables.scss create mode 100644 app/assets/stylesheets/ci/xterm.scss diff --git a/app/assets/stylesheets/ci/application.scss b/app/assets/stylesheets/ci/application.scss index ce080c7cf8a..7a124ae87d9 100644 --- a/app/assets/stylesheets/ci/application.scss +++ b/app/assets/stylesheets/ci/application.scss @@ -11,15 +11,19 @@ *= require_self */ -@import "main/variables.scss"; -@import "main/mixins.scss"; -@import "main/fonts.scss"; -@import "main/layout.scss"; +@import "../base/fonts"; +@import "../base/variables"; +@import "../base/mixins"; +@import "../base/layout"; +@import "../base/gl_variables"; +@import "../base/gl_bootstrap"; /** - * Twitter bootstrap + * Customized Twitter bootstrap */ -@import 'bootstrap'; +@import '../base/gl_variables'; +@import '../base/gl_bootstrap'; + /** * Font icons @@ -30,12 +34,13 @@ /** * Generic css (forms, nav etc): */ -@import "generic/*"; +@import "../generic/**/*"; /** * Page specific styles (issues, projects etc): */ +@import "xterm"; @import "sections/*"; /* @@ -44,3 +49,7 @@ $nprogress-color: #9BC; @import 'nprogress'; @import 'nprogress-bootstrap'; + +body { + padding-top: 0 !important; +} diff --git a/app/assets/stylesheets/ci/generic/avatar.scss b/app/assets/stylesheets/ci/generic/avatar.scss deleted file mode 100644 index fc0914cddea..00000000000 --- a/app/assets/stylesheets/ci/generic/avatar.scss +++ /dev/null @@ -1,29 +0,0 @@ -.avatar { - float: left; - margin-right: 12px; - width: 40px; - height: 40px; - padding: 0; - @include border-radius($avatar_radius); - - &.avatar-inline { - float: none; - margin-left: 4px; - margin-bottom: 2px; - - &.s16 { margin-right: 4px; } - &.s24 { margin-right: 4px; } - } - - &.avatar-tile { - @include border-radius(0px); - } - - &.s16 { width: 16px; height: 16px; margin-right: 6px; } - &.s24 { width: 24px; height: 24px; margin-right: 8px; } - &.s26 { width: 26px; height: 26px; margin-right: 8px; } - &.s32 { width: 32px; height: 32px; margin-right: 10px; } - &.s60 { width: 60px; height: 60px; margin-right: 12px; } - &.s90 { width: 90px; height: 90px; margin-right: 15px; } - &.s160 { width: 160px; height: 160px; margin-right: 20px; } -} diff --git a/app/assets/stylesheets/ci/generic/buttons.scss b/app/assets/stylesheets/ci/generic/buttons.scss deleted file mode 100644 index 5605c097c03..00000000000 --- a/app/assets/stylesheets/ci/generic/buttons.scss +++ /dev/null @@ -1,7 +0,0 @@ -.btn { - @extend .btn-default; - - &.btn-save { - @extend .btn-primary; - } -} diff --git a/app/assets/stylesheets/ci/generic/callout.scss b/app/assets/stylesheets/ci/generic/callout.scss deleted file mode 100644 index f1699d21c9b..00000000000 --- a/app/assets/stylesheets/ci/generic/callout.scss +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Callouts from Bootstrap3 docs - * - * Not quite alerts, but custom and helpful notes for folks reading the docs. - * Requires a base and modifier class. - */ - -/* Common styles for all types */ -.bs-callout { - margin: 20px 0; - padding: 20px; - border-left: 3px solid #eee; - color: #666; - background: #f9f9f9; -} -.bs-callout h4 { - margin-top: 0; - margin-bottom: 5px; -} -.bs-callout p:last-child { - margin-bottom: 0; -} - -/* Variations */ -.bs-callout-danger { - background-color: #fdf7f7; - border-color: #eed3d7; - color: #b94a48; -} -.bs-callout-warning { - background-color: #faf8f0; - border-color: #faebcc; - color: #8a6d3b; -} -.bs-callout-info { - background-color: #f4f8fa; - border-color: #bce8f1; - color: #34789a; -} -.bs-callout-success { - background-color: #dff0d8; - border-color: #5cA64d; - color: #3c763d; -} - diff --git a/app/assets/stylesheets/ci/generic/common.scss b/app/assets/stylesheets/ci/generic/common.scss deleted file mode 100644 index 8b0d59fdb73..00000000000 --- a/app/assets/stylesheets/ci/generic/common.scss +++ /dev/null @@ -1,189 +0,0 @@ -/** COLORS **/ -.cgray { color: gray } -.clgray { color: #BBB } -.cred { color: #D12F19 } -.cgreen { color: #4a2 } -.cblue { color: #29A } -.cblack { color: #111 } -.cdark { color: #444 } -.camber { color: #ffc000 } -.cwhite { color: #fff!important } -.bgred { background: #F2DEDE!important } - -/** COMMON CLASSES **/ -.prepend-top-10 { margin-top:10px } -.prepend-top-20 { margin-top:20px } -.prepend-left-10 { margin-left:10px } -.prepend-left-20 { margin-left:20px } -.append-right-10 { margin-right:10px } -.append-right-20 { margin-right:20px } -.append-bottom-10 { margin-bottom:10px } -.append-bottom-15 { margin-bottom:15px } -.append-bottom-20 { margin-bottom:20px } -.inline { display: inline-block } -.padded { padding:20px } -.ipadded { padding:20px!important } -.lborder { border-left:1px solid #eee } -.underlined_link { text-decoration: underline; } -.hint { font-style: italic; color: #999; } -.light { color: #888 } -.tiny { font-weight: normal } -.vtop { vertical-align: top !important; } - - -.dropdown-menu > li > a { - text-shadow: none; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - background: #29b; -} - -.breadcrumb > li + li:before { - content: "/"; - padding: 0; - color: #666; -} - -.str-truncated { - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: top; - white-space: nowrap; - max-width: 82%; -} - -.page-title { - color: #444; - line-height: 1.5; - margin-top: 0px; - margin-bottom: 15px; -} - -.slead { - margin-bottom: 18px; - font-size: 16px; - font-weight: normal; - line-height: 1.4; -} - -.help-callout { - li { - font-size: 15px; - line-height: 1.6; - } -} - -/** light list with border-bottom between li **/ -ul.bordered-list { - margin: 5px 0px; - padding: 0px; - li { - padding: 5px 0; - border-bottom: 1px solid #EEE; - overflow: hidden; - display: block; - margin: 0px; - &:last-child { border:none } - &.active { - background: #f9f9f9; - a { font-weight: bold; } - } - } - - &.top-list { - li:first-child { - padding-top: 0; - h4, h5 { - margin-top: 0; - } - } - } -} - -.underlined-title { - border-bottom: 1px solid #ccc; - padding: 0 0 3px 3px; -} - -// Nav tabs -.nav.nav-tabs { - li { - > a { - padding: 8px 20px; - margin-right: 7px; - line-height: 20px; - border-color: #EEE; - color: #888; - border-bottom: 1px solid #ddd; - .badge { - background-color: #eee; - color: #888; - text-shadow: 0 1px 1px #fff; - } - i.fa { - line-height: 14px; - } - } - &.active { - > a { - border-color: #CCC; - border-bottom: 1px solid #fff; - color: #333; - font-weight: bold; - } - } - } - - &.nav-small-tabs > li > a { - padding: 6px 9px; - } -} - -.nav-tabs > li > a, -.nav-pills > li > a { - color: #666; -} - -.nav-small > li > a { - padding: 3px 5px; - font-size: 12px; -} - - - -// Breadcrumb -ul.breadcrumb { - background: white; - border: none; - li { - display: inline; - text-shadow: 0 1px 0 white - } - - a { - font-size: 16px; - } -} - -/** - * fix to keep tooltips position in top navigation bar - * - */ -.navbar .nav > li { - position: relative; - white-space: nowrap; -} - -// alerts -.alert-disabled { - background-color: #e6e6e6; - border-color: #ebccd1; - color: #b0b0b0; -} - -.label { - margin-right: 5px; - font-weight: normal; -} diff --git a/app/assets/stylesheets/ci/generic/forms.scss b/app/assets/stylesheets/ci/generic/forms.scss deleted file mode 100644 index c8e4e8d6602..00000000000 --- a/app/assets/stylesheets/ci/generic/forms.scss +++ /dev/null @@ -1,28 +0,0 @@ -input[type='text'].danger { - background: #F2DEDE!important; - border-color: #D66; - text-shadow: 0 1px 1px #fff -} - -fieldset { - margin-bottom: 25px; -} - -.form-actions { - padding: 17px 20px 18px; - margin-top: 18px; - margin-bottom: 18px; - background-color: whitesmoke; - border-top: 1px solid #e5e5e5; - padding-left: 17%; -} - -label { - &.control-label { - @extend .col-sm-2; - } - - &.inline-label { - margin: 0; - } -} diff --git a/app/assets/stylesheets/ci/generic/tables.scss b/app/assets/stylesheets/ci/generic/tables.scss deleted file mode 100644 index 71a7d4abaee..00000000000 --- a/app/assets/stylesheets/ci/generic/tables.scss +++ /dev/null @@ -1,20 +0,0 @@ -table { - &.table { - tr { - td, th { - padding: 8px 10px; - line-height: 20px; - vertical-align: middle; - } - th { - font-weight: normal; - font-size: 15px; - border-bottom: 1px solid #CCC !important; - } - td { - border-color: #F1F1F1 !important; - border-bottom: 1px solid; - } - } - } -} diff --git a/app/assets/stylesheets/ci/generic/typography.scss b/app/assets/stylesheets/ci/generic/typography.scss deleted file mode 100644 index b9ed23b9d3a..00000000000 --- a/app/assets/stylesheets/ci/generic/typography.scss +++ /dev/null @@ -1,63 +0,0 @@ -h6 { - color: #888; - text-transform: uppercase; -} - -pre { - font-family: $monospace_font; - - &.dark { - background: #333; - color: #f5f5f5; - } -} - -/** - * Links - * - */ -a { - outline: none; - color: $link_color; - &:hover { - text-decoration: none; - color: $primary_color; - } - - &:focus { - text-decoration: underline; - } - - &.dark { - color: $style_color; - } - - &.lined { - text-decoration: underline; - &:hover { text-decoration: underline; } - } - - &.gray { - color: gray; - } - - &.supp_diff_link { - text-align: center; - padding: 20px 0; - background: #f1f1f1; - width: 100%; - float: left; - } - - &.neib { - margin-right: 15px; - } -} - -a:focus { - outline: none; -} - -.monospace { - font-family: $monospace_font; -} diff --git a/app/assets/stylesheets/ci/generic/xterm.scss b/app/assets/stylesheets/ci/generic/xterm.scss deleted file mode 100644 index 460a6bb2024..00000000000 --- a/app/assets/stylesheets/ci/generic/xterm.scss +++ /dev/null @@ -1,904 +0,0 @@ -// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg -// see also: https://gist.github.com/jasonm23/2868981 - -$black: #000000; -$red: #cd0000; -$green: #00cd00; -$yellow: #cdcd00; -$blue: #0000ee; // according to wikipedia, this is the xterm standard -//$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) -$magenta: #cd00cd; -$cyan: #00cdcd; -$white: #e5e5e5; -$l-black: #7f7f7f; -$l-red: #ff0000; -$l-green: #00ff00; -$l-yellow: #ffff00; -$l-blue: #5c5cff; -$l-magenta: #ff00ff; -$l-cyan: #00ffff; -$l-white: #ffffff; - -.term-bold { - font-weight: bold; -} -.term-italic { - font-style: italic; -} -.term-conceal { - visibility: hidden; -} -.term-underline { - text-decoration: underline; -} -.term-cross { - text-decoration: line-through; -} - -.term-fg-black { - color: $black; -} -.term-fg-red { - color: $red; -} -.term-fg-green { - color: $green; -} -.term-fg-yellow { - color: $yellow; -} -.term-fg-blue { - color: $blue; -} -.term-fg-magenta { - color: $magenta; -} -.term-fg-cyan { - color: $cyan; -} -.term-fg-white { - color: $white; -} -.term-fg-l-black { - color: $l-black; -} -.term-fg-l-red { - color: $l-red; -} -.term-fg-l-green { - color: $l-green; -} -.term-fg-l-yellow { - color: $l-yellow; -} -.term-fg-l-blue { - color: $l-blue; -} -.term-fg-l-magenta { - color: $l-magenta; -} -.term-fg-l-cyan { - color: $l-cyan; -} -.term-fg-l-white { - color: $l-white; -} - -.term-bg-black { - background-color: $black; -} -.term-bg-red { - background-color: $red; -} -.term-bg-green { - background-color: $green; -} -.term-bg-yellow { - background-color: $yellow; -} -.term-bg-blue { - background-color: $blue; -} -.term-bg-magenta { - background-color: $magenta; -} -.term-bg-cyan { - background-color: $cyan; -} -.term-bg-white { - background-color: $white; -} -.term-bg-l-black { - background-color: $l-black; -} -.term-bg-l-red { - background-color: $l-red; -} -.term-bg-l-green { - background-color: $l-green; -} -.term-bg-l-yellow { - background-color: $l-yellow; -} -.term-bg-l-blue { - background-color: $l-blue; -} -.term-bg-l-magenta { - background-color: $l-magenta; -} -.term-bg-l-cyan { - background-color: $l-cyan; -} -.term-bg-l-white { - background-color: $l-white; -} - - -.xterm-fg-0 { - color: #000000; -} -.xterm-fg-1 { - color: #800000; -} -.xterm-fg-2 { - color: #008000; -} -.xterm-fg-3 { - color: #808000; -} -.xterm-fg-4 { - color: #000080; -} -.xterm-fg-5 { - color: #800080; -} -.xterm-fg-6 { - color: #008080; -} -.xterm-fg-7 { - color: #c0c0c0; -} -.xterm-fg-8 { - color: #808080; -} -.xterm-fg-9 { - color: #ff0000; -} -.xterm-fg-10 { - color: #00ff00; -} -.xterm-fg-11 { - color: #ffff00; -} -.xterm-fg-12 { - color: #0000ff; -} -.xterm-fg-13 { - color: #ff00ff; -} -.xterm-fg-14 { - color: #00ffff; -} -.xterm-fg-15 { - color: #ffffff; -} -.xterm-fg-16 { - color: #000000; -} -.xterm-fg-17 { - color: #00005f; -} -.xterm-fg-18 { - color: #000087; -} -.xterm-fg-19 { - color: #0000af; -} -.xterm-fg-20 { - color: #0000d7; -} -.xterm-fg-21 { - color: #0000ff; -} -.xterm-fg-22 { - color: #005f00; -} -.xterm-fg-23 { - color: #005f5f; -} -.xterm-fg-24 { - color: #005f87; -} -.xterm-fg-25 { - color: #005faf; -} -.xterm-fg-26 { - color: #005fd7; -} -.xterm-fg-27 { - color: #005fff; -} -.xterm-fg-28 { - color: #008700; -} -.xterm-fg-29 { - color: #00875f; -} -.xterm-fg-30 { - color: #008787; -} -.xterm-fg-31 { - color: #0087af; -} -.xterm-fg-32 { - color: #0087d7; -} -.xterm-fg-33 { - color: #0087ff; -} -.xterm-fg-34 { - color: #00af00; -} -.xterm-fg-35 { - color: #00af5f; -} -.xterm-fg-36 { - color: #00af87; -} -.xterm-fg-37 { - color: #00afaf; -} -.xterm-fg-38 { - color: #00afd7; -} -.xterm-fg-39 { - color: #00afff; -} -.xterm-fg-40 { - color: #00d700; -} -.xterm-fg-41 { - color: #00d75f; -} -.xterm-fg-42 { - color: #00d787; -} -.xterm-fg-43 { - color: #00d7af; -} -.xterm-fg-44 { - color: #00d7d7; -} -.xterm-fg-45 { - color: #00d7ff; -} -.xterm-fg-46 { - color: #00ff00; -} -.xterm-fg-47 { - color: #00ff5f; -} -.xterm-fg-48 { - color: #00ff87; -} -.xterm-fg-49 { - color: #00ffaf; -} -.xterm-fg-50 { - color: #00ffd7; -} -.xterm-fg-51 { - color: #00ffff; -} -.xterm-fg-52 { - color: #5f0000; -} -.xterm-fg-53 { - color: #5f005f; -} -.xterm-fg-54 { - color: #5f0087; -} -.xterm-fg-55 { - color: #5f00af; -} -.xterm-fg-56 { - color: #5f00d7; -} -.xterm-fg-57 { - color: #5f00ff; -} -.xterm-fg-58 { - color: #5f5f00; -} -.xterm-fg-59 { - color: #5f5f5f; -} -.xterm-fg-60 { - color: #5f5f87; -} -.xterm-fg-61 { - color: #5f5faf; -} -.xterm-fg-62 { - color: #5f5fd7; -} -.xterm-fg-63 { - color: #5f5fff; -} -.xterm-fg-64 { - color: #5f8700; -} -.xterm-fg-65 { - color: #5f875f; -} -.xterm-fg-66 { - color: #5f8787; -} -.xterm-fg-67 { - color: #5f87af; -} -.xterm-fg-68 { - color: #5f87d7; -} -.xterm-fg-69 { - color: #5f87ff; -} -.xterm-fg-70 { - color: #5faf00; -} -.xterm-fg-71 { - color: #5faf5f; -} -.xterm-fg-72 { - color: #5faf87; -} -.xterm-fg-73 { - color: #5fafaf; -} -.xterm-fg-74 { - color: #5fafd7; -} -.xterm-fg-75 { - color: #5fafff; -} -.xterm-fg-76 { - color: #5fd700; -} -.xterm-fg-77 { - color: #5fd75f; -} -.xterm-fg-78 { - color: #5fd787; -} -.xterm-fg-79 { - color: #5fd7af; -} -.xterm-fg-80 { - color: #5fd7d7; -} -.xterm-fg-81 { - color: #5fd7ff; -} -.xterm-fg-82 { - color: #5fff00; -} -.xterm-fg-83 { - color: #5fff5f; -} -.xterm-fg-84 { - color: #5fff87; -} -.xterm-fg-85 { - color: #5fffaf; -} -.xterm-fg-86 { - color: #5fffd7; -} -.xterm-fg-87 { - color: #5fffff; -} -.xterm-fg-88 { - color: #870000; -} -.xterm-fg-89 { - color: #87005f; -} -.xterm-fg-90 { - color: #870087; -} -.xterm-fg-91 { - color: #8700af; -} -.xterm-fg-92 { - color: #8700d7; -} -.xterm-fg-93 { - color: #8700ff; -} -.xterm-fg-94 { - color: #875f00; -} -.xterm-fg-95 { - color: #875f5f; -} -.xterm-fg-96 { - color: #875f87; -} -.xterm-fg-97 { - color: #875faf; -} -.xterm-fg-98 { - color: #875fd7; -} -.xterm-fg-99 { - color: #875fff; -} -.xterm-fg-100 { - color: #878700; -} -.xterm-fg-101 { - color: #87875f; -} -.xterm-fg-102 { - color: #878787; -} -.xterm-fg-103 { - color: #8787af; -} -.xterm-fg-104 { - color: #8787d7; -} -.xterm-fg-105 { - color: #8787ff; -} -.xterm-fg-106 { - color: #87af00; -} -.xterm-fg-107 { - color: #87af5f; -} -.xterm-fg-108 { - color: #87af87; -} -.xterm-fg-109 { - color: #87afaf; -} -.xterm-fg-110 { - color: #87afd7; -} -.xterm-fg-111 { - color: #87afff; -} -.xterm-fg-112 { - color: #87d700; -} -.xterm-fg-113 { - color: #87d75f; -} -.xterm-fg-114 { - color: #87d787; -} -.xterm-fg-115 { - color: #87d7af; -} -.xterm-fg-116 { - color: #87d7d7; -} -.xterm-fg-117 { - color: #87d7ff; -} -.xterm-fg-118 { - color: #87ff00; -} -.xterm-fg-119 { - color: #87ff5f; -} -.xterm-fg-120 { - color: #87ff87; -} -.xterm-fg-121 { - color: #87ffaf; -} -.xterm-fg-122 { - color: #87ffd7; -} -.xterm-fg-123 { - color: #87ffff; -} -.xterm-fg-124 { - color: #af0000; -} -.xterm-fg-125 { - color: #af005f; -} -.xterm-fg-126 { - color: #af0087; -} -.xterm-fg-127 { - color: #af00af; -} -.xterm-fg-128 { - color: #af00d7; -} -.xterm-fg-129 { - color: #af00ff; -} -.xterm-fg-130 { - color: #af5f00; -} -.xterm-fg-131 { - color: #af5f5f; -} -.xterm-fg-132 { - color: #af5f87; -} -.xterm-fg-133 { - color: #af5faf; -} -.xterm-fg-134 { - color: #af5fd7; -} -.xterm-fg-135 { - color: #af5fff; -} -.xterm-fg-136 { - color: #af8700; -} -.xterm-fg-137 { - color: #af875f; -} -.xterm-fg-138 { - color: #af8787; -} -.xterm-fg-139 { - color: #af87af; -} -.xterm-fg-140 { - color: #af87d7; -} -.xterm-fg-141 { - color: #af87ff; -} -.xterm-fg-142 { - color: #afaf00; -} -.xterm-fg-143 { - color: #afaf5f; -} -.xterm-fg-144 { - color: #afaf87; -} -.xterm-fg-145 { - color: #afafaf; -} -.xterm-fg-146 { - color: #afafd7; -} -.xterm-fg-147 { - color: #afafff; -} -.xterm-fg-148 { - color: #afd700; -} -.xterm-fg-149 { - color: #afd75f; -} -.xterm-fg-150 { - color: #afd787; -} -.xterm-fg-151 { - color: #afd7af; -} -.xterm-fg-152 { - color: #afd7d7; -} -.xterm-fg-153 { - color: #afd7ff; -} -.xterm-fg-154 { - color: #afff00; -} -.xterm-fg-155 { - color: #afff5f; -} -.xterm-fg-156 { - color: #afff87; -} -.xterm-fg-157 { - color: #afffaf; -} -.xterm-fg-158 { - color: #afffd7; -} -.xterm-fg-159 { - color: #afffff; -} -.xterm-fg-160 { - color: #d70000; -} -.xterm-fg-161 { - color: #d7005f; -} -.xterm-fg-162 { - color: #d70087; -} -.xterm-fg-163 { - color: #d700af; -} -.xterm-fg-164 { - color: #d700d7; -} -.xterm-fg-165 { - color: #d700ff; -} -.xterm-fg-166 { - color: #d75f00; -} -.xterm-fg-167 { - color: #d75f5f; -} -.xterm-fg-168 { - color: #d75f87; -} -.xterm-fg-169 { - color: #d75faf; -} -.xterm-fg-170 { - color: #d75fd7; -} -.xterm-fg-171 { - color: #d75fff; -} -.xterm-fg-172 { - color: #d78700; -} -.xterm-fg-173 { - color: #d7875f; -} -.xterm-fg-174 { - color: #d78787; -} -.xterm-fg-175 { - color: #d787af; -} -.xterm-fg-176 { - color: #d787d7; -} -.xterm-fg-177 { - color: #d787ff; -} -.xterm-fg-178 { - color: #d7af00; -} -.xterm-fg-179 { - color: #d7af5f; -} -.xterm-fg-180 { - color: #d7af87; -} -.xterm-fg-181 { - color: #d7afaf; -} -.xterm-fg-182 { - color: #d7afd7; -} -.xterm-fg-183 { - color: #d7afff; -} -.xterm-fg-184 { - color: #d7d700; -} -.xterm-fg-185 { - color: #d7d75f; -} -.xterm-fg-186 { - color: #d7d787; -} -.xterm-fg-187 { - color: #d7d7af; -} -.xterm-fg-188 { - color: #d7d7d7; -} -.xterm-fg-189 { - color: #d7d7ff; -} -.xterm-fg-190 { - color: #d7ff00; -} -.xterm-fg-191 { - color: #d7ff5f; -} -.xterm-fg-192 { - color: #d7ff87; -} -.xterm-fg-193 { - color: #d7ffaf; -} -.xterm-fg-194 { - color: #d7ffd7; -} -.xterm-fg-195 { - color: #d7ffff; -} -.xterm-fg-196 { - color: #ff0000; -} -.xterm-fg-197 { - color: #ff005f; -} -.xterm-fg-198 { - color: #ff0087; -} -.xterm-fg-199 { - color: #ff00af; -} -.xterm-fg-200 { - color: #ff00d7; -} -.xterm-fg-201 { - color: #ff00ff; -} -.xterm-fg-202 { - color: #ff5f00; -} -.xterm-fg-203 { - color: #ff5f5f; -} -.xterm-fg-204 { - color: #ff5f87; -} -.xterm-fg-205 { - color: #ff5faf; -} -.xterm-fg-206 { - color: #ff5fd7; -} -.xterm-fg-207 { - color: #ff5fff; -} -.xterm-fg-208 { - color: #ff8700; -} -.xterm-fg-209 { - color: #ff875f; -} -.xterm-fg-210 { - color: #ff8787; -} -.xterm-fg-211 { - color: #ff87af; -} -.xterm-fg-212 { - color: #ff87d7; -} -.xterm-fg-213 { - color: #ff87ff; -} -.xterm-fg-214 { - color: #ffaf00; -} -.xterm-fg-215 { - color: #ffaf5f; -} -.xterm-fg-216 { - color: #ffaf87; -} -.xterm-fg-217 { - color: #ffafaf; -} -.xterm-fg-218 { - color: #ffafd7; -} -.xterm-fg-219 { - color: #ffafff; -} -.xterm-fg-220 { - color: #ffd700; -} -.xterm-fg-221 { - color: #ffd75f; -} -.xterm-fg-222 { - color: #ffd787; -} -.xterm-fg-223 { - color: #ffd7af; -} -.xterm-fg-224 { - color: #ffd7d7; -} -.xterm-fg-225 { - color: #ffd7ff; -} -.xterm-fg-226 { - color: #ffff00; -} -.xterm-fg-227 { - color: #ffff5f; -} -.xterm-fg-228 { - color: #ffff87; -} -.xterm-fg-229 { - color: #ffffaf; -} -.xterm-fg-230 { - color: #ffffd7; -} -.xterm-fg-231 { - color: #ffffff; -} -.xterm-fg-232 { - color: #080808; -} -.xterm-fg-233 { - color: #121212; -} -.xterm-fg-234 { - color: #1c1c1c; -} -.xterm-fg-235 { - color: #262626; -} -.xterm-fg-236 { - color: #303030; -} -.xterm-fg-237 { - color: #3a3a3a; -} -.xterm-fg-238 { - color: #444444; -} -.xterm-fg-239 { - color: #4e4e4e; -} -.xterm-fg-240 { - color: #585858; -} -.xterm-fg-241 { - color: #626262; -} -.xterm-fg-242 { - color: #6c6c6c; -} -.xterm-fg-243 { - color: #767676; -} -.xterm-fg-244 { - color: #808080; -} -.xterm-fg-245 { - color: #8a8a8a; -} -.xterm-fg-246 { - color: #949494; -} -.xterm-fg-247 { - color: #9e9e9e; -} -.xterm-fg-248 { - color: #a8a8a8; -} -.xterm-fg-249 { - color: #b2b2b2; -} -.xterm-fg-250 { - color: #bcbcbc; -} -.xterm-fg-251 { - color: #c6c6c6; -} -.xterm-fg-252 { - color: #d0d0d0; -} -.xterm-fg-253 { - color: #dadada; -} -.xterm-fg-254 { - color: #e4e4e4; -} -.xterm-fg-255 { - color: #eeeeee; -} diff --git a/app/assets/stylesheets/ci/main/fonts.scss b/app/assets/stylesheets/ci/main/fonts.scss deleted file mode 100644 index 8cc9986415c..00000000000 --- a/app/assets/stylesheets/ci/main/fonts.scss +++ /dev/null @@ -1,2 +0,0 @@ -/** Typo **/ -$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; diff --git a/app/assets/stylesheets/ci/main/layout.scss b/app/assets/stylesheets/ci/main/layout.scss deleted file mode 100644 index fa54481fa05..00000000000 --- a/app/assets/stylesheets/ci/main/layout.scss +++ /dev/null @@ -1,18 +0,0 @@ -html { - overflow-y: scroll; - - &.touch .tooltip { display: none !important; } -} - -body { - margin-bottom: 20px; -} - -.container { - padding-top: 0; - z-index: 5; -} - -.container .content { - margin: 0 0; -} diff --git a/app/assets/stylesheets/ci/main/mixins.scss b/app/assets/stylesheets/ci/main/mixins.scss deleted file mode 100644 index 40040822331..00000000000 --- a/app/assets/stylesheets/ci/main/mixins.scss +++ /dev/null @@ -1,31 +0,0 @@ -@mixin box-shadow($shadow) { - -webkit-box-shadow: $shadow; - -moz-box-shadow: $shadow; - -ms-box-shadow: $shadow; - -o-box-shadow: $shadow; - box-shadow: $shadow; -} - -@mixin border-radius($radius) { - -webkit-border-radius: $radius; - -moz-border-radius: $radius; - -ms-border-radius: $radius; - -o-border-radius: $radius; - border-radius: $radius; -} - -@mixin linear-gradient($from, $to) { - background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); - background-image: -webkit-linear-gradient($from, $to); - background-image: -moz-linear-gradient($from, $to); - background-image: -ms-linear-gradient($from, $to); - background-image: -o-linear-gradient($from, $to); -} - -@mixin transition($transition) { - -webkit-transition: $transition; - -moz-transition: $transition; - -ms-transition: $transition; - -o-transition: $transition; - transition: $transition; -} diff --git a/app/assets/stylesheets/ci/main/variables.scss b/app/assets/stylesheets/ci/main/variables.scss deleted file mode 100644 index a8c672a8057..00000000000 --- a/app/assets/stylesheets/ci/main/variables.scss +++ /dev/null @@ -1,44 +0,0 @@ -/** - * General Colors - */ -$primary_color: #2FA0BB; -$link_color: #3A89A3; -$style_color: #246; -$bg_style_color: #246; -$hover: #D9EDF7; - -/* - * Success colors (green) - */ -$border_success: #019875; -$bg_success: #019875; - -/* - * Danger colors (red) - */ -$border_danger: #d43f3a; -$bg_danger: #d9534f; - -/* - * Primary colors (blue) - */ -$border_primary: #246; -$bg_primary: #246; - -/* - * Warning colors (yellow) - */ -$bg_warning: #EB9532; -$border_warning: #EB9532; - -/** - * Twitter bootstrap variables - */ -$font-size-base: 13px !default; -$nav-pills-active-link-hover-bg: $bg_style_color; -$pagination-active-bg: $bg_style_color; - -/** - * Avatar variables - */ -$avatar_radius: 50%; diff --git a/app/assets/stylesheets/ci/sections/navbar.scss b/app/assets/stylesheets/ci/sections/navbar.scss index efa70eb2956..80d93087e66 100644 --- a/app/assets/stylesheets/ci/sections/navbar.scss +++ b/app/assets/stylesheets/ci/sections/navbar.scss @@ -3,7 +3,7 @@ } .navbar-ci { - background: $style_color; + background: #224466; .navbar-brand { color: #fff; diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/ci/xterm.scss new file mode 100644 index 00000000000..460a6bb2024 --- /dev/null +++ b/app/assets/stylesheets/ci/xterm.scss @@ -0,0 +1,904 @@ +// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg +// see also: https://gist.github.com/jasonm23/2868981 + +$black: #000000; +$red: #cd0000; +$green: #00cd00; +$yellow: #cdcd00; +$blue: #0000ee; // according to wikipedia, this is the xterm standard +//$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) +$magenta: #cd00cd; +$cyan: #00cdcd; +$white: #e5e5e5; +$l-black: #7f7f7f; +$l-red: #ff0000; +$l-green: #00ff00; +$l-yellow: #ffff00; +$l-blue: #5c5cff; +$l-magenta: #ff00ff; +$l-cyan: #00ffff; +$l-white: #ffffff; + +.term-bold { + font-weight: bold; +} +.term-italic { + font-style: italic; +} +.term-conceal { + visibility: hidden; +} +.term-underline { + text-decoration: underline; +} +.term-cross { + text-decoration: line-through; +} + +.term-fg-black { + color: $black; +} +.term-fg-red { + color: $red; +} +.term-fg-green { + color: $green; +} +.term-fg-yellow { + color: $yellow; +} +.term-fg-blue { + color: $blue; +} +.term-fg-magenta { + color: $magenta; +} +.term-fg-cyan { + color: $cyan; +} +.term-fg-white { + color: $white; +} +.term-fg-l-black { + color: $l-black; +} +.term-fg-l-red { + color: $l-red; +} +.term-fg-l-green { + color: $l-green; +} +.term-fg-l-yellow { + color: $l-yellow; +} +.term-fg-l-blue { + color: $l-blue; +} +.term-fg-l-magenta { + color: $l-magenta; +} +.term-fg-l-cyan { + color: $l-cyan; +} +.term-fg-l-white { + color: $l-white; +} + +.term-bg-black { + background-color: $black; +} +.term-bg-red { + background-color: $red; +} +.term-bg-green { + background-color: $green; +} +.term-bg-yellow { + background-color: $yellow; +} +.term-bg-blue { + background-color: $blue; +} +.term-bg-magenta { + background-color: $magenta; +} +.term-bg-cyan { + background-color: $cyan; +} +.term-bg-white { + background-color: $white; +} +.term-bg-l-black { + background-color: $l-black; +} +.term-bg-l-red { + background-color: $l-red; +} +.term-bg-l-green { + background-color: $l-green; +} +.term-bg-l-yellow { + background-color: $l-yellow; +} +.term-bg-l-blue { + background-color: $l-blue; +} +.term-bg-l-magenta { + background-color: $l-magenta; +} +.term-bg-l-cyan { + background-color: $l-cyan; +} +.term-bg-l-white { + background-color: $l-white; +} + + +.xterm-fg-0 { + color: #000000; +} +.xterm-fg-1 { + color: #800000; +} +.xterm-fg-2 { + color: #008000; +} +.xterm-fg-3 { + color: #808000; +} +.xterm-fg-4 { + color: #000080; +} +.xterm-fg-5 { + color: #800080; +} +.xterm-fg-6 { + color: #008080; +} +.xterm-fg-7 { + color: #c0c0c0; +} +.xterm-fg-8 { + color: #808080; +} +.xterm-fg-9 { + color: #ff0000; +} +.xterm-fg-10 { + color: #00ff00; +} +.xterm-fg-11 { + color: #ffff00; +} +.xterm-fg-12 { + color: #0000ff; +} +.xterm-fg-13 { + color: #ff00ff; +} +.xterm-fg-14 { + color: #00ffff; +} +.xterm-fg-15 { + color: #ffffff; +} +.xterm-fg-16 { + color: #000000; +} +.xterm-fg-17 { + color: #00005f; +} +.xterm-fg-18 { + color: #000087; +} +.xterm-fg-19 { + color: #0000af; +} +.xterm-fg-20 { + color: #0000d7; +} +.xterm-fg-21 { + color: #0000ff; +} +.xterm-fg-22 { + color: #005f00; +} +.xterm-fg-23 { + color: #005f5f; +} +.xterm-fg-24 { + color: #005f87; +} +.xterm-fg-25 { + color: #005faf; +} +.xterm-fg-26 { + color: #005fd7; +} +.xterm-fg-27 { + color: #005fff; +} +.xterm-fg-28 { + color: #008700; +} +.xterm-fg-29 { + color: #00875f; +} +.xterm-fg-30 { + color: #008787; +} +.xterm-fg-31 { + color: #0087af; +} +.xterm-fg-32 { + color: #0087d7; +} +.xterm-fg-33 { + color: #0087ff; +} +.xterm-fg-34 { + color: #00af00; +} +.xterm-fg-35 { + color: #00af5f; +} +.xterm-fg-36 { + color: #00af87; +} +.xterm-fg-37 { + color: #00afaf; +} +.xterm-fg-38 { + color: #00afd7; +} +.xterm-fg-39 { + color: #00afff; +} +.xterm-fg-40 { + color: #00d700; +} +.xterm-fg-41 { + color: #00d75f; +} +.xterm-fg-42 { + color: #00d787; +} +.xterm-fg-43 { + color: #00d7af; +} +.xterm-fg-44 { + color: #00d7d7; +} +.xterm-fg-45 { + color: #00d7ff; +} +.xterm-fg-46 { + color: #00ff00; +} +.xterm-fg-47 { + color: #00ff5f; +} +.xterm-fg-48 { + color: #00ff87; +} +.xterm-fg-49 { + color: #00ffaf; +} +.xterm-fg-50 { + color: #00ffd7; +} +.xterm-fg-51 { + color: #00ffff; +} +.xterm-fg-52 { + color: #5f0000; +} +.xterm-fg-53 { + color: #5f005f; +} +.xterm-fg-54 { + color: #5f0087; +} +.xterm-fg-55 { + color: #5f00af; +} +.xterm-fg-56 { + color: #5f00d7; +} +.xterm-fg-57 { + color: #5f00ff; +} +.xterm-fg-58 { + color: #5f5f00; +} +.xterm-fg-59 { + color: #5f5f5f; +} +.xterm-fg-60 { + color: #5f5f87; +} +.xterm-fg-61 { + color: #5f5faf; +} +.xterm-fg-62 { + color: #5f5fd7; +} +.xterm-fg-63 { + color: #5f5fff; +} +.xterm-fg-64 { + color: #5f8700; +} +.xterm-fg-65 { + color: #5f875f; +} +.xterm-fg-66 { + color: #5f8787; +} +.xterm-fg-67 { + color: #5f87af; +} +.xterm-fg-68 { + color: #5f87d7; +} +.xterm-fg-69 { + color: #5f87ff; +} +.xterm-fg-70 { + color: #5faf00; +} +.xterm-fg-71 { + color: #5faf5f; +} +.xterm-fg-72 { + color: #5faf87; +} +.xterm-fg-73 { + color: #5fafaf; +} +.xterm-fg-74 { + color: #5fafd7; +} +.xterm-fg-75 { + color: #5fafff; +} +.xterm-fg-76 { + color: #5fd700; +} +.xterm-fg-77 { + color: #5fd75f; +} +.xterm-fg-78 { + color: #5fd787; +} +.xterm-fg-79 { + color: #5fd7af; +} +.xterm-fg-80 { + color: #5fd7d7; +} +.xterm-fg-81 { + color: #5fd7ff; +} +.xterm-fg-82 { + color: #5fff00; +} +.xterm-fg-83 { + color: #5fff5f; +} +.xterm-fg-84 { + color: #5fff87; +} +.xterm-fg-85 { + color: #5fffaf; +} +.xterm-fg-86 { + color: #5fffd7; +} +.xterm-fg-87 { + color: #5fffff; +} +.xterm-fg-88 { + color: #870000; +} +.xterm-fg-89 { + color: #87005f; +} +.xterm-fg-90 { + color: #870087; +} +.xterm-fg-91 { + color: #8700af; +} +.xterm-fg-92 { + color: #8700d7; +} +.xterm-fg-93 { + color: #8700ff; +} +.xterm-fg-94 { + color: #875f00; +} +.xterm-fg-95 { + color: #875f5f; +} +.xterm-fg-96 { + color: #875f87; +} +.xterm-fg-97 { + color: #875faf; +} +.xterm-fg-98 { + color: #875fd7; +} +.xterm-fg-99 { + color: #875fff; +} +.xterm-fg-100 { + color: #878700; +} +.xterm-fg-101 { + color: #87875f; +} +.xterm-fg-102 { + color: #878787; +} +.xterm-fg-103 { + color: #8787af; +} +.xterm-fg-104 { + color: #8787d7; +} +.xterm-fg-105 { + color: #8787ff; +} +.xterm-fg-106 { + color: #87af00; +} +.xterm-fg-107 { + color: #87af5f; +} +.xterm-fg-108 { + color: #87af87; +} +.xterm-fg-109 { + color: #87afaf; +} +.xterm-fg-110 { + color: #87afd7; +} +.xterm-fg-111 { + color: #87afff; +} +.xterm-fg-112 { + color: #87d700; +} +.xterm-fg-113 { + color: #87d75f; +} +.xterm-fg-114 { + color: #87d787; +} +.xterm-fg-115 { + color: #87d7af; +} +.xterm-fg-116 { + color: #87d7d7; +} +.xterm-fg-117 { + color: #87d7ff; +} +.xterm-fg-118 { + color: #87ff00; +} +.xterm-fg-119 { + color: #87ff5f; +} +.xterm-fg-120 { + color: #87ff87; +} +.xterm-fg-121 { + color: #87ffaf; +} +.xterm-fg-122 { + color: #87ffd7; +} +.xterm-fg-123 { + color: #87ffff; +} +.xterm-fg-124 { + color: #af0000; +} +.xterm-fg-125 { + color: #af005f; +} +.xterm-fg-126 { + color: #af0087; +} +.xterm-fg-127 { + color: #af00af; +} +.xterm-fg-128 { + color: #af00d7; +} +.xterm-fg-129 { + color: #af00ff; +} +.xterm-fg-130 { + color: #af5f00; +} +.xterm-fg-131 { + color: #af5f5f; +} +.xterm-fg-132 { + color: #af5f87; +} +.xterm-fg-133 { + color: #af5faf; +} +.xterm-fg-134 { + color: #af5fd7; +} +.xterm-fg-135 { + color: #af5fff; +} +.xterm-fg-136 { + color: #af8700; +} +.xterm-fg-137 { + color: #af875f; +} +.xterm-fg-138 { + color: #af8787; +} +.xterm-fg-139 { + color: #af87af; +} +.xterm-fg-140 { + color: #af87d7; +} +.xterm-fg-141 { + color: #af87ff; +} +.xterm-fg-142 { + color: #afaf00; +} +.xterm-fg-143 { + color: #afaf5f; +} +.xterm-fg-144 { + color: #afaf87; +} +.xterm-fg-145 { + color: #afafaf; +} +.xterm-fg-146 { + color: #afafd7; +} +.xterm-fg-147 { + color: #afafff; +} +.xterm-fg-148 { + color: #afd700; +} +.xterm-fg-149 { + color: #afd75f; +} +.xterm-fg-150 { + color: #afd787; +} +.xterm-fg-151 { + color: #afd7af; +} +.xterm-fg-152 { + color: #afd7d7; +} +.xterm-fg-153 { + color: #afd7ff; +} +.xterm-fg-154 { + color: #afff00; +} +.xterm-fg-155 { + color: #afff5f; +} +.xterm-fg-156 { + color: #afff87; +} +.xterm-fg-157 { + color: #afffaf; +} +.xterm-fg-158 { + color: #afffd7; +} +.xterm-fg-159 { + color: #afffff; +} +.xterm-fg-160 { + color: #d70000; +} +.xterm-fg-161 { + color: #d7005f; +} +.xterm-fg-162 { + color: #d70087; +} +.xterm-fg-163 { + color: #d700af; +} +.xterm-fg-164 { + color: #d700d7; +} +.xterm-fg-165 { + color: #d700ff; +} +.xterm-fg-166 { + color: #d75f00; +} +.xterm-fg-167 { + color: #d75f5f; +} +.xterm-fg-168 { + color: #d75f87; +} +.xterm-fg-169 { + color: #d75faf; +} +.xterm-fg-170 { + color: #d75fd7; +} +.xterm-fg-171 { + color: #d75fff; +} +.xterm-fg-172 { + color: #d78700; +} +.xterm-fg-173 { + color: #d7875f; +} +.xterm-fg-174 { + color: #d78787; +} +.xterm-fg-175 { + color: #d787af; +} +.xterm-fg-176 { + color: #d787d7; +} +.xterm-fg-177 { + color: #d787ff; +} +.xterm-fg-178 { + color: #d7af00; +} +.xterm-fg-179 { + color: #d7af5f; +} +.xterm-fg-180 { + color: #d7af87; +} +.xterm-fg-181 { + color: #d7afaf; +} +.xterm-fg-182 { + color: #d7afd7; +} +.xterm-fg-183 { + color: #d7afff; +} +.xterm-fg-184 { + color: #d7d700; +} +.xterm-fg-185 { + color: #d7d75f; +} +.xterm-fg-186 { + color: #d7d787; +} +.xterm-fg-187 { + color: #d7d7af; +} +.xterm-fg-188 { + color: #d7d7d7; +} +.xterm-fg-189 { + color: #d7d7ff; +} +.xterm-fg-190 { + color: #d7ff00; +} +.xterm-fg-191 { + color: #d7ff5f; +} +.xterm-fg-192 { + color: #d7ff87; +} +.xterm-fg-193 { + color: #d7ffaf; +} +.xterm-fg-194 { + color: #d7ffd7; +} +.xterm-fg-195 { + color: #d7ffff; +} +.xterm-fg-196 { + color: #ff0000; +} +.xterm-fg-197 { + color: #ff005f; +} +.xterm-fg-198 { + color: #ff0087; +} +.xterm-fg-199 { + color: #ff00af; +} +.xterm-fg-200 { + color: #ff00d7; +} +.xterm-fg-201 { + color: #ff00ff; +} +.xterm-fg-202 { + color: #ff5f00; +} +.xterm-fg-203 { + color: #ff5f5f; +} +.xterm-fg-204 { + color: #ff5f87; +} +.xterm-fg-205 { + color: #ff5faf; +} +.xterm-fg-206 { + color: #ff5fd7; +} +.xterm-fg-207 { + color: #ff5fff; +} +.xterm-fg-208 { + color: #ff8700; +} +.xterm-fg-209 { + color: #ff875f; +} +.xterm-fg-210 { + color: #ff8787; +} +.xterm-fg-211 { + color: #ff87af; +} +.xterm-fg-212 { + color: #ff87d7; +} +.xterm-fg-213 { + color: #ff87ff; +} +.xterm-fg-214 { + color: #ffaf00; +} +.xterm-fg-215 { + color: #ffaf5f; +} +.xterm-fg-216 { + color: #ffaf87; +} +.xterm-fg-217 { + color: #ffafaf; +} +.xterm-fg-218 { + color: #ffafd7; +} +.xterm-fg-219 { + color: #ffafff; +} +.xterm-fg-220 { + color: #ffd700; +} +.xterm-fg-221 { + color: #ffd75f; +} +.xterm-fg-222 { + color: #ffd787; +} +.xterm-fg-223 { + color: #ffd7af; +} +.xterm-fg-224 { + color: #ffd7d7; +} +.xterm-fg-225 { + color: #ffd7ff; +} +.xterm-fg-226 { + color: #ffff00; +} +.xterm-fg-227 { + color: #ffff5f; +} +.xterm-fg-228 { + color: #ffff87; +} +.xterm-fg-229 { + color: #ffffaf; +} +.xterm-fg-230 { + color: #ffffd7; +} +.xterm-fg-231 { + color: #ffffff; +} +.xterm-fg-232 { + color: #080808; +} +.xterm-fg-233 { + color: #121212; +} +.xterm-fg-234 { + color: #1c1c1c; +} +.xterm-fg-235 { + color: #262626; +} +.xterm-fg-236 { + color: #303030; +} +.xterm-fg-237 { + color: #3a3a3a; +} +.xterm-fg-238 { + color: #444444; +} +.xterm-fg-239 { + color: #4e4e4e; +} +.xterm-fg-240 { + color: #585858; +} +.xterm-fg-241 { + color: #626262; +} +.xterm-fg-242 { + color: #6c6c6c; +} +.xterm-fg-243 { + color: #767676; +} +.xterm-fg-244 { + color: #808080; +} +.xterm-fg-245 { + color: #8a8a8a; +} +.xterm-fg-246 { + color: #949494; +} +.xterm-fg-247 { + color: #9e9e9e; +} +.xterm-fg-248 { + color: #a8a8a8; +} +.xterm-fg-249 { + color: #b2b2b2; +} +.xterm-fg-250 { + color: #bcbcbc; +} +.xterm-fg-251 { + color: #c6c6c6; +} +.xterm-fg-252 { + color: #d0d0d0; +} +.xterm-fg-253 { + color: #dadada; +} +.xterm-fg-254 { + color: #e4e4e4; +} +.xterm-fg-255 { + color: #eeeeee; +} diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml index 4a0e45a0217..3d2c7ce06da 100644 --- a/app/views/layouts/ci/_nav.html.haml +++ b/app/views/layouts/ci/_nav.html.haml @@ -19,7 +19,7 @@ %ul.nav.navbar-nav.pull-right - if current_user %li - = link_to new_user_session_path do + = link_to "/profile", no_turbolink do .profile-holder = image_tag user_avatar_url(current_user, 64), class: 'avatar s32', alt: '' %span= current_user.name diff --git a/app/views/layouts/ci/admin.html.haml b/app/views/layouts/ci/admin.html.haml index 71b767cc4f1..cd160a54455 100644 --- a/app/views/layouts/ci/admin.html.haml +++ b/app/views/layouts/ci/admin.html.haml @@ -9,9 +9,10 @@ = yield(:title) %hr - .container - .row - .col-md-2.append-bottom-20 - = render 'layouts/ci/nav_admin' - .col-md-10 - = yield + .container.container-body + .content + .row + .col-md-2.append-bottom-20 + = render 'layouts/ci/nav_admin' + .col-md-10 + = yield diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml index 7306d378e44..8990ffbffe6 100644 --- a/app/views/layouts/ci/application.html.haml +++ b/app/views/layouts/ci/application.html.haml @@ -10,4 +10,5 @@ %hr .container.container-body - = yield + .content + = yield diff --git a/app/views/layouts/ci/empty.html.haml b/app/views/layouts/ci/empty.html.haml index a36ebee7ef3..bda574c0ed1 100644 --- a/app/views/layouts/ci/empty.html.haml +++ b/app/views/layouts/ci/empty.html.haml @@ -9,5 +9,6 @@ %hr .container.container-body - = yield + .content + = yield diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index 9549485d095..f78d749e592 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -15,12 +15,13 @@ .pull-right = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' ) %hr - .container - - if current_user && can?(current_user, :manage_project, gl_project) - .row - .col-md-2.append-bottom-20 - = render 'layouts/ci/nav_project' - .col-md-10 - = yield - - else - = yield + .container.container-body + .content + - if current_user && can?(current_user, :manage_project, gl_project) + .row + .col-md-2.append-bottom-20 + = render 'layouts/ci/nav_project' + .col-md-10 + = yield + - else + = yield -- cgit v1.2.1 From 6db2843bc2e662f785625eb8aff6803c3e2786f8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 10 Sep 2015 17:54:09 +0300 Subject: fix of project creation --- app/controllers/ci/projects_controller.rb | 2 +- app/controllers/ci/user_sessions_controller.rb | 55 --------------------- app/models/ci/network.rb | 2 +- app/models/ci/user.rb | 67 -------------------------- app/models/ci/user_session.rb | 23 --------- app/services/ci/create_project_service.rb | 2 +- app/services/ci/register_build_service.rb | 2 +- app/views/ci/admin/projects/index.html.haml | 3 +- app/views/ci/projects/_gl_projects.html.haml | 2 +- 9 files changed, 7 insertions(+), 151 deletions(-) delete mode 100644 app/controllers/ci/user_sessions_controller.rb delete mode 100644 app/models/ci/user.rb delete mode 100644 app/models/ci/user_session.rb diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 80a5e602171..c30c5d9590b 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -55,7 +55,7 @@ module Ci def create project_data = OpenStruct.new(JSON.parse(params["project"])) - unless can?(current_user, :manage_project, ::Project.find(project_data.id)) + unless can?(current_user, :admin_project, ::Project.find(project_data.id)) return redirect_to ci_root_path, alert: 'You have to have at least master role to enable CI for this project' end diff --git a/app/controllers/ci/user_sessions_controller.rb b/app/controllers/ci/user_sessions_controller.rb deleted file mode 100644 index 818e1fcdea1..00000000000 --- a/app/controllers/ci/user_sessions_controller.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Ci - class UserSessionsController < Ci::ApplicationController - before_filter :authenticate_user!, except: [:new, :callback, :auth] - - def show - @user = current_user - end - - def new - end - - def auth - redirect_to client.auth_code.authorize_url({ - redirect_uri: callback_ci_user_sessions_url, - state: params[:state] - }) - end - - def callback - token = client.auth_code.get_token(params[:code], redirect_uri: callback_ci_user_sessions_url).token - - @user_session = Ci::UserSession.new - user = @user_session.authenticate(access_token: token) - - if user && sign_in(user) - return_to = get_ouath_state_return_to(params[:state]) - redirect_to(return_to || ci_root_path) - else - @error = 'Invalid credentials' - render :new - end - - end - - def destroy - sign_out - - redirect_to new_ci_user_sessions_path - end - - protected - - def client - @client ||= ::OAuth2::Client.new( - GitlabCi.config.gitlab_server.app_id, - GitlabCi.config.gitlab_server.app_secret, - { - site: GitlabCi.config.gitlab_server.url, - authorize_url: 'oauth/authorize', - token_url: 'oauth/token' - } - ) - end - end -end diff --git a/app/models/ci/network.rb b/app/models/ci/network.rb index c307907e6b8..1a54213f4f1 100644 --- a/app/models/ci/network.rb +++ b/app/models/ci/network.rb @@ -99,7 +99,7 @@ module Ci private def url - GitlabCi.config.gitlab_server.url + Gitlab.config.gitlab.url end def default_opts diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb deleted file mode 100644 index f7e2eaae1f1..00000000000 --- a/app/models/ci/user.rb +++ /dev/null @@ -1,67 +0,0 @@ -# User object is stored in session -module Ci - class User - DEVELOPER_ACCESS = 30 - - attr_reader :attributes - - def initialize(hash) - @attributes = hash - end - - def gitlab_projects(search = nil, page = 1, per_page = 100) - Rails.cache.fetch(cache_key(page, per_page, search)) do - Ci::Project.from_gitlab(self, :authorized, { page: page, per_page: per_page, search: search, ci_enabled_first: true }) - end - end - - def method_missing(meth, *args, &block) - if attributes.has_key?(meth.to_s) - attributes[meth.to_s] - else - super - end - end - - def avatar_url - attributes['avatar_url'] - end - - def cache_key(*args) - "#{self.id}:#{args.join(":")}:#{sync_at.to_s}" - end - - def sync_at - @sync_at ||= Time.now - end - - def reset_cache - @sync_at = Time.now - end - - def authorized_runners - Ci::Runner.specific.includes(:runner_projects). - where(Ci::RunnerProject.table_name => { project_id: authorized_projects } ) - end - - def authorized_projects - Ci::Project.where(gitlab_id: current_user.authorized_projects.map(&:id)) - end - - def authenticate_options - if attributes['access_token'] - { access_token: attributes['access_token'] } - else - { private_token: attributes['private_token'] } - end - end - - private - - def project_info(project_gitlab_id) - Rails.cache.fetch(cache_key("project_info", project_gitlab_id, sync_at)) do - Ci::Network.new.project(authenticate_options, project_gitlab_id) - end - end - end -end diff --git a/app/models/ci/user_session.rb b/app/models/ci/user_session.rb deleted file mode 100644 index 27c71e30591..00000000000 --- a/app/models/ci/user_session.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Ci - class UserSession - include ActiveModel::Conversion - include Ci::StaticModel - extend ActiveModel::Naming - - def authenticate(auth_opts) - network = Ci::Network.new - user = network.authenticate(auth_opts) - - if user - user["access_token"] = auth_opts[:access_token] - return Ci::User.new(user) - else - nil - end - - user - rescue - nil - end - end -end diff --git a/app/services/ci/create_project_service.rb b/app/services/ci/create_project_service.rb index 049ac2e9181..81bbc29bb4f 100644 --- a/app/services/ci/create_project_service.rb +++ b/app/services/ci/create_project_service.rb @@ -13,7 +13,7 @@ module Ci project_url: project_route.gsub(":project_id", @project.id.to_s), } - unless Ci::Network.new.enable_ci(@project.gitlab_id, data, current_user.authenticate_options) + unless Ci::Network.new.enable_ci(@project.gitlab_id, data, {private_token: current_user.private_token}) raise ActiveRecord::Rollback end end diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 7e0b58a5dc9..33f1c1e918d 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,7 +8,7 @@ module Ci builds = if current_runner.shared? # don't run projects which have not enables shared runners - builds.includes(:project).where(projects: { shared_runners_enabled: true }) + builds.includes(:project).where(ci_projects: { shared_runners_enabled: true }) else # do run projects which are only assigned to this runner builds.where(project_id: current_runner.projects) diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml index 73956575a89..d6c0243880f 100644 --- a/app/views/ci/admin/projects/index.html.haml +++ b/app/views/ci/admin/projects/index.html.haml @@ -8,7 +8,8 @@ %th Builds %th - = render @projects + - @projects.each do |project| + = render "ci/admin/projects/project", project: @projects = paginate @projects diff --git a/app/views/ci/projects/_gl_projects.html.haml b/app/views/ci/projects/_gl_projects.html.haml index f63d8246e96..7bd30b37caf 100644 --- a/app/views/ci/projects/_gl_projects.html.haml +++ b/app/views/ci/projects/_gl_projects.html.haml @@ -11,5 +11,5 @@ Added - else = form_tag ci_projects_path do - = hidden_field_tag :project, project.to_json + = hidden_field_tag :project, project.to_json(methods: [:name_with_namespace, :path_with_namespace, :ssh_url_to_repo]) = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm' -- cgit v1.2.1 From 4e7b47dde1077690cc90b7c8779158af0f175c9a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 16:55:09 +0200 Subject: Convert ci features specs to v3 --- spec/features/ci/admin/builds_spec.rb | 22 +++++++++--------- spec/features/ci/admin/events_spec.rb | 4 ++-- spec/features/ci/admin/projects_spec.rb | 2 +- spec/features/ci/admin/runners_spec.rb | 14 ++++++------ spec/features/ci/builds_spec.rb | 16 ++++++------- spec/features/ci/commits_spec.rb | 40 ++++++++++++++++----------------- spec/features/ci/events_spec.rb | 4 ++-- spec/features/ci/lint_spec.rb | 12 +++++----- spec/features/ci/projects_spec.rb | 26 ++++++++++----------- spec/features/ci/runners_spec.rb | 18 +++++++-------- spec/features/ci/triggers_spec.rb | 6 ++--- spec/features/ci/variables_spec.rb | 4 ++-- spec/support/stub_gitlab_calls.rb | 6 ++--- 13 files changed, 87 insertions(+), 87 deletions(-) diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/ci/admin/builds_spec.rb index e62e83692da..d524dc29795 100644 --- a/spec/features/ci/admin/builds_spec.rb +++ b/spec/features/ci/admin/builds_spec.rb @@ -16,8 +16,8 @@ describe "Admin Builds" do visit admin_builds_path end - it { page.should have_content "All builds" } - it { page.should have_content build.short_sha } + it { expect(page).to have_content "All builds" } + it { expect(page).to have_content build.short_sha } end describe "Tabs" do @@ -29,7 +29,7 @@ describe "Admin Builds" do visit admin_builds_path - page.all(".build-link").size.should == 4 + expect(page.all(".build-link").size).to eq(4) end it "shows pending builds" do @@ -44,10 +44,10 @@ describe "Admin Builds" do click_on "Pending" end - page.find(".build-link").should have_content(build.id) - page.find(".build-link").should_not have_content(build1.id) - page.find(".build-link").should_not have_content(build2.id) - page.find(".build-link").should_not have_content(build3.id) + expect(page.find(".build-link")).to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build1.id) + expect(page.find(".build-link")).not_to have_content(build2.id) + expect(page.find(".build-link")).not_to have_content(build3.id) end it "shows running builds" do @@ -62,10 +62,10 @@ describe "Admin Builds" do click_on "Running" end - page.find(".build-link").should have_content(build1.id) - page.find(".build-link").should_not have_content(build.id) - page.find(".build-link").should_not have_content(build2.id) - page.find(".build-link").should_not have_content(build3.id) + expect(page.find(".build-link")).to have_content(build1.id) + expect(page.find(".build-link")).not_to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build2.id) + expect(page.find(".build-link")).not_to have_content(build3.id) end end end diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb index 469c6ed102d..4f5dbd9fb6b 100644 --- a/spec/features/ci/admin/events_spec.rb +++ b/spec/features/ci/admin/events_spec.rb @@ -14,7 +14,7 @@ describe "Admin Events" do visit admin_events_path end - it { page.should have_content "Events" } - it { page.should have_content event.description } + it { expect(page).to have_content "Events" } + it { expect(page).to have_content event.description } end end diff --git a/spec/features/ci/admin/projects_spec.rb b/spec/features/ci/admin/projects_spec.rb index 6f87e368deb..9113300077d 100644 --- a/spec/features/ci/admin/projects_spec.rb +++ b/spec/features/ci/admin/projects_spec.rb @@ -14,6 +14,6 @@ describe "Admin Projects" do visit admin_projects_path end - it { page.should have_content "Projects" } + it { expect(page).to have_content "Projects" } end end diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index 2827a7fc6e5..fa3beb7b915 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -27,8 +27,8 @@ describe "Admin Runners" do click_button 'Search' end - it { page.should have_content("foo") } - it { page.should_not have_content("bar") } + it { expect(page).to have_content("foo") } + it { expect(page).not_to have_content("bar") } end end @@ -42,12 +42,12 @@ describe "Admin Runners" do end describe 'runner info' do - it { find_field('runner_token').value.should eq runner.token } + it { expect(find_field('runner_token').value).to eq runner.token } end describe 'projects' do - it { page.should have_content("foo") } - it { page.should have_content("bar") } + it { expect(page).to have_content("foo") } + it { expect(page).to have_content("bar") } end describe 'search' do @@ -56,8 +56,8 @@ describe "Admin Runners" do click_button 'Search' end - it { page.should have_content("foo") } - it { page.should_not have_content("bar") } + it { expect(page).to have_content("foo") } + it { expect(page).not_to have_content("bar") } end end end diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb index fcd7996efd7..ddfc579d1b8 100644 --- a/spec/features/ci/builds_spec.rb +++ b/spec/features/ci/builds_spec.rb @@ -13,9 +13,9 @@ describe "Builds" do visit project_build_path(@project, @build) end - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } end describe "GET /:project/builds/:id/cancel" do @@ -25,8 +25,8 @@ describe "Builds" do visit cancel_project_build_path(@project, @build) end - it { page.should have_content 'canceled' } - it { page.should have_content 'Retry' } + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content 'Retry' } end describe "POST /:project/builds/:id/retry" do @@ -37,8 +37,8 @@ describe "Builds" do click_link 'Retry' end - it { page.should have_content 'pending' } - it { page.should have_content 'Cancel' } + it { expect(page).to have_content 'pending' } + it { expect(page).to have_content 'Cancel' } end describe "Show page public accessible" do @@ -52,6 +52,6 @@ describe "Builds" do visit project_build_path(@project, @build) end - it { page.should have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.sha[0..7] } end end diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb index 202f05c516f..774beee9a10 100644 --- a/spec/features/ci/commits_spec.rb +++ b/spec/features/ci/commits_spec.rb @@ -4,63 +4,63 @@ describe "Commits" do context "Authenticated user" do before do login_as :user - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit + @project = FactoryGirl.create :ci_project + @commit = FactoryGirl.create :ci_commit, project: @project + @build = FactoryGirl.create :ci_build, commit: @commit end describe "GET /:project/commits/:sha" do before do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) end - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } end describe "Cancel commit" do it "cancels commit" do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) click_on "Cancel" - page.should have_content "canceled" + expect(page).to have_content "canceled" end end describe ".gitlab-ci.yml not found warning" do it "does not show warning" do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) - page.should_not have_content ".gitlab-ci.yml not found in this commit" + expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" end it "shows warning" do @commit.push_data[:ci_yaml_file] = nil @commit.save - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) - page.should have_content ".gitlab-ci.yml not found in this commit" + expect(page).to have_content ".gitlab-ci.yml not found in this commit" end end end context "Public pages" do before do - @project = FactoryGirl.create :public_project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit + @project = FactoryGirl.create :ci_public_project + @commit = FactoryGirl.create :ci_commit, project: @project + @build = FactoryGirl.create :ci_build, commit: @commit end describe "GET /:project/commits/:sha" do before do - visit project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) end - it { page.should have_content @commit.sha[0..7] } - it { page.should have_content @commit.git_commit_message } - it { page.should have_content @commit.git_author_name } + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } end end end diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb index 77d1fba5769..d1bcf493eaa 100644 --- a/spec/features/ci/events_spec.rb +++ b/spec/features/ci/events_spec.rb @@ -14,7 +14,7 @@ describe "Events" do visit project_events_path(project) end - it { page.should have_content "Events" } - it { page.should have_content event.description } + it { expect(page).to have_content "Events" } + it { expect(page).to have_content event.description } end end diff --git a/spec/features/ci/lint_spec.rb b/spec/features/ci/lint_spec.rb index 0b3d4e099fb..820ed5b4716 100644 --- a/spec/features/ci/lint_spec.rb +++ b/spec/features/ci/lint_spec.rb @@ -11,10 +11,10 @@ describe "Lint" do fill_in "content", with: content click_on "Validate" within "table" do - page.should have_content("Job - rspec") - page.should have_content("Job - spinach") - page.should have_content("Deploy Job - staging") - page.should have_content("Deploy Job - production") + expect(page).to have_content("Job - rspec") + expect(page).to have_content("Job - spinach") + expect(page).to have_content("Deploy Job - staging") + expect(page).to have_content("Deploy Job - production") end end @@ -22,7 +22,7 @@ describe "Lint" do visit lint_path fill_in "content", with: "" click_on "Validate" - page.should have_content("Status: syntax is incorrect") - page.should have_content("Error: Please provide content of .gitlab-ci.yml") + expect(page).to have_content("Status: syntax is incorrect") + expect(page).to have_content("Error: Please provide content of .gitlab-ci.yml") end end diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb index 3f21af92a2b..8f6c238743f 100644 --- a/spec/features/ci/projects_spec.rb +++ b/spec/features/ci/projects_spec.rb @@ -12,8 +12,8 @@ describe "Projects" do visit projects_path end - it { page.should have_content "GitLab / gitlab-shell" } - it { page.should have_selector ".search input#search" } + it { expect(page).to have_content "GitLab / gitlab-shell" } + it { expect(page).to have_selector ".search input#search" } end describe "GET /projects/:id" do @@ -21,8 +21,8 @@ describe "Projects" do visit project_path(@project) end - it { page.should have_content @project.name } - it { page.should have_content 'All commits' } + it { expect(page).to have_content @project.name } + it { expect(page).to have_content 'All commits' } end describe "GET /projects/:id/edit" do @@ -30,16 +30,16 @@ describe "Projects" do visit edit_project_path(@project) end - it { page.should have_content @project.name } - it { page.should have_content 'Build Schedule' } + it { expect(page).to have_content @project.name } + it { expect(page).to have_content 'Build Schedule' } it "updates configuration" do fill_in 'Timeout', with: '70' click_button 'Save changes' - page.should have_content 'was successfully updated' + expect(page).to have_content 'was successfully updated' - find_field('Timeout').value.should eq '70' + expect(find_field('Timeout').value).to eq '70' end end @@ -48,10 +48,10 @@ describe "Projects" do visit project_charts_path(@project) end - it { page.should have_content 'Overall' } - it { page.should have_content 'Builds chart for last week' } - it { page.should have_content 'Builds chart for last month' } - it { page.should have_content 'Builds chart for last year' } - it { page.should have_content 'Commit duration in minutes for last 30 commits' } + it { expect(page).to have_content 'Overall' } + it { expect(page).to have_content 'Builds chart for last week' } + it { expect(page).to have_content 'Builds chart for last month' } + it { expect(page).to have_content 'Builds chart for last year' } + it { expect(page).to have_content 'Commit duration in minutes for last 30 commits' } end end diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb index c41dc5b2e2e..ea28170bb2c 100644 --- a/spec/features/ci/runners_spec.rb +++ b/spec/features/ci/runners_spec.rb @@ -12,7 +12,7 @@ describe "Runners" do stub_js_gitlab_calls # all projects should be authorized for user - Network.any_instance.stub(:projects).and_return([ + allow_any_instance_of(Network).to receive(:projects).and_return([ OpenStruct.new({id: @project.gitlab_id}), OpenStruct.new({id: @project2.gitlab_id}) ]) @@ -26,9 +26,9 @@ describe "Runners" do it "places runners in right places" do visit project_runners_path(@project) - page.find(".available-specific-runners").should have_content(@specific_runner2.display_name) - page.find(".activated-specific-runners").should have_content(@specific_runner.display_name) - page.find(".available-shared-runners").should have_content(@shared_runner.display_name) + expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name) + expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name) + expect(page.find(".available-shared-runners")).to have_content(@shared_runner.display_name) end it "enables specific runner for project" do @@ -38,7 +38,7 @@ describe "Runners" do click_on "Enable for this project" end - page.find(".activated-specific-runners").should have_content(@specific_runner2.display_name) + expect(page.find(".activated-specific-runners")).to have_content(@specific_runner2.display_name) end it "disables specific runner for project" do @@ -50,7 +50,7 @@ describe "Runners" do click_on "Disable for this project" end - page.find(".available-specific-runners").should have_content(@specific_runner.display_name) + expect(page.find(".available-specific-runners")).to have_content(@specific_runner.display_name) end it "removes specific runner for project if this is last project for that runners" do @@ -60,7 +60,7 @@ describe "Runners" do click_on "Remove runner" end - Runner.exists?(id: @specific_runner).should be_false + expect(Runner.exists?(id: @specific_runner)).to be_falsey end end @@ -75,7 +75,7 @@ describe "Runners" do click_on "Enable shared runners" - @project.reload.shared_runners_enabled.should be_true + expect(@project.reload.shared_runners_enabled).to be_truthy end end @@ -92,7 +92,7 @@ describe "Runners" do click_on @specific_runner.short_sha - page.should have_content(@specific_runner.platform) + expect(page).to have_content(@specific_runner.platform) end end end diff --git a/spec/features/ci/triggers_spec.rb b/spec/features/ci/triggers_spec.rb index 2076429383d..39ef67578fb 100644 --- a/spec/features/ci/triggers_spec.rb +++ b/spec/features/ci/triggers_spec.rb @@ -11,16 +11,16 @@ describe 'Variables' do context 'create a trigger' do before do click_on 'Add Trigger' - @project.triggers.count.should == 1 + expect(@project.triggers.count).to eq(1) end it 'contains trigger token' do - page.should have_content(@project.triggers.first.token) + expect(page).to have_content(@project.triggers.first.token) end it 'revokes the trigger' do click_on 'Revoke' - @project.triggers.count.should == 0 + expect(@project.triggers.count).to eq(0) end end end diff --git a/spec/features/ci/variables_spec.rb b/spec/features/ci/variables_spec.rb index 2bb0d9dedde..2e75c9fa1a7 100644 --- a/spec/features/ci/variables_spec.rb +++ b/spec/features/ci/variables_spec.rb @@ -18,8 +18,8 @@ describe "Variables" do fill_in "Value", with: "SECRET_VALUE" click_on "Save changes" - page.should have_content("Variables were successfully updated.") - @project.variables.count.should == 1 + expect(page).to have_content("Variables were successfully updated.") + expect(@project.variables.count).to eq(1) end end diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 51425e3095c..fadc3df412b 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -10,7 +10,7 @@ module StubGitlabCalls end def stub_js_gitlab_calls - Network.any_instance.stub(:projects) { project_hash_array } + allow_any_instance_of(Network).to receive(:projects) { project_hash_array } end private @@ -42,12 +42,12 @@ module StubGitlabCalls def stub_project_8 data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8.json')) - Network.any_instance.stub(:project).and_return(JSON.parse(data)) + allow_any_instance_of(Network).to receive(:project).and_return(JSON.parse(data)) end def stub_project_8_hooks data = File.read(Rails.root.join('spec/support/gitlab_stubs/project_8_hooks.json')) - Network.any_instance.stub(:project_hooks).and_return(JSON.parse(data)) + allow_any_instance_of(Network).to receive(:project_hooks).and_return(JSON.parse(data)) end def stub_projects -- cgit v1.2.1 From c1de46dbc7ea1afc89f059bdc87a5b717bc322c9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 17:14:55 +0200 Subject: Fix project pages for authorized user --- app/assets/stylesheets/ci/application.scss | 4 ++++ app/controllers/ci/application_controller.rb | 5 ++--- app/controllers/ci/runners_controller.rb | 4 +++- app/views/ci/builds/show.html.haml | 4 ++-- app/views/layouts/ci/project.html.haml | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/ci/application.scss b/app/assets/stylesheets/ci/application.scss index 7a124ae87d9..2f2928f7054 100644 --- a/app/assets/stylesheets/ci/application.scss +++ b/app/assets/stylesheets/ci/application.scss @@ -52,4 +52,8 @@ $nprogress-color: #9BC; body { padding-top: 0 !important; + + a { + color: #3084bb; + } } diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index e5c99066a68..9a32efaee7d 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -8,7 +8,6 @@ module Ci rescue_from Ci::Network::UnauthorizedError, with: :invalid_token before_filter :default_headers - #before_filter :check_config helper_method :gl_project protect_from_forgery @@ -38,7 +37,7 @@ module Ci end def authorize_manage_builds! - unless can?(current_user, :manage_builds, gl_project) + unless can?(current_user, :admin_project, gl_project) return page_404 end end @@ -48,7 +47,7 @@ module Ci end def authorize_manage_project! - unless can?(current_user, :manage_project, gl_project) + unless can?(current_user, :admin_project, gl_project) return page_404 end end diff --git a/app/controllers/ci/runners_controller.rb b/app/controllers/ci/runners_controller.rb index 0ef32ce928f..0e9d576a15b 100644 --- a/app/controllers/ci/runners_controller.rb +++ b/app/controllers/ci/runners_controller.rb @@ -10,7 +10,9 @@ module Ci def index @runners = @project.runners.order('id DESC') - @specific_runners = current_user.authorized_runners. + @specific_runners = + Ci::Runner.specific.includes(:runner_projects). + where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ). where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index a698176c3ed..db8926e30d3 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -2,7 +2,7 @@ = link_to ci_project_path(@project) @ = @commit.short_sha - + %p = link_to ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) do ← Back to project commit @@ -104,7 +104,7 @@ #{time_ago_in_words(@build.finished_at)} ago %p %span.attr-name Runner: - - if @build.runner && current_user && current_user.is_admin + - if @build.runner && current_user && current_user.admin \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)} - elsif @build.runner \##{@build.runner.id} diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index f78d749e592..88c21211a57 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -17,7 +17,7 @@ %hr .container.container-body .content - - if current_user && can?(current_user, :manage_project, gl_project) + - if current_user && can?(current_user, :admin_project, gl_project) .row .col-md-2.append-bottom-20 = render 'layouts/ci/nav_project' -- cgit v1.2.1 From dc2e38e56db43c86e7ecf44c01234130f648d350 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 17:27:17 +0200 Subject: Config does not exists any more Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a4cc0d4abc..ddf4e31204a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,6 @@ before_script: - which ruby - gem install bundler --no-ri --no-rdoc - cp config/gitlab.yml.example config/gitlab.yml - - cp config/gitlab_ci.yml.example config/gitlab_ci.yml - touch log/application.log - touch log/test.log - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" -- cgit v1.2.1 From 9a9417ee8e8f3d8fe8320eaaf150ff1eb77a471e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 18:12:14 +0200 Subject: Fix more tests --- spec/controllers/ci/projects_controller_spec.rb | 6 +-- .../ci/project_services/hip_chat_message_spec.rb | 8 ++-- .../ci/project_services/hip_chat_service_spec.rb | 8 ++-- .../ci/project_services/slack_message_spec.rb | 4 +- .../ci/project_services/slack_service_spec.rb | 6 +-- spec/models/ci/user_spec.rb | 50 ---------------------- 6 files changed, 16 insertions(+), 66 deletions(-) delete mode 100644 spec/models/ci/user_spec.rb diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index 9af766eff33..563064b0cef 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -55,7 +55,7 @@ describe Ci::ProjectsController do end let(:user) do - Ci::User.new(user_data) + create(:user) end it "creates project" do @@ -73,7 +73,7 @@ describe Ci::ProjectsController do it "shows error" do allow(controller).to receive(:reset_cache) { true } allow(controller).to receive(:current_user) { user } - allow_any_instance_of(Ci::User).to receive(:can_manage_project?).and_return(false) + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access @@ -91,7 +91,7 @@ describe Ci::ProjectsController do end let(:user) do - Ci::User.new(user_data) + create(:user) end it "searches projects" do diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb index 7318898b3b4..49ac0860259 100644 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' describe Ci::HipChatMessage do - subject { HipChatMessage.new(build) } + subject { Ci::HipChatMessage.new(build) } - let(:project) { FactoryGirl.create(:project) } + let(:project) { FactoryGirl.create(:ci_project) } context "One build" do - let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } let(:build) do commit.create_builds @@ -37,7 +37,7 @@ describe Ci::HipChatMessage do end context "Several builds" do - let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs, project: project) } let(:build) do commit.builds.first diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index 5d1c6c0900b..33a3a8109e5 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -32,10 +32,10 @@ describe Ci::HipChatService do describe "Execute" do - let(:service) { HipChatService.new } - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:service) { Ci::HipChatService.new } + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } before do diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index 8d3bf86ae7a..ef0714909d5 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' describe Ci::SlackMessage do subject { SlackMessage.new(commit) } - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } context "One build" do - let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } let(:build) do commit.create_builds diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index 3f064bffc89..ae577adfb75 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -31,9 +31,9 @@ describe Ci::SlackService do describe "Execute" do let(:slack) { SlackService.new } - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' } + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } let(:notify_only_broken_builds) { false } diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb deleted file mode 100644 index df42d4ddb8e..00000000000 --- a/spec/models/ci/user_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'spec_helper' - -describe Ci::User do - - describe "authorized_projects" do - let (:user) { User.new({}) } - - before do - FactoryGirl.create :ci_project, gitlab_id: 1 - FactoryGirl.create :ci_project, gitlab_id: 2 - gitlab_project = OpenStruct.new({id: 1}) - gitlab_project1 = OpenStruct.new({id: 2}) - allow_any_instance_of(User).to receive(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) - end - - it "returns projects" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(true) - - expect(user.authorized_projects.count).to eq(2) - end - - it "empty list if user miss manage permission" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - - expect(user.authorized_projects.count).to eq(0) - end - end - - describe "authorized_runners" do - it "returns authorized runners" do - project = FactoryGirl.create :ci_project, gitlab_id: 1 - project1 = FactoryGirl.create :ci_project, gitlab_id: 2 - gitlab_project = OpenStruct.new({id: 1}) - gitlab_project1 = OpenStruct.new({id: 2}) - allow_any_instance_of(User).to receive(:gitlab_projects).and_return([gitlab_project, gitlab_project1]) - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(true) - user = User.new({}) - - runner = FactoryGirl.create :ci_specific_runner - runner1 = FactoryGirl.create :ci_specific_runner - runner2 = FactoryGirl.create :ci_specific_runner - - project.runners << runner - project1.runners << runner1 - - expect(user.authorized_runners).to include(runner, runner1) - expect(user.authorized_runners).not_to include(runner2) - end - end -end -- cgit v1.2.1 From a8dd4d36e2ba2bcf2488ce12aa0809c339653fc8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 12:52:56 +0200 Subject: Fix build features specs --- app/models/ci/project.rb | 4 ++- spec/factories/ci/projects.rb | 2 +- spec/features/ci/builds_spec.rb | 79 +++++++++++++++++++++-------------------- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 9c9198302f6..c9e5707f218 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -28,9 +28,11 @@ module Ci class Project < ActiveRecord::Base extend Ci::Model - + include Ci::ProjectStatus + belongs_to :gl_project, class_name: 'Project', foreign_key: :gitlab_id + has_many :commits, ->() { order(:committed_at) }, dependent: :destroy, class_name: 'Ci::Commit' has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb index e6be88fa585..e6bd0685f8d 100644 --- a/spec/factories/ci/projects.rb +++ b/spec/factories/ci/projects.rb @@ -43,7 +43,7 @@ FactoryGirl.define do "git@demo.gitlab.com:gitlab/gitlab-shell#{n}.git" end - sequence :gitlab_id + gl_project factory: :project factory :ci_project do token 'iPWx6WM4lhHNedGfBpPJNP' diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb index ddfc579d1b8..2f020e524e2 100644 --- a/spec/features/ci/builds_spec.rb +++ b/spec/features/ci/builds_spec.rb @@ -1,57 +1,60 @@ require 'spec_helper' describe "Builds" do - before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit - end - - describe "GET /:project/builds/:id" do + context :private_project do before do + @project = FactoryGirl.create :ci_project + @commit = FactoryGirl.create :ci_commit, project: @project + @build = FactoryGirl.create :ci_build, commit: @commit login_as :user - visit project_build_path(@project, @build) + @project.gl_project.team << [@user, :master] end - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } - end + describe "GET /:project/builds/:id" do + before do + visit ci_project_build_path(@project, @build) + end - describe "GET /:project/builds/:id/cancel" do - before do - login_as :user - @build.run! - visit cancel_project_build_path(@project, @build) + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } end - it { expect(page).to have_content 'canceled' } - it { expect(page).to have_content 'Retry' } - end + describe "GET /:project/builds/:id/cancel" do + before do + @build.run! + visit cancel_ci_project_build_path(@project, @build) + end - describe "POST /:project/builds/:id/retry" do - before do - login_as :user - @build.cancel! - visit project_build_path(@project, @build) - click_link 'Retry' + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content 'Retry' } end - it { expect(page).to have_content 'pending' } - it { expect(page).to have_content 'Cancel' } + describe "POST /:project/builds/:id/retry" do + before do + @build.cancel! + visit ci_project_build_path(@project, @build) + click_link 'Retry' + end + + it { expect(page).to have_content 'pending' } + it { expect(page).to have_content 'Cancel' } + end end - describe "Show page public accessible" do - before do - @project = FactoryGirl.create :public_project - @commit = FactoryGirl.create :commit, project: @project - @runner = FactoryGirl.create :specific_runner - @build = FactoryGirl.create :build, commit: @commit, runner: @runner + context :public_project do + describe "Show page public accessible" do + before do + @project = FactoryGirl.create :ci_public_project + @commit = FactoryGirl.create :ci_commit, project: @project + @runner = FactoryGirl.create :ci_specific_runner + @build = FactoryGirl.create :ci_build, commit: @commit, runner: @runner - stub_gitlab_calls - visit project_build_path(@project, @build) - end + stub_gitlab_calls + visit ci_project_build_path(@project, @build) + end - it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.sha[0..7] } + end end end -- cgit v1.2.1 From 0615de013d1e30d147ca7c62fa62a251567337b1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 13:05:31 +0200 Subject: Fix ci commits features specs --- app/assets/stylesheets/ci/sections/builds.scss | 8 ++++++++ app/controllers/ci/commits_controller.rb | 2 +- app/helpers/ci/commits_helper.rb | 6 +++++- spec/features/ci/commits_spec.rb | 15 +++++++++------ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/ci/sections/builds.scss b/app/assets/stylesheets/ci/sections/builds.scss index a9d39bb0cbd..600919635d0 100644 --- a/app/assets/stylesheets/ci/sections/builds.scss +++ b/app/assets/stylesheets/ci/sections/builds.scss @@ -52,3 +52,11 @@ pre.trace { color: #777; } } + +.alert-disabled { + background: #EEE; + + a { + color: #3084bb !important; + } +} diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb index bad9075dde6..0f7f5485661 100644 --- a/app/controllers/ci/commits_controller.rb +++ b/app/controllers/ci/commits_controller.rb @@ -21,7 +21,7 @@ module Ci def cancel commit.builds.running_or_pending.each(&:cancel) - redirect_to ci_project_ref_commit_path(project, commit.ref, commit.sha) + redirect_to ci_project_ref_commits_path(project, commit.ref, commit.sha) end private diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb index 748d12138b1..86f254223cb 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -15,8 +15,12 @@ module Ci end end + def ci_commit_path(commit) + ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) + end + def commit_link(commit) - link_to(commit.short_sha, ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)) + link_to(commit.short_sha, ci_commit_path(commit)) end def truncate_first_line(message, length = 50) diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb index 774beee9a10..40a62ca4574 100644 --- a/spec/features/ci/commits_spec.rb +++ b/spec/features/ci/commits_spec.rb @@ -1,17 +1,20 @@ require 'spec_helper' describe "Commits" do + include Ci::CommitsHelper + context "Authenticated user" do before do - login_as :user @project = FactoryGirl.create :ci_project @commit = FactoryGirl.create :ci_commit, project: @project @build = FactoryGirl.create :ci_build, commit: @commit + login_as :user + @project.gl_project.team << [@user, :master] end describe "GET /:project/commits/:sha" do before do - visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_commit_path(@commit) end it { expect(page).to have_content @commit.sha[0..7] } @@ -21,7 +24,7 @@ describe "Commits" do describe "Cancel commit" do it "cancels commit" do - visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_commit_path(@commit) click_on "Cancel" expect(page).to have_content "canceled" @@ -30,7 +33,7 @@ describe "Commits" do describe ".gitlab-ci.yml not found warning" do it "does not show warning" do - visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_commit_path(@commit) expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" end @@ -39,7 +42,7 @@ describe "Commits" do @commit.push_data[:ci_yaml_file] = nil @commit.save - visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_commit_path(@commit) expect(page).to have_content ".gitlab-ci.yml not found in this commit" end @@ -55,7 +58,7 @@ describe "Commits" do describe "GET /:project/commits/:sha" do before do - visit ci_project_ref_commit_path(@project, @commit.ref, @commit.sha) + visit ci_commit_path(@commit) end it { expect(page).to have_content @commit.sha[0..7] } -- cgit v1.2.1 From ae5d2f5b3132b6ce6fefe5fdef764616bbec3a5d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 13:10:37 +0200 Subject: Fix Ci::Ansi2html spec --- spec/lib/ci/ansi2html_spec.rb | 67 ++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb index 35f115f7f4a..75c023bbc43 100644 --- a/spec/lib/ci/ansi2html_spec.rb +++ b/spec/lib/ci/ansi2html_spec.rb @@ -1,133 +1,134 @@ require 'spec_helper' describe Ci::Ansi2html do + subject { Ci::Ansi2html } it "prints non-ansi as-is" do - Ansi2html::convert("Hello").should == 'Hello' + expect(subject.convert("Hello")).to eq('Hello') end it "strips non-color-changing controll sequences" do - Ansi2html::convert("Hello \e[2Kworld").should == 'Hello world' + expect(subject.convert("Hello \e[2Kworld")).to eq('Hello world') end it "prints simply red" do - Ansi2html::convert("\e[31mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[31mHello\e[0m")).to eq('Hello') end it "prints simply red without trailing reset" do - Ansi2html::convert("\e[31mHello").should == 'Hello' + expect(subject.convert("\e[31mHello")).to eq('Hello') end it "prints simply yellow" do - Ansi2html::convert("\e[33mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[33mHello\e[0m")).to eq('Hello') end it "prints default on blue" do - Ansi2html::convert("\e[39;44mHello").should == 'Hello' + expect(subject.convert("\e[39;44mHello")).to eq('Hello') end it "prints red on blue" do - Ansi2html::convert("\e[31;44mHello").should == 'Hello' + expect(subject.convert("\e[31;44mHello")).to eq('Hello') end it "resets colors after red on blue" do - Ansi2html::convert("\e[31;44mHello\e[0m world").should == 'Hello world' + expect(subject.convert("\e[31;44mHello\e[0m world")).to eq('Hello world') end it "performs color change from red/blue to yellow/blue" do - Ansi2html::convert("\e[31;44mHello \e[33mworld").should == 'Hello world' + expect(subject.convert("\e[31;44mHello \e[33mworld")).to eq('Hello world') end it "performs color change from red/blue to yellow/green" do - Ansi2html::convert("\e[31;44mHello \e[33;42mworld").should == 'Hello world' + expect(subject.convert("\e[31;44mHello \e[33;42mworld")).to eq('Hello world') end it "performs color change from red/blue to reset to yellow/green" do - Ansi2html::convert("\e[31;44mHello\e[0m \e[33;42mworld").should == 'Hello world' + expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('Hello world') end it "ignores unsupported codes" do - Ansi2html::convert("\e[51mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[51mHello\e[0m")).to eq('Hello') end it "prints light red" do - Ansi2html::convert("\e[91mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[91mHello\e[0m")).to eq('Hello') end it "prints default on light red" do - Ansi2html::convert("\e[101mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[101mHello\e[0m")).to eq('Hello') end it "performs color change from red/blue to default/blue" do - Ansi2html::convert("\e[31;44mHello \e[39mworld").should == 'Hello world' + expect(subject.convert("\e[31;44mHello \e[39mworld")).to eq('Hello world') end it "performs color change from light red/blue to default/blue" do - Ansi2html::convert("\e[91;44mHello \e[39mworld").should == 'Hello world' + expect(subject.convert("\e[91;44mHello \e[39mworld")).to eq('Hello world') end it "prints bold text" do - Ansi2html::convert("\e[1mHello").should == 'Hello' + expect(subject.convert("\e[1mHello")).to eq('Hello') end it "resets bold text" do - Ansi2html::convert("\e[1mHello\e[21m world").should == 'Hello world' - Ansi2html::convert("\e[1mHello\e[22m world").should == 'Hello world' + expect(subject.convert("\e[1mHello\e[21m world")).to eq('Hello world') + expect(subject.convert("\e[1mHello\e[22m world")).to eq('Hello world') end it "prints italic text" do - Ansi2html::convert("\e[3mHello").should == 'Hello' + expect(subject.convert("\e[3mHello")).to eq('Hello') end it "resets italic text" do - Ansi2html::convert("\e[3mHello\e[23m world").should == 'Hello world' + expect(subject.convert("\e[3mHello\e[23m world")).to eq('Hello world') end it "prints underlined text" do - Ansi2html::convert("\e[4mHello").should == 'Hello' + expect(subject.convert("\e[4mHello")).to eq('Hello') end it "resets underlined text" do - Ansi2html::convert("\e[4mHello\e[24m world").should == 'Hello world' + expect(subject.convert("\e[4mHello\e[24m world")).to eq('Hello world') end it "prints concealed text" do - Ansi2html::convert("\e[8mHello").should == 'Hello' + expect(subject.convert("\e[8mHello")).to eq('Hello') end it "resets concealed text" do - Ansi2html::convert("\e[8mHello\e[28m world").should == 'Hello world' + expect(subject.convert("\e[8mHello\e[28m world")).to eq('Hello world') end it "prints crossed-out text" do - Ansi2html::convert("\e[9mHello").should == 'Hello' + expect(subject.convert("\e[9mHello")).to eq('Hello') end it "resets crossed-out text" do - Ansi2html::convert("\e[9mHello\e[29m world").should == 'Hello world' + expect(subject.convert("\e[9mHello\e[29m world")).to eq('Hello world') end it "can print 256 xterm fg colors" do - Ansi2html::convert("\e[38;5;16mHello").should == 'Hello' + expect(subject.convert("\e[38;5;16mHello")).to eq('Hello') end it "can print 256 xterm fg colors on normal magenta background" do - Ansi2html::convert("\e[38;5;16;45mHello").should == 'Hello' + expect(subject.convert("\e[38;5;16;45mHello")).to eq('Hello') end it "can print 256 xterm bg colors" do - Ansi2html::convert("\e[48;5;240mHello").should == 'Hello' + expect(subject.convert("\e[48;5;240mHello")).to eq('Hello') end it "can print 256 xterm bg colors on normal magenta foreground" do - Ansi2html::convert("\e[48;5;16;35mHello").should == 'Hello' + expect(subject.convert("\e[48;5;16;35mHello")).to eq('Hello') end it "prints bold colored text vividly" do - Ansi2html::convert("\e[1;31mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[1;31mHello\e[0m")).to eq('Hello') end it "prints bold light colored text correctly" do - Ansi2html::convert("\e[1;91mHello\e[0m").should == 'Hello' + expect(subject.convert("\e[1;91mHello\e[0m")).to eq('Hello') end end -- cgit v1.2.1 From 187face620cfa0d5617a1c49e28dfbb20134fe41 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 13:15:18 +0200 Subject: CLeanup CI helpers since we dont use oauth any more --- app/helpers/ci/user_sessions_helper.rb | 32 ------------- spec/helpers/ci/user_helper_spec.rb | 49 -------------------- spec/helpers/ci/user_sessions_helper_spec.rb | 69 ---------------------------- 3 files changed, 150 deletions(-) delete mode 100644 app/helpers/ci/user_sessions_helper.rb delete mode 100644 spec/helpers/ci/user_helper_spec.rb delete mode 100644 spec/helpers/ci/user_sessions_helper_spec.rb diff --git a/app/helpers/ci/user_sessions_helper.rb b/app/helpers/ci/user_sessions_helper.rb deleted file mode 100644 index 0296a74395c..00000000000 --- a/app/helpers/ci/user_sessions_helper.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Ci - module UserSessionsHelper - def generate_oauth_salt - SecureRandom.hex(16) - end - - def generate_oauth_hmac(salt, return_to) - return unless return_to - digest = OpenSSL::Digest.new('sha256') - key = Gitlab::Application.secrets.db_key_base + salt - OpenSSL::HMAC.hexdigest(digest, key, return_to) - end - - def generate_oauth_state(return_to) - return unless return_to - salt = generate_oauth_salt - hmac = generate_oauth_hmac(salt, return_to) - "#{salt}:#{hmac}:#{return_to}" - end - - def get_ouath_state_return_to(state) - state.split(':', 3)[2] if state - end - - def is_oauth_state_valid?(state) - return true unless state - salt, hmac, return_to = state.split(':', 3) - return false unless return_to - hmac == generate_oauth_hmac(salt, return_to) - end - end -end diff --git a/spec/helpers/ci/user_helper_spec.rb b/spec/helpers/ci/user_helper_spec.rb deleted file mode 100644 index f95bfb355ed..00000000000 --- a/spec/helpers/ci/user_helper_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe Ci::UserHelper do - describe :user_avatar_url do - let (:user) { User.new({'avatar_url' => avatar_url}) } - - context 'no avatar' do - let (:avatar_url) { nil } - - it 'should return a generic avatar' do - user_avatar_url(user).should == 'ci/no_avatar.png' - end - end - - context 'plain gravatar' do - let (:base_url) { 'http://www.gravatar.com/avatar/abcdefgh' } - let (:avatar_url) { "#{base_url}?s=40&d=mm" } - - it 'should return gravatar with default size' do - user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" - end - - it 'should return gravatar with custom size' do - user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" - end - end - - context 'secure gravatar' do - let (:base_url) { 'https://secure.gravatar.com/avatar/abcdefgh' } - let (:avatar_url) { "#{base_url}?s=40&d=mm" } - - it 'should return gravatar with default size' do - user_avatar_url(user).should == "#{base_url}?s=40&d=identicon" - end - - it 'should return gravatar with custom size' do - user_avatar_url(user, 120).should == "#{base_url}?s=120&d=identicon" - end - end - - context 'custom avatar' do - let (:avatar_url) { 'http://example.local/avatar.png' } - - it 'should return custom avatar' do - user_avatar_url(user).should == avatar_url - end - end - end -end diff --git a/spec/helpers/ci/user_sessions_helper_spec.rb b/spec/helpers/ci/user_sessions_helper_spec.rb deleted file mode 100644 index 5f654866d99..00000000000 --- a/spec/helpers/ci/user_sessions_helper_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'spec_helper' - -describe Ci::UserSessionsHelper do - describe :generate_oauth_hmac do - let (:salt) { 'a' } - let (:salt2) { 'b' } - let (:return_to) { 'b' } - - it 'should return null if return_to is also null' do - generate_oauth_hmac(salt, nil).should be_nil - end - - it 'should return not null if return_to is also not null' do - generate_oauth_hmac(salt, return_to).should_not be_nil - end - - it 'should return different hmacs for different salts' do - secret1 = generate_oauth_hmac(salt, return_to) - secret2 = generate_oauth_hmac(salt2, return_to) - secret1.should_not eq(secret2) - end - end - - describe :generate_oauth_state do - let (:return_to) { 'b' } - - it 'should return null if return_to is also null' do - generate_oauth_state(nil).should be_nil - end - - it 'should return two different states for same return_to' do - state1 = generate_oauth_state(return_to) - state2 = generate_oauth_state(return_to) - state1.should_not eq(state2) - end - end - - describe :get_ouath_state_return_to do - let (:return_to) { 'a' } - let (:state) { generate_oauth_state(return_to) } - - it 'should return return_to' do - get_ouath_state_return_to(state).should eq(return_to) - end - end - - describe :is_oauth_state_valid? do - let (:return_to) { 'a' } - let (:state) { generate_oauth_state(return_to) } - let (:forged) { "forged#{state}" } - let (:invalid) { 'aa' } - let (:invalid2) { 'aa:bb' } - let (:invalid3) { 'aa:bb:' } - - it 'should validate oauth state' do - is_oauth_state_valid?(state).should be_true - end - - it 'should not validate forged state' do - is_oauth_state_valid?(forged).should be_false - end - - it 'should not validate invalid state' do - is_oauth_state_valid?(invalid).should be_false - is_oauth_state_valid?(invalid2).should be_false - is_oauth_state_valid?(invalid3).should be_false - end - end -end -- cgit v1.2.1 From 2ed2ef921026cbde5dddda89177bfa50e2993ecd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 13:38:37 +0200 Subject: Remove network from CI --- app/controllers/ci/application_controller.rb | 27 ------ app/controllers/ci/projects_controller.rb | 6 +- app/models/ci/network.rb | 122 --------------------------- app/models/ci/project.rb | 6 +- app/services/ci/create_project_service.rb | 6 +- lib/api/services.rb | 4 +- lib/ci/api/projects.rb | 2 +- spec/models/ci/network_spec.rb | 54 ------------ 8 files changed, 12 insertions(+), 215 deletions(-) delete mode 100644 app/models/ci/network.rb delete mode 100644 spec/models/ci/network_spec.rb diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 9a32efaee7d..a5868da377f 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -4,14 +4,8 @@ module Ci "app/helpers/ci" end - include Ci::UserSessionsHelper - - rescue_from Ci::Network::UnauthorizedError, with: :invalid_token - before_filter :default_headers helper_method :gl_project - protect_from_forgery - private def authenticate_public_page! @@ -75,27 +69,6 @@ module Ci } end - def check_config - redirect_to oauth2_ci_help_path unless valid_config? - end - - def valid_config? - server = GitlabCi.config.gitlab_server - - if server.blank? || server.url.blank? || server.app_id.blank? || server.app_secret.blank? - false - else - true - end - rescue Settingslogic::MissingSetting, NoMethodError - false - end - - def invalid_token - reset_session - redirect_to ci_root_path - end - def gl_project ::Project.find(@project.gitlab_id) end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index c30c5d9590b..9074972e94a 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -27,7 +27,7 @@ module Ci @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date @total_count = @gl_projects.size - + @gl_projects = @gl_projects.where.not(id: @projects.map(&:gitlab_id)) respond_to do |format| @@ -35,8 +35,6 @@ module Ci pager_json("ci/projects/gitlab", @total_count) end end - rescue Ci::Network::UnauthorizedError - raise rescue @error = 'Failed to fetch GitLab projects' end @@ -82,8 +80,8 @@ module Ci end def destroy + project.gl_project.gitlab_ci_service.update_attributes(active: false) project.destroy - Ci::Network.new.disable_ci(project.gitlab_id, current_user.authenticate_options) Ci::EventService.new.remove_project(current_user, project) diff --git a/app/models/ci/network.rb b/app/models/ci/network.rb deleted file mode 100644 index 1a54213f4f1..00000000000 --- a/app/models/ci/network.rb +++ /dev/null @@ -1,122 +0,0 @@ -module Ci - class Network - class UnauthorizedError < StandardError; end - - include HTTParty - - API_PREFIX = '/api/v3/' - - def authenticate(api_opts) - opts = { - query: api_opts - } - - endpoint = File.join(url, API_PREFIX, 'user') - response = self.class.get(endpoint, default_opts.merge(opts)) - - build_response(response) - end - - def projects(api_opts, scope = :owned) - # Dont load archived projects - api_opts.merge!(archived: false) - - opts = { - query: api_opts - } - - query = if scope == :owned - 'projects/owned.json' - else - 'projects.json' - end - - endpoint = File.join(url, API_PREFIX, query) - response = self.class.get(endpoint, default_opts.merge(opts)) - - build_response(response) - end - - def project(api_opts, project_id) - opts = { - query: api_opts - } - - query = "projects/#{project_id}.json" - - endpoint = File.join(url, API_PREFIX, query) - response = self.class.get(endpoint, default_opts.merge(opts)) - - build_response(response) - end - - def project_hooks(api_opts, project_id) - opts = { - query: api_opts - } - - query = "projects/#{project_id}/hooks.json" - - endpoint = File.join(url, API_PREFIX, query) - response = self.class.get(endpoint, default_opts.merge(opts)) - - build_response(response) - end - - def enable_ci(project_id, data, api_opts) - opts = { - body: data.to_json, - query: api_opts - } - - query = "projects/#{project_id}/services/gitlab-ci.json" - endpoint = File.join(url, API_PREFIX, query) - response = self.class.put(endpoint, default_opts.merge(opts)) - - case response.code - when 200 - true - when 401 - raise UnauthorizedError - else - nil - end - end - - def disable_ci(project_id, api_opts) - opts = { - query: api_opts - } - - query = "projects/#{project_id}/services/gitlab-ci.json" - - endpoint = File.join(url, API_PREFIX, query) - response = self.class.delete(endpoint, default_opts.merge(opts)) - - build_response(response) - end - - private - - def url - Gitlab.config.gitlab.url - end - - def default_opts - { - headers: { "Content-Type" => "application/json" }, - } - end - - def build_response(response) - case response.code - when 200 - response.parsed_response - when 401 - raise UnauthorizedError - else - nil - end - end - end -end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index c9e5707f218..2cf1783616f 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -31,7 +31,7 @@ module Ci include Ci::ProjectStatus - belongs_to :gl_project, class_name: 'Project', foreign_key: :gitlab_id + belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id has_many :commits, ->() { order(:committed_at) }, dependent: :destroy, class_name: 'Ci::Commit' has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' @@ -92,11 +92,13 @@ module Ci project end + # TODO: remove def from_gitlab(user, scope = :owned, options) opts = user.authenticate_options opts.merge! options - projects = Ci::Network.new.projects(opts.compact, scope) + raise 'Implement me of fix' + #projects = Ci::Network.new.projects(opts.compact, scope) if projects projects.map { |pr| OpenStruct.new(pr) } diff --git a/app/services/ci/create_project_service.rb b/app/services/ci/create_project_service.rb index 81bbc29bb4f..0419612d521 100644 --- a/app/services/ci/create_project_service.rb +++ b/app/services/ci/create_project_service.rb @@ -13,9 +13,9 @@ module Ci project_url: project_route.gsub(":project_id", @project.id.to_s), } - unless Ci::Network.new.enable_ci(@project.gitlab_id, data, {private_token: current_user.private_token}) - raise ActiveRecord::Rollback - end + gl_project = ::Project.find(@project.gitlab_id) + gl_project.build_missing_services + gl_project.gitlab_ci_service.update_attributes(data.merge(active: true)) end if forked_project diff --git a/lib/api/services.rb b/lib/api/services.rb index 73645cedea4..6d2322bb464 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -20,7 +20,7 @@ module API end required_attributes! validators.map(&:attributes).flatten.uniq - attrs = attributes_for_keys service_attributes + attrs = attributes_for_keys service_attributes if project_service.update_attributes(attrs.merge(active: true)) true @@ -41,7 +41,7 @@ module API attrs = service_attributes.inject({}) do |hash, key| hash.merge!(key => nil) end - + if project_service.update_attributes(attrs.merge(active: false)) true else diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index bdcacecf6ab..556de3bff9f 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -18,7 +18,7 @@ module Ci project = Ci::Project.find(params[:project_id]) unauthorized! unless current_user.can_manage_project?(project.gitlab_id) - + web_hook = project.web_hooks.new({ url: params[:web_hook] }) if web_hook.save diff --git a/spec/models/ci/network_spec.rb b/spec/models/ci/network_spec.rb deleted file mode 100644 index 551eb08ab33..00000000000 --- a/spec/models/ci/network_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'spec_helper' - -describe Network do - let(:network) { Network.new } - - describe :enable_ci do - subject { network.enable_ci '', '', '' } - - context 'on success' do - before do - response = double - allow(response).to receive(:code) { 200 } - allow(network.class).to receive(:put) { response } - end - - it { is_expected.to be_truthy } - end - - context 'on failure' do - before do - response = double - allow(response).to receive(:code) { 404 } - allow(network.class).to receive(:put) { response } - end - - it { is_expected.to be_nil } - end - end - - describe :disable_ci do - let(:response) { double } - subject { network.disable_ci '', '' } - - context 'on success' do - let(:parsed_response) { 'parsed' } - before do - allow(response).to receive(:code) { 200 } - allow(response).to receive(:parsed_response) { parsed_response } - allow(network.class).to receive(:delete) { response } - end - - it { is_expected.to equal(parsed_response) } - end - - context 'on failure' do - before do - allow(response).to receive(:code) { 404 } - allow(network.class).to receive(:delete) { response } - end - - it { is_expected.to be_nil } - end - end -end -- cgit v1.2.1 From d1914c1e1f4452feae3a29520b6852f7b8009ced Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 11 Sep 2015 14:40:57 +0300 Subject: admin fix --- app/views/ci/admin/builds/_build.html.haml | 2 +- app/views/ci/admin/builds/index.html.haml | 3 ++- app/views/ci/admin/projects/index.html.haml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml index 1766ca39760..47f8df8f98e 100644 --- a/app/views/ci/admin/builds/_build.html.haml +++ b/app/views/ci/admin/builds/_build.html.haml @@ -1,7 +1,7 @@ - if build.commit && build.project %tr.build.alert{class: build_status_alert_class(build)} %td.build-link - = link_to ci_build_url(build) do + = link_to ci_project_build_url(build.project, build) do %strong #{build.id} %td.status diff --git a/app/views/ci/admin/builds/index.html.haml b/app/views/ci/admin/builds/index.html.haml index ab4ced54327..d23119162cc 100644 --- a/app/views/ci/admin/builds/index.html.haml +++ b/app/views/ci/admin/builds/index.html.haml @@ -22,6 +22,7 @@ %th Duration %th Finished at - = render @builds + - @builds.each do |build| + = render "ci/admin/builds/build", build: build = paginate @builds diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml index d6c0243880f..dc7b041473b 100644 --- a/app/views/ci/admin/projects/index.html.haml +++ b/app/views/ci/admin/projects/index.html.haml @@ -9,7 +9,7 @@ %th - @projects.each do |project| - = render "ci/admin/projects/project", project: @projects + = render "ci/admin/projects/project", project: project = paginate @projects -- cgit v1.2.1 From ae83051ef9a3fad9dda89eaf537f07e1ba323f88 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 13:43:37 +0200 Subject: Fix CI admin projects page --- app/views/ci/admin/projects/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml index d6c0243880f..dc7b041473b 100644 --- a/app/views/ci/admin/projects/index.html.haml +++ b/app/views/ci/admin/projects/index.html.haml @@ -9,7 +9,7 @@ %th - @projects.each do |project| - = render "ci/admin/projects/project", project: @projects + = render "ci/admin/projects/project", project: project = paginate @projects -- cgit v1.2.1 From ec8e2463db5058ae15ac56ed779ff68da24afdee Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 14:38:34 +0200 Subject: Remove db/ci --- db/ci/migrate/20121004140911_create_projects.rb | 14 -- db/ci/migrate/20121004165038_create_builds.rb | 15 -- .../migrate/20121101091638_devise_create_users.rb | 46 ----- .../migrate/20121101121639_add_token_to_project.rb | 5 - .../20121106143042_add_ref_functionality.rb | 10 - .../20121108160657_add_gitlab_url_to_project.rb | 5 - .../20121108174237_add_started_at_to_build.rb | 5 - .../20121115094430_increate_trace_colunm_limit.rb | 8 - .../20121115132252_add_tmp_file_to_build.rb | 5 - .../20121116144312_add_before_sha_to_build.rb | 5 - .../20121224092350_add_schedule_to_projects.rb | 6 - .../20130114153451_change_schedule_invertal.rb | 25 --- .../20130129121754_add_public_flag_to_project.rb | 5 - .../20130531112551_add_data_field_to_build.rb | 5 - ...0130531122131_remove_path_field_from_project.rb | 8 - db/ci/migrate/20130531125905_create_runners.rb | 10 - .../20130531133603_add_runner_id_to_build.rb | 5 - db/ci/migrate/20130603130920_remove_users_table.rb | 5 - .../20130603144030_add_more_fields_to_project.rb | 5 - .../20130603144959_create_runner_projects.rb | 10 - ...30603161449_add_project_gitlab_id_to_project.rb | 5 - ...0130628142321_add_index_project_id_to_builds.rb | 5 - .../20130705171042_add_description_to_runner.rb | 5 - db/ci/migrate/20130710164015_add_db_index.rb | 7 - .../20130816201200_change_push_data_limit.rb | 5 - db/ci/migrate/20130906175737_add_sessions_table.rb | 12 -- ...0131023103430_add_allow_git_fetch_to_project.rb | 5 - ...545_add_email_notification_fields_to_project.rb | 7 - .../20140130121538_rename_project_fields.rb | 5 - db/ci/migrate/20140222210357_create_web_hook.rb | 9 - ...20140506091853_remove_public_key_from_runner.rb | 5 - .../20140823225019_create_commits_from_builds.rb | 22 -- .../20140909142245_add_skip_refs_to_projects.rb | 5 - .../migrate/20141001125939_add_coverage_parser.rb | 5 - .../20141001132129_add_coverage_to_build.rb | 5 - .../20141028162820_add_sha_index_to_build.rb | 6 - .../20141031114419_migrate_build_to_commits.rb | 21 -- .../migrate/20141031141708_add_commit_indicies.rb | 9 - .../20141103135037_add_parallel_to_build.rb | 12 -- .../20141103151359_add_commands_to_build.rb | 5 - .../migrate/20141103162726_add_job_id_to_build.rb | 5 - db/ci/migrate/20141104130024_migrate_jobs.rb | 12 -- db/ci/migrate/20141104153744_add_name_to_job.rb | 5 - .../20141127153745_remove_scripts_from_project.rb | 5 - .../migrate/20141201153755_remove_invalid_build.rb | 5 - db/ci/migrate/20141204133321_create_service.rb | 15 -- db/ci/migrate/20150111062026_add_filter_to_jobs.rb | 6 - ...able_on_migration.acts_as_taggable_on_engine.rb | 31 --- ...ng_unique_indices.acts_as_taggable_on_engine.rb | 20 -- ...ter_cache_to_tags.acts_as_taggable_on_engine.rb | 15 -- ...ng_taggable_index.acts_as_taggable_on_engine.rb | 10 - .../20150204001035_build_missing_services.rb | 21 -- .../migrate/20150226001835_add_job_type_to_job.rb | 6 - .../20150306131416_add_contacted_at_to_runner.rb | 5 - .../migrate/20150306135341_add_active_to_runner.rb | 5 - .../20150310001733_rename_committer_to_pusher.rb | 5 - db/ci/migrate/20150320001810_create_event_table.rb | 16 -- ...150324001123_add_settings_for_shared_runners.rb | 6 - .../20150324001227_migrate_shared_runners.rb | 11 - .../20150330001111_disable_shared_runners.rb | 8 - .../20150415142013_add_deleted_at_to_jobs.rb | 6 - .../20150417000045_cleanup_the_build_model.rb | 9 - .../migrate/20150504010150_migrate_url_to_path.rb | 11 - .../20150504010250_rename_gitlab_url_to_path.rb | 5 - .../20150508011360_add_info_fields_to_runner.rb | 9 - .../migrate/20150528011001_add_fields_to_builds.rb | 6 - .../20150528011012_move_job_name_to_build.rb | 10 - db/ci/migrate/20150529012113_add_tag_to_commits.rb | 5 - .../migrate/20150601043220_add_yaml_to_projects.rb | 9 - .../migrate/20150601043231_migrate_jobs_to_yaml.rb | 97 --------- .../20150602000240_change_default_build_timeout.rb | 9 - db/ci/migrate/20150605002131_create_variables.rb | 11 - .../migrate/20150616001155_add_errors_to_commit.rb | 5 - .../migrate/20150630091815_add_options_to_build.rb | 5 - ...50703125244_add_encrypted_value_to_variables.rb | 7 - db/ci/migrate/20150703125325_encrypt_variables.rb | 10 - .../20150707134456_add_allow_failure_to_builds.rb | 5 - .../20150710113836_add_job_type_to_builds.rb | 5 - ...113851_migrate_deploy_to_job_type_for_builds.rb | 6 - db/ci/migrate/20150721204649_truncate_sessions.rb | 9 - .../20150729145246_create_application_settings.rb | 10 - ...150803142346_rename_job_type_to_stage_builds.rb | 9 - .../20150806091503_add_committed_at_to_commits.rb | 6 - ...06091655_update_committed_at_with_created_at.rb | 5 - db/ci/migrate/20150806102222_create_trigger.rb | 12 -- .../20150806102457_add_trigger_to_builds.rb | 5 - .../20150806105404_create_trigger_request.rb | 9 - ...0819162227_add_commit_id_to_trigger_requests.rb | 8 - db/ci/schema.rb | 226 --------------------- db/ci/seeds.rb | 0 90 files changed, 1103 deletions(-) delete mode 100644 db/ci/migrate/20121004140911_create_projects.rb delete mode 100644 db/ci/migrate/20121004165038_create_builds.rb delete mode 100644 db/ci/migrate/20121101091638_devise_create_users.rb delete mode 100644 db/ci/migrate/20121101121639_add_token_to_project.rb delete mode 100644 db/ci/migrate/20121106143042_add_ref_functionality.rb delete mode 100644 db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb delete mode 100644 db/ci/migrate/20121108174237_add_started_at_to_build.rb delete mode 100644 db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb delete mode 100644 db/ci/migrate/20121115132252_add_tmp_file_to_build.rb delete mode 100644 db/ci/migrate/20121116144312_add_before_sha_to_build.rb delete mode 100644 db/ci/migrate/20121224092350_add_schedule_to_projects.rb delete mode 100644 db/ci/migrate/20130114153451_change_schedule_invertal.rb delete mode 100644 db/ci/migrate/20130129121754_add_public_flag_to_project.rb delete mode 100644 db/ci/migrate/20130531112551_add_data_field_to_build.rb delete mode 100644 db/ci/migrate/20130531122131_remove_path_field_from_project.rb delete mode 100644 db/ci/migrate/20130531125905_create_runners.rb delete mode 100644 db/ci/migrate/20130531133603_add_runner_id_to_build.rb delete mode 100644 db/ci/migrate/20130603130920_remove_users_table.rb delete mode 100644 db/ci/migrate/20130603144030_add_more_fields_to_project.rb delete mode 100644 db/ci/migrate/20130603144959_create_runner_projects.rb delete mode 100644 db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb delete mode 100644 db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb delete mode 100644 db/ci/migrate/20130705171042_add_description_to_runner.rb delete mode 100644 db/ci/migrate/20130710164015_add_db_index.rb delete mode 100644 db/ci/migrate/20130816201200_change_push_data_limit.rb delete mode 100644 db/ci/migrate/20130906175737_add_sessions_table.rb delete mode 100644 db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb delete mode 100644 db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb delete mode 100644 db/ci/migrate/20140130121538_rename_project_fields.rb delete mode 100644 db/ci/migrate/20140222210357_create_web_hook.rb delete mode 100644 db/ci/migrate/20140506091853_remove_public_key_from_runner.rb delete mode 100644 db/ci/migrate/20140823225019_create_commits_from_builds.rb delete mode 100644 db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb delete mode 100644 db/ci/migrate/20141001125939_add_coverage_parser.rb delete mode 100644 db/ci/migrate/20141001132129_add_coverage_to_build.rb delete mode 100644 db/ci/migrate/20141028162820_add_sha_index_to_build.rb delete mode 100644 db/ci/migrate/20141031114419_migrate_build_to_commits.rb delete mode 100644 db/ci/migrate/20141031141708_add_commit_indicies.rb delete mode 100644 db/ci/migrate/20141103135037_add_parallel_to_build.rb delete mode 100644 db/ci/migrate/20141103151359_add_commands_to_build.rb delete mode 100644 db/ci/migrate/20141103162726_add_job_id_to_build.rb delete mode 100644 db/ci/migrate/20141104130024_migrate_jobs.rb delete mode 100644 db/ci/migrate/20141104153744_add_name_to_job.rb delete mode 100644 db/ci/migrate/20141127153745_remove_scripts_from_project.rb delete mode 100644 db/ci/migrate/20141201153755_remove_invalid_build.rb delete mode 100644 db/ci/migrate/20141204133321_create_service.rb delete mode 100644 db/ci/migrate/20150111062026_add_filter_to_jobs.rb delete mode 100644 db/ci/migrate/20150113001832_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb delete mode 100644 db/ci/migrate/20150113001833_add_missing_unique_indices.acts_as_taggable_on_engine.rb delete mode 100644 db/ci/migrate/20150113001834_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb delete mode 100644 db/ci/migrate/20150113001835_add_missing_taggable_index.acts_as_taggable_on_engine.rb delete mode 100644 db/ci/migrate/20150204001035_build_missing_services.rb delete mode 100644 db/ci/migrate/20150226001835_add_job_type_to_job.rb delete mode 100644 db/ci/migrate/20150306131416_add_contacted_at_to_runner.rb delete mode 100644 db/ci/migrate/20150306135341_add_active_to_runner.rb delete mode 100644 db/ci/migrate/20150310001733_rename_committer_to_pusher.rb delete mode 100644 db/ci/migrate/20150320001810_create_event_table.rb delete mode 100644 db/ci/migrate/20150324001123_add_settings_for_shared_runners.rb delete mode 100644 db/ci/migrate/20150324001227_migrate_shared_runners.rb delete mode 100644 db/ci/migrate/20150330001111_disable_shared_runners.rb delete mode 100644 db/ci/migrate/20150415142013_add_deleted_at_to_jobs.rb delete mode 100644 db/ci/migrate/20150417000045_cleanup_the_build_model.rb delete mode 100644 db/ci/migrate/20150504010150_migrate_url_to_path.rb delete mode 100644 db/ci/migrate/20150504010250_rename_gitlab_url_to_path.rb delete mode 100644 db/ci/migrate/20150508011360_add_info_fields_to_runner.rb delete mode 100644 db/ci/migrate/20150528011001_add_fields_to_builds.rb delete mode 100644 db/ci/migrate/20150528011012_move_job_name_to_build.rb delete mode 100644 db/ci/migrate/20150529012113_add_tag_to_commits.rb delete mode 100644 db/ci/migrate/20150601043220_add_yaml_to_projects.rb delete mode 100644 db/ci/migrate/20150601043231_migrate_jobs_to_yaml.rb delete mode 100644 db/ci/migrate/20150602000240_change_default_build_timeout.rb delete mode 100644 db/ci/migrate/20150605002131_create_variables.rb delete mode 100644 db/ci/migrate/20150616001155_add_errors_to_commit.rb delete mode 100644 db/ci/migrate/20150630091815_add_options_to_build.rb delete mode 100644 db/ci/migrate/20150703125244_add_encrypted_value_to_variables.rb delete mode 100644 db/ci/migrate/20150703125325_encrypt_variables.rb delete mode 100644 db/ci/migrate/20150707134456_add_allow_failure_to_builds.rb delete mode 100644 db/ci/migrate/20150710113836_add_job_type_to_builds.rb delete mode 100644 db/ci/migrate/20150710113851_migrate_deploy_to_job_type_for_builds.rb delete mode 100644 db/ci/migrate/20150721204649_truncate_sessions.rb delete mode 100644 db/ci/migrate/20150729145246_create_application_settings.rb delete mode 100644 db/ci/migrate/20150803142346_rename_job_type_to_stage_builds.rb delete mode 100644 db/ci/migrate/20150806091503_add_committed_at_to_commits.rb delete mode 100644 db/ci/migrate/20150806091655_update_committed_at_with_created_at.rb delete mode 100644 db/ci/migrate/20150806102222_create_trigger.rb delete mode 100644 db/ci/migrate/20150806102457_add_trigger_to_builds.rb delete mode 100644 db/ci/migrate/20150806105404_create_trigger_request.rb delete mode 100644 db/ci/migrate/20150819162227_add_commit_id_to_trigger_requests.rb delete mode 100644 db/ci/schema.rb delete mode 100644 db/ci/seeds.rb diff --git a/db/ci/migrate/20121004140911_create_projects.rb b/db/ci/migrate/20121004140911_create_projects.rb deleted file mode 100644 index a9fee3aa6c8..00000000000 --- a/db/ci/migrate/20121004140911_create_projects.rb +++ /dev/null @@ -1,14 +0,0 @@ -class CreateProjects < ActiveRecord::Migration - def up - create_table :projects do |t| - t.string :name, null: false - t.string :path, null: false - t.integer :timeout, null: false, default: 1800 - t.text :scripts, null: false - t.timestamps - end - end - - def down - end -end diff --git a/db/ci/migrate/20121004165038_create_builds.rb b/db/ci/migrate/20121004165038_create_builds.rb deleted file mode 100644 index 547803489fb..00000000000 --- a/db/ci/migrate/20121004165038_create_builds.rb +++ /dev/null @@ -1,15 +0,0 @@ -class CreateBuilds < ActiveRecord::Migration - def up - create_table :builds do |t| - t.integer :project_id - t.string :commit_ref - t.string :status - t.datetime :finished_at - t.text :trace - t.timestamps - end - end - - def down - end -end diff --git a/db/ci/migrate/20121101091638_devise_create_users.rb b/db/ci/migrate/20121101091638_devise_create_users.rb deleted file mode 100644 index 2099d998fa4..00000000000 --- a/db/ci/migrate/20121101091638_devise_create_users.rb +++ /dev/null @@ -1,46 +0,0 @@ -class DeviseCreateUsers < ActiveRecord::Migration - def change - create_table(:users) do |t| - ## Database authenticatable - t.string :email, :null => false, :default => "" - t.string :encrypted_password, :null => false, :default => "" - - ## Recoverable - t.string :reset_password_token - t.datetime :reset_password_sent_at - - ## Rememberable - t.datetime :remember_created_at - - ## Trackable - t.integer :sign_in_count, :default => 0 - t.datetime :current_sign_in_at - t.datetime :last_sign_in_at - t.string :current_sign_in_ip - t.string :last_sign_in_ip - - ## Confirmable - # t.string :confirmation_token - # t.datetime :confirmed_at - # t.datetime :confirmation_sent_at - # t.string :unconfirmed_email # Only if using reconfirmable - - ## Lockable - # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts - # t.string :unlock_token # Only if unlock strategy is :email or :both - # t.datetime :locked_at - - ## Token authenticatable - # t.string :authentication_token - - - t.timestamps - end - - add_index :users, :email, :unique => true - add_index :users, :reset_password_token, :unique => true - # add_index :users, :confirmation_token, :unique => true - # add_index :users, :unlock_token, :unique => true - # add_index :users, :authentication_token, :unique => true - end -end diff --git a/db/ci/migrate/20121101121639_add_token_to_project.rb b/db/ci/migrate/20121101121639_add_token_to_project.rb deleted file mode 100644 index bb66677b6b1..00000000000 --- a/db/ci/migrate/20121101121639_add_token_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddTokenToProject < ActiveRecord::Migration - def change - add_column :projects, :token, :string, null: true - end -end diff --git a/db/ci/migrate/20121106143042_add_ref_functionality.rb b/db/ci/migrate/20121106143042_add_ref_functionality.rb deleted file mode 100644 index 0c26571e305..00000000000 --- a/db/ci/migrate/20121106143042_add_ref_functionality.rb +++ /dev/null @@ -1,10 +0,0 @@ -class AddRefFunctionality < ActiveRecord::Migration - def change - rename_column :builds, :commit_ref, :ref - add_column :builds, :sha, :string - add_column :projects, :default_ref, :string - end - - def down - end -end diff --git a/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb b/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb deleted file mode 100644 index 8a4e8fd666f..00000000000 --- a/db/ci/migrate/20121108160657_add_gitlab_url_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddGitlabUrlToProject < ActiveRecord::Migration - def change - add_column :projects, :gitlab_url, :string, null: true - end -end diff --git a/db/ci/migrate/20121108174237_add_started_at_to_build.rb b/db/ci/migrate/20121108174237_add_started_at_to_build.rb deleted file mode 100644 index b4d65c75004..00000000000 --- a/db/ci/migrate/20121108174237_add_started_at_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddStartedAtToBuild < ActiveRecord::Migration - def change - add_column :builds, :started_at, :datetime, null: true - end -end diff --git a/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb b/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb deleted file mode 100644 index 5853f440f59..00000000000 --- a/db/ci/migrate/20121115094430_increate_trace_colunm_limit.rb +++ /dev/null @@ -1,8 +0,0 @@ -class IncreateTraceColunmLimit < ActiveRecord::Migration - def up - change_column :builds, :trace, :text, :limit => 1073741823 - end - - def down - end -end diff --git a/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb b/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb deleted file mode 100644 index a9a4e36b5ba..00000000000 --- a/db/ci/migrate/20121115132252_add_tmp_file_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddTmpFileToBuild < ActiveRecord::Migration - def change - add_column :builds, :tmp_file, :string - end -end diff --git a/db/ci/migrate/20121116144312_add_before_sha_to_build.rb b/db/ci/migrate/20121116144312_add_before_sha_to_build.rb deleted file mode 100644 index 7b8cfd93caa..00000000000 --- a/db/ci/migrate/20121116144312_add_before_sha_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddBeforeShaToBuild < ActiveRecord::Migration - def change - add_column :builds, :before_sha, :string, null: true - end -end diff --git a/db/ci/migrate/20121224092350_add_schedule_to_projects.rb b/db/ci/migrate/20121224092350_add_schedule_to_projects.rb deleted file mode 100644 index fb3155f1159..00000000000 --- a/db/ci/migrate/20121224092350_add_schedule_to_projects.rb +++ /dev/null @@ -1,6 +0,0 @@ -class AddScheduleToProjects < ActiveRecord::Migration - def change - add_column :projects, :always_build, :boolean, default: false, null: false - add_column :projects, :polling_interval, :string, null: true - end -end diff --git a/db/ci/migrate/20130114153451_change_schedule_invertal.rb b/db/ci/migrate/20130114153451_change_schedule_invertal.rb deleted file mode 100644 index accf3eef473..00000000000 --- a/db/ci/migrate/20130114153451_change_schedule_invertal.rb +++ /dev/null @@ -1,25 +0,0 @@ -class ChangeScheduleInvertal < ActiveRecord::Migration - def up - if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' - connection.execute(%q{ - ALTER TABLE projects - ALTER COLUMN polling_interval - TYPE integer USING CAST(polling_interval AS integer) - }) - else - change_column :projects, :polling_interval, :integer, null: true - end - end - - def down - if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' - connection.execute(%q{ - ALTER TABLE projects - ALTER COLUMN polling_interval - TYPE integer USING CAST(polling_interval AS varchar) - }) - else - change_column :projects, :polling_interval, :string, null: true - end - end -end diff --git a/db/ci/migrate/20130129121754_add_public_flag_to_project.rb b/db/ci/migrate/20130129121754_add_public_flag_to_project.rb deleted file mode 100644 index 2bfe52f0df4..00000000000 --- a/db/ci/migrate/20130129121754_add_public_flag_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddPublicFlagToProject < ActiveRecord::Migration - def change - add_column :projects, :public, :boolean, null: false, default: false - end -end diff --git a/db/ci/migrate/20130531112551_add_data_field_to_build.rb b/db/ci/migrate/20130531112551_add_data_field_to_build.rb deleted file mode 100644 index ff897bce448..00000000000 --- a/db/ci/migrate/20130531112551_add_data_field_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddDataFieldToBuild < ActiveRecord::Migration - def change - add_column :builds, :push_data, :text - end -end diff --git a/db/ci/migrate/20130531122131_remove_path_field_from_project.rb b/db/ci/migrate/20130531122131_remove_path_field_from_project.rb deleted file mode 100644 index 684c16470a4..00000000000 --- a/db/ci/migrate/20130531122131_remove_path_field_from_project.rb +++ /dev/null @@ -1,8 +0,0 @@ -class RemovePathFieldFromProject < ActiveRecord::Migration - def up - remove_column :projects, :path - end - - def down - end -end diff --git a/db/ci/migrate/20130531125905_create_runners.rb b/db/ci/migrate/20130531125905_create_runners.rb deleted file mode 100644 index 2619394f51b..00000000000 --- a/db/ci/migrate/20130531125905_create_runners.rb +++ /dev/null @@ -1,10 +0,0 @@ -class CreateRunners < ActiveRecord::Migration - def change - create_table :runners do |t| - t.string :token - t.text :public_key - - t.timestamps - end - end -end diff --git a/db/ci/migrate/20130531133603_add_runner_id_to_build.rb b/db/ci/migrate/20130531133603_add_runner_id_to_build.rb deleted file mode 100644 index bccc0970835..00000000000 --- a/db/ci/migrate/20130531133603_add_runner_id_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddRunnerIdToBuild < ActiveRecord::Migration - def change - add_column :builds, :runner_id, :integer - end -end diff --git a/db/ci/migrate/20130603130920_remove_users_table.rb b/db/ci/migrate/20130603130920_remove_users_table.rb deleted file mode 100644 index 6948ef265ef..00000000000 --- a/db/ci/migrate/20130603130920_remove_users_table.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RemoveUsersTable < ActiveRecord::Migration - def up - drop_table :users - end -end diff --git a/db/ci/migrate/20130603144030_add_more_fields_to_project.rb b/db/ci/migrate/20130603144030_add_more_fields_to_project.rb deleted file mode 100644 index 0897682285a..00000000000 --- a/db/ci/migrate/20130603144030_add_more_fields_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddMoreFieldsToProject < ActiveRecord::Migration - def change - add_column :projects, :ssh_url_to_repo, :string - end -end diff --git a/db/ci/migrate/20130603144959_create_runner_projects.rb b/db/ci/migrate/20130603144959_create_runner_projects.rb deleted file mode 100644 index c65c8a51bcf..00000000000 --- a/db/ci/migrate/20130603144959_create_runner_projects.rb +++ /dev/null @@ -1,10 +0,0 @@ -class CreateRunnerProjects < ActiveRecord::Migration - def change - create_table :runner_projects do |t| - t.integer :runner_id, null: false - t.integer :project_id, null: false - - t.timestamps - end - end -end diff --git a/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb b/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb deleted file mode 100644 index 3efdbb7af1c..00000000000 --- a/db/ci/migrate/20130603161449_add_project_gitlab_id_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddProjectGitlabIdToProject < ActiveRecord::Migration - def change - add_column :projects, :gitlab_id, :integer - end -end diff --git a/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb b/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb deleted file mode 100644 index 5f968b06b5d..00000000000 --- a/db/ci/migrate/20130628142321_add_index_project_id_to_builds.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIndexProjectIdToBuilds < ActiveRecord::Migration - def change - add_index :builds, :project_id - end -end diff --git a/db/ci/migrate/20130705171042_add_description_to_runner.rb b/db/ci/migrate/20130705171042_add_description_to_runner.rb deleted file mode 100644 index 1e04e98d109..00000000000 --- a/db/ci/migrate/20130705171042_add_description_to_runner.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddDescriptionToRunner < ActiveRecord::Migration - def change - add_column :runners, :description, :string - end -end diff --git a/db/ci/migrate/20130710164015_add_db_index.rb b/db/ci/migrate/20130710164015_add_db_index.rb deleted file mode 100644 index 4907fae888b..00000000000 --- a/db/ci/migrate/20130710164015_add_db_index.rb +++ /dev/null @@ -1,7 +0,0 @@ -class AddDbIndex < ActiveRecord::Migration - def change - add_index :builds, :runner_id - add_index :runner_projects, :runner_id - add_index :runner_projects, :project_id - end -end diff --git a/db/ci/migrate/20130816201200_change_push_data_limit.rb b/db/ci/migrate/20130816201200_change_push_data_limit.rb deleted file mode 100644 index 29bd45c2cf9..00000000000 --- a/db/ci/migrate/20130816201200_change_push_data_limit.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ChangePushDataLimit < ActiveRecord::Migration - def change - change_column :builds, :push_data, :text, :limit => 16777215 - end -end diff --git a/db/ci/migrate/20130906175737_add_sessions_table.rb b/db/ci/migrate/20130906175737_add_sessions_table.rb deleted file mode 100644 index 4c879564a58..00000000000 --- a/db/ci/migrate/20130906175737_add_sessions_table.rb +++ /dev/null @@ -1,12 +0,0 @@ -class AddSessionsTable < ActiveRecord::Migration - def change - create_table :sessions do |t| - t.string :session_id, :null => false - t.text :data - t.timestamps - end - - add_index :sessions, :session_id - add_index :sessions, :updated_at - end -end diff --git a/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb b/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb deleted file mode 100644 index 900ea913728..00000000000 --- a/db/ci/migrate/20131023103430_add_allow_git_fetch_to_project.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddAllowGitFetchToProject < ActiveRecord::Migration - def change - add_column :projects, :allow_git_fetch, :boolean, default: true, null: false - end -end diff --git a/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb b/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb deleted file mode 100644 index e0f4943d40f..00000000000 --- a/db/ci/migrate/20131120155545_add_email_notification_fields_to_project.rb +++ /dev/null @@ -1,7 +0,0 @@ -class AddEmailNotificationFieldsToProject < ActiveRecord::Migration - def change - add_column :projects, :email_recipients, :string, default: '', null: false - add_column :projects, :email_add_committer, :boolean, default: true, null: false - add_column :projects, :email_all_broken_builds, :boolean, default: true, null: false - end -end diff --git a/db/ci/migrate/20140130121538_rename_project_fields.rb b/db/ci/migrate/20140130121538_rename_project_fields.rb deleted file mode 100644 index 3d7d3e8167e..00000000000 --- a/db/ci/migrate/20140130121538_rename_project_fields.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RenameProjectFields < ActiveRecord::Migration - def change - rename_column :projects, :email_all_broken_builds, :email_only_broken_builds - end -end diff --git a/db/ci/migrate/20140222210357_create_web_hook.rb b/db/ci/migrate/20140222210357_create_web_hook.rb deleted file mode 100644 index 743ad816906..00000000000 --- a/db/ci/migrate/20140222210357_create_web_hook.rb +++ /dev/null @@ -1,9 +0,0 @@ -class CreateWebHook < ActiveRecord::Migration - def change - create_table :web_hooks do |t| - t.string :url, null: false - t.integer :project_id, null: false - t.timestamps - end - end -end diff --git a/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb b/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb deleted file mode 100644 index 3bf9f036ae8..00000000000 --- a/db/ci/migrate/20140506091853_remove_public_key_from_runner.rb +++ /dev/null @@ -1,5 +0,0 @@ -class RemovePublicKeyFromRunner < ActiveRecord::Migration - def change - remove_column :runners, :public_key - end -end diff --git a/db/ci/migrate/20140823225019_create_commits_from_builds.rb b/db/ci/migrate/20140823225019_create_commits_from_builds.rb deleted file mode 100644 index 15f84b11511..00000000000 --- a/db/ci/migrate/20140823225019_create_commits_from_builds.rb +++ /dev/null @@ -1,22 +0,0 @@ -class CreateCommitsFromBuilds < ActiveRecord::Migration - def change - create_table :commits do |t| - t.integer :project_id - t.string :ref, nil: false - t.string :sha, nil: false - t.string :before_sha, nil: false - t.text :push_data, nil: false - - t.timestamps - end - - add_column :builds, :commit_id, :integer - - # Remove commit data from builds - #remove_column :builds, :project_id, :integer - #remove_column :builds, :ref, :string - #remove_column :builds, :sha, :string - #remove_column :builds, :before_sha, :string - #remove_column :builds, :push_data, :text - end -end diff --git a/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb b/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb deleted file mode 100644 index 2d7b1a223e2..00000000000 --- a/db/ci/migrate/20140909142245_add_skip_refs_to_projects.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddSkipRefsToProjects < ActiveRecord::Migration - def change - add_column :projects, :skip_refs, :string - end -end diff --git a/db/ci/migrate/20141001125939_add_coverage_parser.rb b/db/ci/migrate/20141001125939_add_coverage_parser.rb deleted file mode 100644 index 7ea7d6047a9..00000000000 --- a/db/ci/migrate/20141001125939_add_coverage_parser.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddCoverageParser < ActiveRecord::Migration - def change - add_column :projects, :coverage_regex, :string - end -end diff --git a/db/ci/migrate/20141001132129_add_coverage_to_build.rb b/db/ci/migrate/20141001132129_add_coverage_to_build.rb deleted file mode 100644 index 442a3dd28c0..00000000000 --- a/db/ci/migrate/20141001132129_add_coverage_to_build.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddCoverageToBuild < ActiveRecord::Migration - def change - add_column :builds, :coverage, :float - end -end diff --git a/db/ci/migrate/20141028162820_add_sha_index_to_build.rb b/db/ci/migrate/20141028162820_add_sha_index_to_build.rb deleted file mode 100644 index bd2a4de5657..00000000000 --- a/db/ci/migrate/20141028162820_add_sha_index_to_build.rb +++ /dev/null @@ -1,6 +0,0 @@ -class AddShaIndexToBuild < ActiveRecord::Migration - def change - add_index :builds, :sha - add_index :builds, [:project_id, :sha] - end -end diff --git a/db/ci/migrate/20141031114419_migrate_build_to_commits.rb b/db/ci/migrate/20141031114419_migrate_build_to_commits.rb deleted file mode 100644 index dc90ec6d15e..00000000000 --- a/db/ci/migrate/20141031114419_migrate_build_to_commits.rb +++ /dev/null @@ -1,21 +0,0 @@ -class MigrateBuildToCommits < ActiveRecord::Migration - def change - execute < Date: Fri, 11 Sep 2015 14:43:51 +0200 Subject: Remove kaminari views moved from CI --- app/views/ci/kaminari/_first_page.html.haml | 2 -- app/views/ci/kaminari/_gap.html.haml | 2 -- app/views/ci/kaminari/_last_page.html.haml | 2 -- app/views/ci/kaminari/_next_page.html.haml | 2 -- app/views/ci/kaminari/_page.html.haml | 2 -- app/views/ci/kaminari/_paginator.html.haml | 11 ----------- app/views/ci/kaminari/_prev_page.html.haml | 2 -- 7 files changed, 23 deletions(-) delete mode 100644 app/views/ci/kaminari/_first_page.html.haml delete mode 100644 app/views/ci/kaminari/_gap.html.haml delete mode 100644 app/views/ci/kaminari/_last_page.html.haml delete mode 100644 app/views/ci/kaminari/_next_page.html.haml delete mode 100644 app/views/ci/kaminari/_page.html.haml delete mode 100644 app/views/ci/kaminari/_paginator.html.haml delete mode 100644 app/views/ci/kaminari/_prev_page.html.haml diff --git a/app/views/ci/kaminari/_first_page.html.haml b/app/views/ci/kaminari/_first_page.html.haml deleted file mode 100644 index a1bbf18690c..00000000000 --- a/app/views/ci/kaminari/_first_page.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li - = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote diff --git a/app/views/ci/kaminari/_gap.html.haml b/app/views/ci/kaminari/_gap.html.haml deleted file mode 100644 index dfe33aac21d..00000000000 --- a/app/views/ci/kaminari/_gap.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li.disabled - = link_to raw(t 'views.pagination.truncate'), '#' diff --git a/app/views/ci/kaminari/_last_page.html.haml b/app/views/ci/kaminari/_last_page.html.haml deleted file mode 100644 index e70697d04ad..00000000000 --- a/app/views/ci/kaminari/_last_page.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li - = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} diff --git a/app/views/ci/kaminari/_next_page.html.haml b/app/views/ci/kaminari/_next_page.html.haml deleted file mode 100644 index ea9af4539e0..00000000000 --- a/app/views/ci/kaminari/_next_page.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li - = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote diff --git a/app/views/ci/kaminari/_page.html.haml b/app/views/ci/kaminari/_page.html.haml deleted file mode 100644 index 9df7ce02f8f..00000000000 --- a/app/views/ci/kaminari/_page.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li{class: "#{'active' if page.current?}"} - = link_to page, page.current? ? '#' : url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} diff --git a/app/views/ci/kaminari/_paginator.html.haml b/app/views/ci/kaminari/_paginator.html.haml deleted file mode 100644 index 07fdb1e08a6..00000000000 --- a/app/views/ci/kaminari/_paginator.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -= paginator.render do - %ul.pagination - = first_page_tag unless current_page.first? - = prev_page_tag unless current_page.first? - - each_page do |page| - - if page.left_outer? || page.right_outer? || page.inside_window? - = page_tag page - - elsif !page.was_truncated? - = gap_tag - = next_page_tag unless current_page.last? - = last_page_tag unless current_page.last? diff --git a/app/views/ci/kaminari/_prev_page.html.haml b/app/views/ci/kaminari/_prev_page.html.haml deleted file mode 100644 index dab3b318dac..00000000000 --- a/app/views/ci/kaminari/_prev_page.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -%li - = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote -- cgit v1.2.1 From 2b683807b52d4b7d156da31ed9e1f8aa20c248d9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 19:25:53 +0200 Subject: Use GitLab UI when render CI --- app/assets/javascripts/ci/application.js.coffee | 10 - app/assets/stylesheets/application.scss | 6 + app/assets/stylesheets/ci/application.scss | 59 - app/assets/stylesheets/ci/builds.scss | 70 + app/assets/stylesheets/ci/lint.scss | 10 + app/assets/stylesheets/ci/projects.scss | 50 + app/assets/stylesheets/ci/runners.scss | 36 + app/assets/stylesheets/ci/sections/builds.scss | 62 - app/assets/stylesheets/ci/sections/lint.scss | 8 - app/assets/stylesheets/ci/sections/login.scss | 13 - app/assets/stylesheets/ci/sections/navbar.scss | 54 - app/assets/stylesheets/ci/sections/projects.scss | 61 - app/assets/stylesheets/ci/sections/runners.scss | 34 - app/assets/stylesheets/ci/sections/setup.scss | 11 - app/assets/stylesheets/ci/xterm.scss | 1796 +++++++++++----------- app/controllers/ci/builds_controller.rb | 1 + app/controllers/ci/commits_controller.rb | 1 + app/views/ci/builds/show.html.haml | 2 +- app/views/ci/commits/show.html.haml | 4 +- app/views/ci/projects/show.html.haml | 5 +- app/views/layouts/ci/_head.html.haml | 11 - app/views/layouts/ci/_nav.html.haml | 32 - app/views/layouts/ci/_nav_admin.html.haml | 11 +- app/views/layouts/ci/_nav_dashboard.html.haml | 24 + app/views/layouts/ci/_nav_project.html.haml | 34 +- app/views/layouts/ci/_page.html.haml | 26 + app/views/layouts/ci/admin.html.haml | 23 +- app/views/layouts/ci/application.html.haml | 19 +- app/views/layouts/ci/empty.html.haml | 14 - app/views/layouts/ci/project.html.haml | 32 +- 30 files changed, 1182 insertions(+), 1337 deletions(-) delete mode 100644 app/assets/stylesheets/ci/application.scss create mode 100644 app/assets/stylesheets/ci/builds.scss create mode 100644 app/assets/stylesheets/ci/lint.scss create mode 100644 app/assets/stylesheets/ci/projects.scss create mode 100644 app/assets/stylesheets/ci/runners.scss delete mode 100644 app/assets/stylesheets/ci/sections/builds.scss delete mode 100644 app/assets/stylesheets/ci/sections/lint.scss delete mode 100644 app/assets/stylesheets/ci/sections/login.scss delete mode 100644 app/assets/stylesheets/ci/sections/navbar.scss delete mode 100644 app/assets/stylesheets/ci/sections/projects.scss delete mode 100644 app/assets/stylesheets/ci/sections/runners.scss delete mode 100644 app/assets/stylesheets/ci/sections/setup.scss delete mode 100644 app/views/layouts/ci/_head.html.haml delete mode 100644 app/views/layouts/ci/_nav.html.haml create mode 100644 app/views/layouts/ci/_nav_dashboard.html.haml create mode 100644 app/views/layouts/ci/_page.html.haml delete mode 100644 app/views/layouts/ci/empty.html.haml diff --git a/app/assets/javascripts/ci/application.js.coffee b/app/assets/javascripts/ci/application.js.coffee index c2f7bfe9776..05aa0f366bb 100644 --- a/app/assets/javascripts/ci/application.js.coffee +++ b/app/assets/javascripts/ci/application.js.coffee @@ -10,20 +10,10 @@ # WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD # GO AFTER THE REQUIRES BELOW. # -#= require jquery -#= require bootstrap -#= require jquery_ujs -#= require turbolinks -#= require jquery.turbolinks -#= require jquery.endless-scroll #= require pager -#= require nprogress -#= require nprogress-turbolinks #= require jquery_nested_form #= require_tree . # -# - $(document).on 'click', '.edit-runner-link', (event) -> event.preventDefault() diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 46f7feddf8d..d9ede637944 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -61,3 +61,9 @@ * Styles for JS behaviors. */ @import "behaviors.scss"; + +/** + * CI specific styles: + */ +@import "ci/**/*"; + diff --git a/app/assets/stylesheets/ci/application.scss b/app/assets/stylesheets/ci/application.scss deleted file mode 100644 index 2f2928f7054..00000000000 --- a/app/assets/stylesheets/ci/application.scss +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the top of the - * compiled file, but it's generally better to create a new file per style scope. - * - *= require_self - */ - -@import "../base/fonts"; -@import "../base/variables"; -@import "../base/mixins"; -@import "../base/layout"; -@import "../base/gl_variables"; -@import "../base/gl_bootstrap"; - -/** - * Customized Twitter bootstrap - */ -@import '../base/gl_variables'; -@import '../base/gl_bootstrap'; - - -/** - * Font icons - * - */ -@import "font-awesome"; - -/** - * Generic css (forms, nav etc): - */ -@import "../generic/**/*"; - -/** - * Page specific styles (issues, projects etc): - */ - -@import "xterm"; -@import "sections/*"; - -/* - * NProgress - */ -$nprogress-color: #9BC; -@import 'nprogress'; -@import 'nprogress-bootstrap'; - -body { - padding-top: 0 !important; - - a { - color: #3084bb; - } -} diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/ci/builds.scss new file mode 100644 index 00000000000..a11a935b54d --- /dev/null +++ b/app/assets/stylesheets/ci/builds.scss @@ -0,0 +1,70 @@ +.ci-body { + pre.trace { + background: #111111; + color: #fff; + font-family: $monospace_font; + white-space: pre; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + overflow: auto; + overflow-y: hidden; + font-size: 12px; + + .fa-refresh { + font-size: 24px; + margin-left: 20px; + } + } + + .autoscroll-container { + position: fixed; + bottom: 10px; + right: 20px; + z-index: 100; + } + + .scroll-controls { + position: fixed; + bottom: 10px; + left: 250px; + z-index: 100; + + a { + display: block; + margin-bottom: 5px; + } + } + + .page-sidebar-collapsed { + .scroll-controls { + left: 70px; + } + } + + .build-widget { + padding: 10px; + background: $background-color; + margin-bottom: 20px; + border-radius: 4px; + + .title { + margin-top: 0; + color: #666; + line-height: 1.5; + } + .attr-name { + color: #777; + } + } + + .alert-disabled { + background: $background-color; + + a { + color: #3084bb !important; + } + } +} diff --git a/app/assets/stylesheets/ci/lint.scss b/app/assets/stylesheets/ci/lint.scss new file mode 100644 index 00000000000..6d2bd33b28b --- /dev/null +++ b/app/assets/stylesheets/ci/lint.scss @@ -0,0 +1,10 @@ +.ci-body { + .incorrect-syntax{ + font-size: 19px; + color: red; + } + .correct-syntax{ + font-size: 19px; + color: #47a447; + } +} diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss new file mode 100644 index 00000000000..167df54453a --- /dev/null +++ b/app/assets/stylesheets/ci/projects.scss @@ -0,0 +1,50 @@ +.ci-body { + .project-title { + margin: 0; + color: #444; + font-size: 20px; + line-height: 1.5; + } + + .builds { + @extend .table; + + .build { + &.alert{ + margin-bottom: 6px; + } + } + } + + .projects-table { + td { + vertical-align: middle !important; + } + } + + .commit-info { + font-size: 14px; + + .attr-name { + font-weight: 300; + color: #666; + margin-right: 5px; + } + + pre.commit-message { + font-size: 14px; + background: none; + padding: 0; + margin: 0; + border: none; + margin: 20px 0; + border-bottom: 1px solid #EEE; + padding-bottom: 20px; + border-radius: 0; + } + } + + .loading{ + font-size: 20px; + } +} diff --git a/app/assets/stylesheets/ci/runners.scss b/app/assets/stylesheets/ci/runners.scss new file mode 100644 index 00000000000..2b15ab83129 --- /dev/null +++ b/app/assets/stylesheets/ci/runners.scss @@ -0,0 +1,36 @@ +.ci-body { + .runner-state { + padding: 6px 12px; + margin-right: 10px; + color: #FFF; + + &.runner-state-shared { + background: #32b186; + } + &.runner-state-specific { + background: #3498db; + } + } + + .runner-status-online { + color: green; + } + + .runner-status-offline { + color: gray; + } + + .runner-status-paused { + color: red; + } + + .runner { + .btn { + padding: 1px 6px; + } + + h4 { + font-weight: normal; + } + } +} diff --git a/app/assets/stylesheets/ci/sections/builds.scss b/app/assets/stylesheets/ci/sections/builds.scss deleted file mode 100644 index 600919635d0..00000000000 --- a/app/assets/stylesheets/ci/sections/builds.scss +++ /dev/null @@ -1,62 +0,0 @@ -pre.trace { - background: #111111; - color: #fff; - font-family: $monospace_font; - white-space: pre; - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - overflow: auto; - overflow-y: hidden; - font-size: 12px; - - .fa-refresh { - font-size: 24px; - margin-left: 20px; - } -} - -.autoscroll-container { - position: fixed; - bottom: 10px; - right: 20px; - z-index: 100; -} - -.scroll-controls { - position: fixed; - bottom: 10px; - left: 20px; - z-index: 100; - - a { - display: block; - margin-bottom: 5px; - } -} - -.build-widget { - padding: 10px; - background: #f4f4f4; - margin-bottom: 20px; - border-radius: 4px; - - .title { - margin-top: 0; - color: #666; - line-height: 1.5; - } - .attr-name { - color: #777; - } -} - -.alert-disabled { - background: #EEE; - - a { - color: #3084bb !important; - } -} diff --git a/app/assets/stylesheets/ci/sections/lint.scss b/app/assets/stylesheets/ci/sections/lint.scss deleted file mode 100644 index 7191b5d47aa..00000000000 --- a/app/assets/stylesheets/ci/sections/lint.scss +++ /dev/null @@ -1,8 +0,0 @@ -.incorrect-syntax{ - font-size: 19px; - color: red; -} -.correct-syntax{ - font-size: 19px; - color: #47a447; -} \ No newline at end of file diff --git a/app/assets/stylesheets/ci/sections/login.scss b/app/assets/stylesheets/ci/sections/login.scss deleted file mode 100644 index 47e453ec8d2..00000000000 --- a/app/assets/stylesheets/ci/sections/login.scss +++ /dev/null @@ -1,13 +0,0 @@ -.login-block { - padding: 15px; - margin: 0 auto; - text-align: center; - - p { - font-size: 15px; - } - - .btn-login { - padding: 18px 32px; - } -} diff --git a/app/assets/stylesheets/ci/sections/navbar.scss b/app/assets/stylesheets/ci/sections/navbar.scss deleted file mode 100644 index 80d93087e66..00000000000 --- a/app/assets/stylesheets/ci/sections/navbar.scss +++ /dev/null @@ -1,54 +0,0 @@ -.navbar-static-top { - margin-bottom: 20px; -} - -.navbar-ci { - background: #224466; - - .navbar-brand { - color: #fff; - - &:hover { - color: #fff; - } - } - .brand, - .nav > li > a { - color: #fff; - - &:hover, &:focus, &:active { - background: none; - } - } - - .profile-holder { - position: relative; - - img { - position: absolute; - top: -8px; - width: 32px; - @include border-radius(32px); - } - - span { - margin-left: 42px; - } - } - - .btn-login { - padding: 7px 22px; - margin-top: 7px; - &:hover, &:active, &:focus { - background: #018865 !important; - } - } -} - -.turbolink-spinner { - position: absolute; - top: 11px; - left: 50%; - color: #FFF; - font-size: 20px; -} diff --git a/app/assets/stylesheets/ci/sections/projects.scss b/app/assets/stylesheets/ci/sections/projects.scss deleted file mode 100644 index 84ee1399bff..00000000000 --- a/app/assets/stylesheets/ci/sections/projects.scss +++ /dev/null @@ -1,61 +0,0 @@ -.project-title { - margin: 0; - color: #444; - font-size: 20px; - line-height: 1.5; -} - -.builds { - @extend .table; - - .build { - &.alert{ - margin-bottom: 6px; - } - } -} - -.projects-table { - td { - vertical-align: middle !important; - } -} - -.commit-info { - font-size: 14px; - - .attr-name { - font-weight: 300; - color: #666; - margin-right: 5px; - } - - pre.commit-message { - font-size: 14px; - background: none; - padding: 0; - margin: 0; - border: none; - margin: 20px 0; - border-bottom: 1px solid #EEE; - padding-bottom: 20px; - border-radius: 0; - } -} - -.search{ - width: 300px; - - .search-input{ - height: 35px; - } - - form{ - margin-top: 0; - margin-bottom: 0; - } -} - -.loading{ - font-size: 20px; -} diff --git a/app/assets/stylesheets/ci/sections/runners.scss b/app/assets/stylesheets/ci/sections/runners.scss deleted file mode 100644 index a9111a7388f..00000000000 --- a/app/assets/stylesheets/ci/sections/runners.scss +++ /dev/null @@ -1,34 +0,0 @@ -.runner-state { - padding: 6px 12px; - margin-right: 10px; - color: #FFF; - - &.runner-state-shared { - background: #32b186; - } - &.runner-state-specific { - background: #3498db; - } -} - -.runner-status-online { - color: green; -} - -.runner-status-offline { - color: gray; -} - -.runner-status-paused { - color: red; -} - -.runner { - .btn { - padding: 1px 6px; - } - - h4 { - font-weight: normal; - } -} diff --git a/app/assets/stylesheets/ci/sections/setup.scss b/app/assets/stylesheets/ci/sections/setup.scss deleted file mode 100644 index 242614616d1..00000000000 --- a/app/assets/stylesheets/ci/sections/setup.scss +++ /dev/null @@ -1,11 +0,0 @@ -.welcome-block { - margin-top: 50px; - color: #555; - font-size: 16px; - line-height: 1.5; - - h1, h2, h3 { - font-weight: bold; - margin-bottom: 20px; - } -} diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/ci/xterm.scss index 460a6bb2024..532dede0b23 100644 --- a/app/assets/stylesheets/ci/xterm.scss +++ b/app/assets/stylesheets/ci/xterm.scss @@ -1,904 +1,906 @@ -// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg -// see also: https://gist.github.com/jasonm23/2868981 +.ci-body { + // color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg + // see also: https://gist.github.com/jasonm23/2868981 -$black: #000000; -$red: #cd0000; -$green: #00cd00; -$yellow: #cdcd00; -$blue: #0000ee; // according to wikipedia, this is the xterm standard -//$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) -$magenta: #cd00cd; -$cyan: #00cdcd; -$white: #e5e5e5; -$l-black: #7f7f7f; -$l-red: #ff0000; -$l-green: #00ff00; -$l-yellow: #ffff00; -$l-blue: #5c5cff; -$l-magenta: #ff00ff; -$l-cyan: #00ffff; -$l-white: #ffffff; + $black: #000000; + $red: #cd0000; + $green: #00cd00; + $yellow: #cdcd00; + $blue: #0000ee; // according to wikipedia, this is the xterm standard + //$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) + $magenta: #cd00cd; + $cyan: #00cdcd; + $white: #e5e5e5; + $l-black: #7f7f7f; + $l-red: #ff0000; + $l-green: #00ff00; + $l-yellow: #ffff00; + $l-blue: #5c5cff; + $l-magenta: #ff00ff; + $l-cyan: #00ffff; + $l-white: #ffffff; -.term-bold { - font-weight: bold; -} -.term-italic { - font-style: italic; -} -.term-conceal { - visibility: hidden; -} -.term-underline { - text-decoration: underline; -} -.term-cross { - text-decoration: line-through; -} + .term-bold { + font-weight: bold; + } + .term-italic { + font-style: italic; + } + .term-conceal { + visibility: hidden; + } + .term-underline { + text-decoration: underline; + } + .term-cross { + text-decoration: line-through; + } -.term-fg-black { - color: $black; -} -.term-fg-red { - color: $red; -} -.term-fg-green { - color: $green; -} -.term-fg-yellow { - color: $yellow; -} -.term-fg-blue { - color: $blue; -} -.term-fg-magenta { - color: $magenta; -} -.term-fg-cyan { - color: $cyan; -} -.term-fg-white { - color: $white; -} -.term-fg-l-black { - color: $l-black; -} -.term-fg-l-red { - color: $l-red; -} -.term-fg-l-green { - color: $l-green; -} -.term-fg-l-yellow { - color: $l-yellow; -} -.term-fg-l-blue { - color: $l-blue; -} -.term-fg-l-magenta { - color: $l-magenta; -} -.term-fg-l-cyan { - color: $l-cyan; -} -.term-fg-l-white { - color: $l-white; -} + .term-fg-black { + color: $black; + } + .term-fg-red { + color: $red; + } + .term-fg-green { + color: $green; + } + .term-fg-yellow { + color: $yellow; + } + .term-fg-blue { + color: $blue; + } + .term-fg-magenta { + color: $magenta; + } + .term-fg-cyan { + color: $cyan; + } + .term-fg-white { + color: $white; + } + .term-fg-l-black { + color: $l-black; + } + .term-fg-l-red { + color: $l-red; + } + .term-fg-l-green { + color: $l-green; + } + .term-fg-l-yellow { + color: $l-yellow; + } + .term-fg-l-blue { + color: $l-blue; + } + .term-fg-l-magenta { + color: $l-magenta; + } + .term-fg-l-cyan { + color: $l-cyan; + } + .term-fg-l-white { + color: $l-white; + } -.term-bg-black { - background-color: $black; -} -.term-bg-red { - background-color: $red; -} -.term-bg-green { - background-color: $green; -} -.term-bg-yellow { - background-color: $yellow; -} -.term-bg-blue { - background-color: $blue; -} -.term-bg-magenta { - background-color: $magenta; -} -.term-bg-cyan { - background-color: $cyan; -} -.term-bg-white { - background-color: $white; -} -.term-bg-l-black { - background-color: $l-black; -} -.term-bg-l-red { - background-color: $l-red; -} -.term-bg-l-green { - background-color: $l-green; -} -.term-bg-l-yellow { - background-color: $l-yellow; -} -.term-bg-l-blue { - background-color: $l-blue; -} -.term-bg-l-magenta { - background-color: $l-magenta; -} -.term-bg-l-cyan { - background-color: $l-cyan; -} -.term-bg-l-white { - background-color: $l-white; -} + .term-bg-black { + background-color: $black; + } + .term-bg-red { + background-color: $red; + } + .term-bg-green { + background-color: $green; + } + .term-bg-yellow { + background-color: $yellow; + } + .term-bg-blue { + background-color: $blue; + } + .term-bg-magenta { + background-color: $magenta; + } + .term-bg-cyan { + background-color: $cyan; + } + .term-bg-white { + background-color: $white; + } + .term-bg-l-black { + background-color: $l-black; + } + .term-bg-l-red { + background-color: $l-red; + } + .term-bg-l-green { + background-color: $l-green; + } + .term-bg-l-yellow { + background-color: $l-yellow; + } + .term-bg-l-blue { + background-color: $l-blue; + } + .term-bg-l-magenta { + background-color: $l-magenta; + } + .term-bg-l-cyan { + background-color: $l-cyan; + } + .term-bg-l-white { + background-color: $l-white; + } -.xterm-fg-0 { - color: #000000; -} -.xterm-fg-1 { - color: #800000; -} -.xterm-fg-2 { - color: #008000; -} -.xterm-fg-3 { - color: #808000; -} -.xterm-fg-4 { - color: #000080; -} -.xterm-fg-5 { - color: #800080; -} -.xterm-fg-6 { - color: #008080; -} -.xterm-fg-7 { - color: #c0c0c0; -} -.xterm-fg-8 { - color: #808080; -} -.xterm-fg-9 { - color: #ff0000; -} -.xterm-fg-10 { - color: #00ff00; -} -.xterm-fg-11 { - color: #ffff00; -} -.xterm-fg-12 { - color: #0000ff; -} -.xterm-fg-13 { - color: #ff00ff; -} -.xterm-fg-14 { - color: #00ffff; -} -.xterm-fg-15 { - color: #ffffff; -} -.xterm-fg-16 { - color: #000000; -} -.xterm-fg-17 { - color: #00005f; -} -.xterm-fg-18 { - color: #000087; -} -.xterm-fg-19 { - color: #0000af; -} -.xterm-fg-20 { - color: #0000d7; -} -.xterm-fg-21 { - color: #0000ff; -} -.xterm-fg-22 { - color: #005f00; -} -.xterm-fg-23 { - color: #005f5f; -} -.xterm-fg-24 { - color: #005f87; -} -.xterm-fg-25 { - color: #005faf; -} -.xterm-fg-26 { - color: #005fd7; -} -.xterm-fg-27 { - color: #005fff; -} -.xterm-fg-28 { - color: #008700; -} -.xterm-fg-29 { - color: #00875f; -} -.xterm-fg-30 { - color: #008787; -} -.xterm-fg-31 { - color: #0087af; -} -.xterm-fg-32 { - color: #0087d7; -} -.xterm-fg-33 { - color: #0087ff; -} -.xterm-fg-34 { - color: #00af00; -} -.xterm-fg-35 { - color: #00af5f; -} -.xterm-fg-36 { - color: #00af87; -} -.xterm-fg-37 { - color: #00afaf; -} -.xterm-fg-38 { - color: #00afd7; -} -.xterm-fg-39 { - color: #00afff; -} -.xterm-fg-40 { - color: #00d700; -} -.xterm-fg-41 { - color: #00d75f; -} -.xterm-fg-42 { - color: #00d787; -} -.xterm-fg-43 { - color: #00d7af; -} -.xterm-fg-44 { - color: #00d7d7; -} -.xterm-fg-45 { - color: #00d7ff; -} -.xterm-fg-46 { - color: #00ff00; -} -.xterm-fg-47 { - color: #00ff5f; -} -.xterm-fg-48 { - color: #00ff87; -} -.xterm-fg-49 { - color: #00ffaf; -} -.xterm-fg-50 { - color: #00ffd7; -} -.xterm-fg-51 { - color: #00ffff; -} -.xterm-fg-52 { - color: #5f0000; -} -.xterm-fg-53 { - color: #5f005f; -} -.xterm-fg-54 { - color: #5f0087; -} -.xterm-fg-55 { - color: #5f00af; -} -.xterm-fg-56 { - color: #5f00d7; -} -.xterm-fg-57 { - color: #5f00ff; -} -.xterm-fg-58 { - color: #5f5f00; -} -.xterm-fg-59 { - color: #5f5f5f; -} -.xterm-fg-60 { - color: #5f5f87; -} -.xterm-fg-61 { - color: #5f5faf; -} -.xterm-fg-62 { - color: #5f5fd7; -} -.xterm-fg-63 { - color: #5f5fff; -} -.xterm-fg-64 { - color: #5f8700; -} -.xterm-fg-65 { - color: #5f875f; -} -.xterm-fg-66 { - color: #5f8787; -} -.xterm-fg-67 { - color: #5f87af; -} -.xterm-fg-68 { - color: #5f87d7; -} -.xterm-fg-69 { - color: #5f87ff; -} -.xterm-fg-70 { - color: #5faf00; -} -.xterm-fg-71 { - color: #5faf5f; -} -.xterm-fg-72 { - color: #5faf87; -} -.xterm-fg-73 { - color: #5fafaf; -} -.xterm-fg-74 { - color: #5fafd7; -} -.xterm-fg-75 { - color: #5fafff; -} -.xterm-fg-76 { - color: #5fd700; -} -.xterm-fg-77 { - color: #5fd75f; -} -.xterm-fg-78 { - color: #5fd787; -} -.xterm-fg-79 { - color: #5fd7af; -} -.xterm-fg-80 { - color: #5fd7d7; -} -.xterm-fg-81 { - color: #5fd7ff; -} -.xterm-fg-82 { - color: #5fff00; -} -.xterm-fg-83 { - color: #5fff5f; -} -.xterm-fg-84 { - color: #5fff87; -} -.xterm-fg-85 { - color: #5fffaf; -} -.xterm-fg-86 { - color: #5fffd7; -} -.xterm-fg-87 { - color: #5fffff; -} -.xterm-fg-88 { - color: #870000; -} -.xterm-fg-89 { - color: #87005f; -} -.xterm-fg-90 { - color: #870087; -} -.xterm-fg-91 { - color: #8700af; -} -.xterm-fg-92 { - color: #8700d7; -} -.xterm-fg-93 { - color: #8700ff; -} -.xterm-fg-94 { - color: #875f00; -} -.xterm-fg-95 { - color: #875f5f; -} -.xterm-fg-96 { - color: #875f87; -} -.xterm-fg-97 { - color: #875faf; -} -.xterm-fg-98 { - color: #875fd7; -} -.xterm-fg-99 { - color: #875fff; -} -.xterm-fg-100 { - color: #878700; -} -.xterm-fg-101 { - color: #87875f; -} -.xterm-fg-102 { - color: #878787; -} -.xterm-fg-103 { - color: #8787af; -} -.xterm-fg-104 { - color: #8787d7; -} -.xterm-fg-105 { - color: #8787ff; -} -.xterm-fg-106 { - color: #87af00; -} -.xterm-fg-107 { - color: #87af5f; -} -.xterm-fg-108 { - color: #87af87; -} -.xterm-fg-109 { - color: #87afaf; -} -.xterm-fg-110 { - color: #87afd7; -} -.xterm-fg-111 { - color: #87afff; -} -.xterm-fg-112 { - color: #87d700; -} -.xterm-fg-113 { - color: #87d75f; -} -.xterm-fg-114 { - color: #87d787; -} -.xterm-fg-115 { - color: #87d7af; -} -.xterm-fg-116 { - color: #87d7d7; -} -.xterm-fg-117 { - color: #87d7ff; -} -.xterm-fg-118 { - color: #87ff00; -} -.xterm-fg-119 { - color: #87ff5f; -} -.xterm-fg-120 { - color: #87ff87; -} -.xterm-fg-121 { - color: #87ffaf; -} -.xterm-fg-122 { - color: #87ffd7; -} -.xterm-fg-123 { - color: #87ffff; -} -.xterm-fg-124 { - color: #af0000; -} -.xterm-fg-125 { - color: #af005f; -} -.xterm-fg-126 { - color: #af0087; -} -.xterm-fg-127 { - color: #af00af; -} -.xterm-fg-128 { - color: #af00d7; -} -.xterm-fg-129 { - color: #af00ff; -} -.xterm-fg-130 { - color: #af5f00; -} -.xterm-fg-131 { - color: #af5f5f; -} -.xterm-fg-132 { - color: #af5f87; -} -.xterm-fg-133 { - color: #af5faf; -} -.xterm-fg-134 { - color: #af5fd7; -} -.xterm-fg-135 { - color: #af5fff; -} -.xterm-fg-136 { - color: #af8700; -} -.xterm-fg-137 { - color: #af875f; -} -.xterm-fg-138 { - color: #af8787; -} -.xterm-fg-139 { - color: #af87af; -} -.xterm-fg-140 { - color: #af87d7; -} -.xterm-fg-141 { - color: #af87ff; -} -.xterm-fg-142 { - color: #afaf00; -} -.xterm-fg-143 { - color: #afaf5f; -} -.xterm-fg-144 { - color: #afaf87; -} -.xterm-fg-145 { - color: #afafaf; -} -.xterm-fg-146 { - color: #afafd7; -} -.xterm-fg-147 { - color: #afafff; -} -.xterm-fg-148 { - color: #afd700; -} -.xterm-fg-149 { - color: #afd75f; -} -.xterm-fg-150 { - color: #afd787; -} -.xterm-fg-151 { - color: #afd7af; -} -.xterm-fg-152 { - color: #afd7d7; -} -.xterm-fg-153 { - color: #afd7ff; -} -.xterm-fg-154 { - color: #afff00; -} -.xterm-fg-155 { - color: #afff5f; -} -.xterm-fg-156 { - color: #afff87; -} -.xterm-fg-157 { - color: #afffaf; -} -.xterm-fg-158 { - color: #afffd7; -} -.xterm-fg-159 { - color: #afffff; -} -.xterm-fg-160 { - color: #d70000; -} -.xterm-fg-161 { - color: #d7005f; -} -.xterm-fg-162 { - color: #d70087; -} -.xterm-fg-163 { - color: #d700af; -} -.xterm-fg-164 { - color: #d700d7; -} -.xterm-fg-165 { - color: #d700ff; -} -.xterm-fg-166 { - color: #d75f00; -} -.xterm-fg-167 { - color: #d75f5f; -} -.xterm-fg-168 { - color: #d75f87; -} -.xterm-fg-169 { - color: #d75faf; -} -.xterm-fg-170 { - color: #d75fd7; -} -.xterm-fg-171 { - color: #d75fff; -} -.xterm-fg-172 { - color: #d78700; -} -.xterm-fg-173 { - color: #d7875f; -} -.xterm-fg-174 { - color: #d78787; -} -.xterm-fg-175 { - color: #d787af; -} -.xterm-fg-176 { - color: #d787d7; -} -.xterm-fg-177 { - color: #d787ff; -} -.xterm-fg-178 { - color: #d7af00; -} -.xterm-fg-179 { - color: #d7af5f; -} -.xterm-fg-180 { - color: #d7af87; -} -.xterm-fg-181 { - color: #d7afaf; -} -.xterm-fg-182 { - color: #d7afd7; -} -.xterm-fg-183 { - color: #d7afff; -} -.xterm-fg-184 { - color: #d7d700; -} -.xterm-fg-185 { - color: #d7d75f; -} -.xterm-fg-186 { - color: #d7d787; -} -.xterm-fg-187 { - color: #d7d7af; -} -.xterm-fg-188 { - color: #d7d7d7; -} -.xterm-fg-189 { - color: #d7d7ff; -} -.xterm-fg-190 { - color: #d7ff00; -} -.xterm-fg-191 { - color: #d7ff5f; -} -.xterm-fg-192 { - color: #d7ff87; -} -.xterm-fg-193 { - color: #d7ffaf; -} -.xterm-fg-194 { - color: #d7ffd7; -} -.xterm-fg-195 { - color: #d7ffff; -} -.xterm-fg-196 { - color: #ff0000; -} -.xterm-fg-197 { - color: #ff005f; -} -.xterm-fg-198 { - color: #ff0087; -} -.xterm-fg-199 { - color: #ff00af; -} -.xterm-fg-200 { - color: #ff00d7; -} -.xterm-fg-201 { - color: #ff00ff; -} -.xterm-fg-202 { - color: #ff5f00; -} -.xterm-fg-203 { - color: #ff5f5f; -} -.xterm-fg-204 { - color: #ff5f87; -} -.xterm-fg-205 { - color: #ff5faf; -} -.xterm-fg-206 { - color: #ff5fd7; -} -.xterm-fg-207 { - color: #ff5fff; -} -.xterm-fg-208 { - color: #ff8700; -} -.xterm-fg-209 { - color: #ff875f; -} -.xterm-fg-210 { - color: #ff8787; -} -.xterm-fg-211 { - color: #ff87af; -} -.xterm-fg-212 { - color: #ff87d7; -} -.xterm-fg-213 { - color: #ff87ff; -} -.xterm-fg-214 { - color: #ffaf00; -} -.xterm-fg-215 { - color: #ffaf5f; -} -.xterm-fg-216 { - color: #ffaf87; -} -.xterm-fg-217 { - color: #ffafaf; -} -.xterm-fg-218 { - color: #ffafd7; -} -.xterm-fg-219 { - color: #ffafff; -} -.xterm-fg-220 { - color: #ffd700; -} -.xterm-fg-221 { - color: #ffd75f; -} -.xterm-fg-222 { - color: #ffd787; -} -.xterm-fg-223 { - color: #ffd7af; -} -.xterm-fg-224 { - color: #ffd7d7; -} -.xterm-fg-225 { - color: #ffd7ff; -} -.xterm-fg-226 { - color: #ffff00; -} -.xterm-fg-227 { - color: #ffff5f; -} -.xterm-fg-228 { - color: #ffff87; -} -.xterm-fg-229 { - color: #ffffaf; -} -.xterm-fg-230 { - color: #ffffd7; -} -.xterm-fg-231 { - color: #ffffff; -} -.xterm-fg-232 { - color: #080808; -} -.xterm-fg-233 { - color: #121212; -} -.xterm-fg-234 { - color: #1c1c1c; -} -.xterm-fg-235 { - color: #262626; -} -.xterm-fg-236 { - color: #303030; -} -.xterm-fg-237 { - color: #3a3a3a; -} -.xterm-fg-238 { - color: #444444; -} -.xterm-fg-239 { - color: #4e4e4e; -} -.xterm-fg-240 { - color: #585858; -} -.xterm-fg-241 { - color: #626262; -} -.xterm-fg-242 { - color: #6c6c6c; -} -.xterm-fg-243 { - color: #767676; -} -.xterm-fg-244 { - color: #808080; -} -.xterm-fg-245 { - color: #8a8a8a; -} -.xterm-fg-246 { - color: #949494; -} -.xterm-fg-247 { - color: #9e9e9e; -} -.xterm-fg-248 { - color: #a8a8a8; -} -.xterm-fg-249 { - color: #b2b2b2; -} -.xterm-fg-250 { - color: #bcbcbc; -} -.xterm-fg-251 { - color: #c6c6c6; -} -.xterm-fg-252 { - color: #d0d0d0; -} -.xterm-fg-253 { - color: #dadada; -} -.xterm-fg-254 { - color: #e4e4e4; -} -.xterm-fg-255 { - color: #eeeeee; + .xterm-fg-0 { + color: #000000; + } + .xterm-fg-1 { + color: #800000; + } + .xterm-fg-2 { + color: #008000; + } + .xterm-fg-3 { + color: #808000; + } + .xterm-fg-4 { + color: #000080; + } + .xterm-fg-5 { + color: #800080; + } + .xterm-fg-6 { + color: #008080; + } + .xterm-fg-7 { + color: #c0c0c0; + } + .xterm-fg-8 { + color: #808080; + } + .xterm-fg-9 { + color: #ff0000; + } + .xterm-fg-10 { + color: #00ff00; + } + .xterm-fg-11 { + color: #ffff00; + } + .xterm-fg-12 { + color: #0000ff; + } + .xterm-fg-13 { + color: #ff00ff; + } + .xterm-fg-14 { + color: #00ffff; + } + .xterm-fg-15 { + color: #ffffff; + } + .xterm-fg-16 { + color: #000000; + } + .xterm-fg-17 { + color: #00005f; + } + .xterm-fg-18 { + color: #000087; + } + .xterm-fg-19 { + color: #0000af; + } + .xterm-fg-20 { + color: #0000d7; + } + .xterm-fg-21 { + color: #0000ff; + } + .xterm-fg-22 { + color: #005f00; + } + .xterm-fg-23 { + color: #005f5f; + } + .xterm-fg-24 { + color: #005f87; + } + .xterm-fg-25 { + color: #005faf; + } + .xterm-fg-26 { + color: #005fd7; + } + .xterm-fg-27 { + color: #005fff; + } + .xterm-fg-28 { + color: #008700; + } + .xterm-fg-29 { + color: #00875f; + } + .xterm-fg-30 { + color: #008787; + } + .xterm-fg-31 { + color: #0087af; + } + .xterm-fg-32 { + color: #0087d7; + } + .xterm-fg-33 { + color: #0087ff; + } + .xterm-fg-34 { + color: #00af00; + } + .xterm-fg-35 { + color: #00af5f; + } + .xterm-fg-36 { + color: #00af87; + } + .xterm-fg-37 { + color: #00afaf; + } + .xterm-fg-38 { + color: #00afd7; + } + .xterm-fg-39 { + color: #00afff; + } + .xterm-fg-40 { + color: #00d700; + } + .xterm-fg-41 { + color: #00d75f; + } + .xterm-fg-42 { + color: #00d787; + } + .xterm-fg-43 { + color: #00d7af; + } + .xterm-fg-44 { + color: #00d7d7; + } + .xterm-fg-45 { + color: #00d7ff; + } + .xterm-fg-46 { + color: #00ff00; + } + .xterm-fg-47 { + color: #00ff5f; + } + .xterm-fg-48 { + color: #00ff87; + } + .xterm-fg-49 { + color: #00ffaf; + } + .xterm-fg-50 { + color: #00ffd7; + } + .xterm-fg-51 { + color: #00ffff; + } + .xterm-fg-52 { + color: #5f0000; + } + .xterm-fg-53 { + color: #5f005f; + } + .xterm-fg-54 { + color: #5f0087; + } + .xterm-fg-55 { + color: #5f00af; + } + .xterm-fg-56 { + color: #5f00d7; + } + .xterm-fg-57 { + color: #5f00ff; + } + .xterm-fg-58 { + color: #5f5f00; + } + .xterm-fg-59 { + color: #5f5f5f; + } + .xterm-fg-60 { + color: #5f5f87; + } + .xterm-fg-61 { + color: #5f5faf; + } + .xterm-fg-62 { + color: #5f5fd7; + } + .xterm-fg-63 { + color: #5f5fff; + } + .xterm-fg-64 { + color: #5f8700; + } + .xterm-fg-65 { + color: #5f875f; + } + .xterm-fg-66 { + color: #5f8787; + } + .xterm-fg-67 { + color: #5f87af; + } + .xterm-fg-68 { + color: #5f87d7; + } + .xterm-fg-69 { + color: #5f87ff; + } + .xterm-fg-70 { + color: #5faf00; + } + .xterm-fg-71 { + color: #5faf5f; + } + .xterm-fg-72 { + color: #5faf87; + } + .xterm-fg-73 { + color: #5fafaf; + } + .xterm-fg-74 { + color: #5fafd7; + } + .xterm-fg-75 { + color: #5fafff; + } + .xterm-fg-76 { + color: #5fd700; + } + .xterm-fg-77 { + color: #5fd75f; + } + .xterm-fg-78 { + color: #5fd787; + } + .xterm-fg-79 { + color: #5fd7af; + } + .xterm-fg-80 { + color: #5fd7d7; + } + .xterm-fg-81 { + color: #5fd7ff; + } + .xterm-fg-82 { + color: #5fff00; + } + .xterm-fg-83 { + color: #5fff5f; + } + .xterm-fg-84 { + color: #5fff87; + } + .xterm-fg-85 { + color: #5fffaf; + } + .xterm-fg-86 { + color: #5fffd7; + } + .xterm-fg-87 { + color: #5fffff; + } + .xterm-fg-88 { + color: #870000; + } + .xterm-fg-89 { + color: #87005f; + } + .xterm-fg-90 { + color: #870087; + } + .xterm-fg-91 { + color: #8700af; + } + .xterm-fg-92 { + color: #8700d7; + } + .xterm-fg-93 { + color: #8700ff; + } + .xterm-fg-94 { + color: #875f00; + } + .xterm-fg-95 { + color: #875f5f; + } + .xterm-fg-96 { + color: #875f87; + } + .xterm-fg-97 { + color: #875faf; + } + .xterm-fg-98 { + color: #875fd7; + } + .xterm-fg-99 { + color: #875fff; + } + .xterm-fg-100 { + color: #878700; + } + .xterm-fg-101 { + color: #87875f; + } + .xterm-fg-102 { + color: #878787; + } + .xterm-fg-103 { + color: #8787af; + } + .xterm-fg-104 { + color: #8787d7; + } + .xterm-fg-105 { + color: #8787ff; + } + .xterm-fg-106 { + color: #87af00; + } + .xterm-fg-107 { + color: #87af5f; + } + .xterm-fg-108 { + color: #87af87; + } + .xterm-fg-109 { + color: #87afaf; + } + .xterm-fg-110 { + color: #87afd7; + } + .xterm-fg-111 { + color: #87afff; + } + .xterm-fg-112 { + color: #87d700; + } + .xterm-fg-113 { + color: #87d75f; + } + .xterm-fg-114 { + color: #87d787; + } + .xterm-fg-115 { + color: #87d7af; + } + .xterm-fg-116 { + color: #87d7d7; + } + .xterm-fg-117 { + color: #87d7ff; + } + .xterm-fg-118 { + color: #87ff00; + } + .xterm-fg-119 { + color: #87ff5f; + } + .xterm-fg-120 { + color: #87ff87; + } + .xterm-fg-121 { + color: #87ffaf; + } + .xterm-fg-122 { + color: #87ffd7; + } + .xterm-fg-123 { + color: #87ffff; + } + .xterm-fg-124 { + color: #af0000; + } + .xterm-fg-125 { + color: #af005f; + } + .xterm-fg-126 { + color: #af0087; + } + .xterm-fg-127 { + color: #af00af; + } + .xterm-fg-128 { + color: #af00d7; + } + .xterm-fg-129 { + color: #af00ff; + } + .xterm-fg-130 { + color: #af5f00; + } + .xterm-fg-131 { + color: #af5f5f; + } + .xterm-fg-132 { + color: #af5f87; + } + .xterm-fg-133 { + color: #af5faf; + } + .xterm-fg-134 { + color: #af5fd7; + } + .xterm-fg-135 { + color: #af5fff; + } + .xterm-fg-136 { + color: #af8700; + } + .xterm-fg-137 { + color: #af875f; + } + .xterm-fg-138 { + color: #af8787; + } + .xterm-fg-139 { + color: #af87af; + } + .xterm-fg-140 { + color: #af87d7; + } + .xterm-fg-141 { + color: #af87ff; + } + .xterm-fg-142 { + color: #afaf00; + } + .xterm-fg-143 { + color: #afaf5f; + } + .xterm-fg-144 { + color: #afaf87; + } + .xterm-fg-145 { + color: #afafaf; + } + .xterm-fg-146 { + color: #afafd7; + } + .xterm-fg-147 { + color: #afafff; + } + .xterm-fg-148 { + color: #afd700; + } + .xterm-fg-149 { + color: #afd75f; + } + .xterm-fg-150 { + color: #afd787; + } + .xterm-fg-151 { + color: #afd7af; + } + .xterm-fg-152 { + color: #afd7d7; + } + .xterm-fg-153 { + color: #afd7ff; + } + .xterm-fg-154 { + color: #afff00; + } + .xterm-fg-155 { + color: #afff5f; + } + .xterm-fg-156 { + color: #afff87; + } + .xterm-fg-157 { + color: #afffaf; + } + .xterm-fg-158 { + color: #afffd7; + } + .xterm-fg-159 { + color: #afffff; + } + .xterm-fg-160 { + color: #d70000; + } + .xterm-fg-161 { + color: #d7005f; + } + .xterm-fg-162 { + color: #d70087; + } + .xterm-fg-163 { + color: #d700af; + } + .xterm-fg-164 { + color: #d700d7; + } + .xterm-fg-165 { + color: #d700ff; + } + .xterm-fg-166 { + color: #d75f00; + } + .xterm-fg-167 { + color: #d75f5f; + } + .xterm-fg-168 { + color: #d75f87; + } + .xterm-fg-169 { + color: #d75faf; + } + .xterm-fg-170 { + color: #d75fd7; + } + .xterm-fg-171 { + color: #d75fff; + } + .xterm-fg-172 { + color: #d78700; + } + .xterm-fg-173 { + color: #d7875f; + } + .xterm-fg-174 { + color: #d78787; + } + .xterm-fg-175 { + color: #d787af; + } + .xterm-fg-176 { + color: #d787d7; + } + .xterm-fg-177 { + color: #d787ff; + } + .xterm-fg-178 { + color: #d7af00; + } + .xterm-fg-179 { + color: #d7af5f; + } + .xterm-fg-180 { + color: #d7af87; + } + .xterm-fg-181 { + color: #d7afaf; + } + .xterm-fg-182 { + color: #d7afd7; + } + .xterm-fg-183 { + color: #d7afff; + } + .xterm-fg-184 { + color: #d7d700; + } + .xterm-fg-185 { + color: #d7d75f; + } + .xterm-fg-186 { + color: #d7d787; + } + .xterm-fg-187 { + color: #d7d7af; + } + .xterm-fg-188 { + color: #d7d7d7; + } + .xterm-fg-189 { + color: #d7d7ff; + } + .xterm-fg-190 { + color: #d7ff00; + } + .xterm-fg-191 { + color: #d7ff5f; + } + .xterm-fg-192 { + color: #d7ff87; + } + .xterm-fg-193 { + color: #d7ffaf; + } + .xterm-fg-194 { + color: #d7ffd7; + } + .xterm-fg-195 { + color: #d7ffff; + } + .xterm-fg-196 { + color: #ff0000; + } + .xterm-fg-197 { + color: #ff005f; + } + .xterm-fg-198 { + color: #ff0087; + } + .xterm-fg-199 { + color: #ff00af; + } + .xterm-fg-200 { + color: #ff00d7; + } + .xterm-fg-201 { + color: #ff00ff; + } + .xterm-fg-202 { + color: #ff5f00; + } + .xterm-fg-203 { + color: #ff5f5f; + } + .xterm-fg-204 { + color: #ff5f87; + } + .xterm-fg-205 { + color: #ff5faf; + } + .xterm-fg-206 { + color: #ff5fd7; + } + .xterm-fg-207 { + color: #ff5fff; + } + .xterm-fg-208 { + color: #ff8700; + } + .xterm-fg-209 { + color: #ff875f; + } + .xterm-fg-210 { + color: #ff8787; + } + .xterm-fg-211 { + color: #ff87af; + } + .xterm-fg-212 { + color: #ff87d7; + } + .xterm-fg-213 { + color: #ff87ff; + } + .xterm-fg-214 { + color: #ffaf00; + } + .xterm-fg-215 { + color: #ffaf5f; + } + .xterm-fg-216 { + color: #ffaf87; + } + .xterm-fg-217 { + color: #ffafaf; + } + .xterm-fg-218 { + color: #ffafd7; + } + .xterm-fg-219 { + color: #ffafff; + } + .xterm-fg-220 { + color: #ffd700; + } + .xterm-fg-221 { + color: #ffd75f; + } + .xterm-fg-222 { + color: #ffd787; + } + .xterm-fg-223 { + color: #ffd7af; + } + .xterm-fg-224 { + color: #ffd7d7; + } + .xterm-fg-225 { + color: #ffd7ff; + } + .xterm-fg-226 { + color: #ffff00; + } + .xterm-fg-227 { + color: #ffff5f; + } + .xterm-fg-228 { + color: #ffff87; + } + .xterm-fg-229 { + color: #ffffaf; + } + .xterm-fg-230 { + color: #ffffd7; + } + .xterm-fg-231 { + color: #ffffff; + } + .xterm-fg-232 { + color: #080808; + } + .xterm-fg-233 { + color: #121212; + } + .xterm-fg-234 { + color: #1c1c1c; + } + .xterm-fg-235 { + color: #262626; + } + .xterm-fg-236 { + color: #303030; + } + .xterm-fg-237 { + color: #3a3a3a; + } + .xterm-fg-238 { + color: #444444; + } + .xterm-fg-239 { + color: #4e4e4e; + } + .xterm-fg-240 { + color: #585858; + } + .xterm-fg-241 { + color: #626262; + } + .xterm-fg-242 { + color: #6c6c6c; + } + .xterm-fg-243 { + color: #767676; + } + .xterm-fg-244 { + color: #808080; + } + .xterm-fg-245 { + color: #8a8a8a; + } + .xterm-fg-246 { + color: #949494; + } + .xterm-fg-247 { + color: #9e9e9e; + } + .xterm-fg-248 { + color: #a8a8a8; + } + .xterm-fg-249 { + color: #b2b2b2; + } + .xterm-fg-250 { + color: #bcbcbc; + } + .xterm-fg-251 { + color: #c6c6c6; + } + .xterm-fg-252 { + color: #d0d0d0; + } + .xterm-fg-253 { + color: #dadada; + } + .xterm-fg-254 { + color: #e4e4e4; + } + .xterm-fg-255 { + color: #eeeeee; + } } diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb index 28fad3671f7..9338b37e678 100644 --- a/app/controllers/ci/builds_controller.rb +++ b/app/controllers/ci/builds_controller.rb @@ -7,6 +7,7 @@ module Ci before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel] before_filter :authorize_manage_builds!, only: [:retry, :cancel] before_filter :build, except: [:show] + layout 'ci/project' def show if params[:id] =~ /\A\d+\Z/ diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb index 0f7f5485661..f0c0ff1bc11 100644 --- a/app/controllers/ci/commits_controller.rb +++ b/app/controllers/ci/commits_controller.rb @@ -6,6 +6,7 @@ module Ci before_filter :authorize_access_project!, except: [:status, :show, :cancel] before_filter :authorize_manage_builds!, only: [:cancel] before_filter :commit, only: :show + layout 'ci/project' def show @builds = @commit.builds diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index db8926e30d3..1a07feeb20e 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -1,5 +1,5 @@ %h4.page-title - = link_to ci_project_path(@project) + = link_to @project.name, ci_project_path(@project) @ = @commit.short_sha diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 9b597b45aa5..72fda8fe949 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -63,7 +63,7 @@ %i.fa.fa-time #{time_interval_in_words @commit.duration} -%table.builds +%table.table.builds %thead %tr %th Status @@ -81,7 +81,7 @@ %h3 Retried builds - %table.builds + %table.table.builds %thead %tr %th Status diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml index b79ab957ba8..6443378af99 100644 --- a/app/views/ci/projects/show.html.haml +++ b/app/views/ci/projects/show.html.haml @@ -16,7 +16,8 @@ %li{class: 'active'} = link_to @ref, ci_project_path(@project, ref: @ref) - + %li.pull-right + = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' ) - if @ref %p @@ -37,7 +38,7 @@ -%table.builds +%table.table.builds %thead %tr %th Status diff --git a/app/views/layouts/ci/_head.html.haml b/app/views/layouts/ci/_head.html.haml deleted file mode 100644 index 871752c9812..00000000000 --- a/app/views/layouts/ci/_head.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -%head - %meta{charset: "utf-8"} - %meta{content: "GitLab Continuous Integration", name: "description"} - %title GitLab CI - = stylesheet_link_tag "ci/application", :media => "all" - = javascript_include_tag "ci/application" - = csrf_meta_tags - = favicon_link_tag 'ci/favicon.ico' - :erb - - diff --git a/app/views/layouts/ci/_nav.html.haml b/app/views/layouts/ci/_nav.html.haml deleted file mode 100644 index 3d2c7ce06da..00000000000 --- a/app/views/layouts/ci/_nav.html.haml +++ /dev/null @@ -1,32 +0,0 @@ -.navbar.navbar-static-top.navbar-ci - .container - .navbar-header - %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} - %span.sr-only Toggle navigation - %i.fa.fa-reorder - - = link_to 'GitLab CI', ci_root_path, class: "navbar-brand" - - .collapse.navbar-collapse - %ul.nav.navbar-nav - - if current_user && current_user.is_admin? - %li - = link_to ci_admin_projects_path do - Admin - %li - = link_to 'Help', ci_help_path - - %ul.nav.navbar-nav.pull-right - - if current_user - %li - = link_to "/profile", no_turbolink do - .profile-holder - = image_tag user_avatar_url(current_user, 64), class: 'avatar s32', alt: '' - %span= current_user.name - %li - = link_to destroy_user_session_path, class: "logout", method: :delete do - %i.fa.fa-signout - Logout - - else - %li - = link_to "Login with GitLab", auth_ci_user_sessions_path, no_turbolink.merge(class: 'btn btn-success btn-login') diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index 26eaf4db0e5..7d65405740c 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -1,4 +1,11 @@ -%ul.nav.nav-pills.nav-stacked.admin-menu +%ul.nav.nav-sidebar + = nav_link do + = link_to ci_root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = icon('caret-square-o-left fw') + %span + Back to Dashboard + + %li.separate-item = nav_link path: 'projects#index' do = link_to ci_admin_projects_path do %i.fa.fa-list-alt @@ -19,8 +26,6 @@ Builds %small.pull-right = Ci::Build.count(:all) - %li - %hr = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to ci_admin_application_settings_path do %i.fa.fa-cogs diff --git a/app/views/layouts/ci/_nav_dashboard.html.haml b/app/views/layouts/ci/_nav_dashboard.html.haml new file mode 100644 index 00000000000..202d73d1713 --- /dev/null +++ b/app/views/layouts/ci/_nav_dashboard.html.haml @@ -0,0 +1,24 @@ +%ul.nav.nav-sidebar + = nav_link do + = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = icon('caret-square-o-left fw') + %span + Back to GitLab + %li.separate-item + = nav_link path: 'projects#show' do + = link_to ci_root_path do + %i.fa.fa-home + %span + Projects + - if current_user && current_user.is_admin? + %li + = link_to ci_admin_projects_path do + %i.fa.fa-cogs + %span + Admin + %li + = link_to ci_help_path do + %i.fa.fa-info + %span + Help + diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index d5b66b92fe8..9cef47eb4f7 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,40 +1,48 @@ -%ul.nav.nav-pills.nav-stacked.project-menu +%ul.nav.nav-sidebar = nav_link path: 'projects#show' do = link_to ci_project_path(@project) do %i.fa.fa-list-alt - Commits - %small.pull-right= @project.commits.count + %span + Commits + %small.pull-right= @project.commits.count = nav_link path: 'charts#show' do = link_to ci_project_charts_path(@project) do %i.fa.fa-bar-chart - Charts + %span + Charts = nav_link path: ['runners#index', 'runners#show'] do = link_to ci_project_runners_path(@project) do %i.fa.fa-cog - Runners + %span + Runners = nav_link path: 'variables#show' do = link_to ci_project_variables_path(@project) do %i.fa.fa-code - Variables + %span + Variables = nav_link path: 'web_hooks#index' do = link_to ci_project_web_hooks_path(@project) do %i.fa.fa-link - Web Hooks + %span + Web Hooks = nav_link path: 'triggers#index' do = link_to ci_project_triggers_path(@project) do %i.fa.fa-retweet - Triggers + %span + Triggers = nav_link path: 'services#index' do = link_to ci_project_services_path(@project) do %i.fa.fa-share - Services + %span + Services = nav_link path: 'events#index' do = link_to ci_project_events_path(@project) do %i.fa.fa-book - Events - %li - %hr + %span + Events + %li.separate-item = nav_link path: 'projects#edit' do = link_to edit_ci_project_path(@project) do %i.fa.fa-cogs - Settings + %span + Settings diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml new file mode 100644 index 00000000000..c598f63c4c8 --- /dev/null +++ b/app/views/layouts/ci/_page.html.haml @@ -0,0 +1,26 @@ +.page-with-sidebar{ class: nav_sidebar_class } + = render "layouts/broadcast" + .sidebar-wrapper.nicescroll + .header-logo + = link_to ci_root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do + = brand_header_logo + .gitlab-text-container + %h3 GitLab CI + - if defined?(sidebar) && sidebar + = render "layouts/ci/#{sidebar}" + - elsif current_user + = render 'layouts/nav/dashboard' + .collapse-nav + = render partial: 'layouts/collapse_button' + - if current_user + = link_to current_user, class: 'sidebar-user' do + = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36' + .username + = current_user.username + .content-wrapper + = render "layouts/flash" + = render 'layouts/ci/info' + %div{ class: container_class } + .content + .clearfix + = yield diff --git a/app/views/layouts/ci/admin.html.haml b/app/views/layouts/ci/admin.html.haml index cd160a54455..c8cb185d28c 100644 --- a/app/views/layouts/ci/admin.html.haml +++ b/app/views/layouts/ci/admin.html.haml @@ -1,18 +1,11 @@ !!! 5 %html{ lang: "en"} - = render 'layouts/ci/head' - %body{ :'data-page' => body_data_page } - = render 'layouts/ci/nav' - = render 'layouts/ci/info' - - if content_for?(:title) - .container.container-title - = yield(:title) - %hr + = render 'layouts/head' + %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} + - header_title = "Admin area" + - if current_user + = render "layouts/header/default", title: header_title + - else + = render "layouts/header/public", title: header_title - .container.container-body - .content - .row - .col-md-2.append-bottom-20 - = render 'layouts/ci/nav_admin' - .col-md-10 - = yield + = render 'layouts/ci/page', sidebar: 'nav_admin' diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml index 8990ffbffe6..7fb1a0097b2 100644 --- a/app/views/layouts/ci/application.html.haml +++ b/app/views/layouts/ci/application.html.haml @@ -1,14 +1,11 @@ !!! 5 %html{ lang: "en"} - = render 'layouts/ci/head' - %body{ :'data-page' => body_data_page } - = render 'layouts/ci/nav' - = render 'layouts/ci/info' - - if content_for?(:title) - .container.container-title - = yield(:title) - %hr + = render 'layouts/head' + %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} + - header_title = "Projects" + - if current_user + = render "layouts/header/default", title: header_title + - else + = render "layouts/header/public", title: header_title - .container.container-body - .content - = yield + = render 'layouts/ci/page', sidebar: 'nav_dashboard' diff --git a/app/views/layouts/ci/empty.html.haml b/app/views/layouts/ci/empty.html.haml deleted file mode 100644 index bda574c0ed1..00000000000 --- a/app/views/layouts/ci/empty.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/ci/head' - %body{ :'data-page' => body_data_page } - = render 'layouts/ci/info' - - if content_for?(:title) - .container.container-title - = yield(:title) - %hr - - .container.container-body - .content - = yield - diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index 88c21211a57..23a4928fcc7 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -1,27 +1,11 @@ !!! 5 %html{ lang: "en"} - = render 'layouts/ci/head' - %body{ :'data-page' => body_data_page } - = render 'layouts/ci/nav' - = render 'layouts/ci/info' - .container - %h3.project-title - = @project.name - - if @project.public - %small - %i.fa.fa-globe - Public + = render 'layouts/head' + %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} + - header_title = @project.name + - if current_user + = render "layouts/header/default", title: header_title + - else + = render "layouts/header/public", title: header_title - .pull-right - = link_to 'View on GitLab', @project.gitlab_url, no_turbolink.merge( class: 'btn btn-sm' ) - %hr - .container.container-body - .content - - if current_user && can?(current_user, :admin_project, gl_project) - .row - .col-md-2.append-bottom-20 - = render 'layouts/ci/nav_project' - .col-md-10 - = yield - - else - = yield + = render 'layouts/ci/page', sidebar: 'nav_project' -- cgit v1.2.1 From 15eeae5fa8f10442617d2edd8fc3d8f5b3f334db Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Sep 2015 19:59:39 +0200 Subject: Fix 500 when search for gitlab projects --- app/assets/javascripts/ci/pager.js.coffee | 6 +++--- app/controllers/ci/projects_controller.rb | 2 +- app/views/ci/projects/index.html.haml | 9 ++------- app/views/layouts/ci/application.html.haml | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/ci/pager.js.coffee b/app/assets/javascripts/ci/pager.js.coffee index b57e7c736e9..226fbd654ab 100644 --- a/app/assets/javascripts/ci/pager.js.coffee +++ b/app/assets/javascripts/ci/pager.js.coffee @@ -16,7 +16,7 @@ complete: => $(".loading").hide() success: (data) => - Pager.append(data.count, data.html) + CiPager.append(data.count, data.html) dataType: "json" append: (count, html) -> @@ -34,9 +34,9 @@ fireDelay: 1000 fireOnce: true ceaseFire: -> - Pager.disable + CiPager.disable callback: (i) => unless $(".loading").is(':visible') $(".loading").show() - Pager.getItems() + CiPager.getItems() diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 9074972e94a..454810ca01f 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -22,7 +22,7 @@ module Ci @page = @offset == 0 ? 1 : (@offset / @limit + 1) @gl_projects = current_user.authorized_projects - @gl_projects = @gl_projects.where("name LIKE %?%", params[:search]) if params[:search] + @gl_projects = @gl_projects.where("name LIKE ?", "%#{params[:search]}%") if params[:search] @gl_projects = @gl_projects.page(@page).per(@limit) @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 99d07329af0..4c74610a575 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -1,10 +1,5 @@ - if current_user - = content_for :title do - %h3.project-title - Dashboard - .pull-right - = render "search" - + = render "search" .projects %p.fetch-status.light %i.fa.fa-refresh.fa-spin @@ -12,6 +7,6 @@ $.get '#{gitlab_ci_projects_path}', (data) -> $(".projects").html data.html CiPager.init "#{gitlab_ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false - + - else = render 'public' diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml index 7fb1a0097b2..b9f871d5447 100644 --- a/app/views/layouts/ci/application.html.haml +++ b/app/views/layouts/ci/application.html.haml @@ -2,7 +2,7 @@ %html{ lang: "en"} = render 'layouts/head' %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = "Projects" + - header_title = "CI Projects" - if current_user = render "layouts/header/default", title: header_title - else -- cgit v1.2.1 From ad5d2c3e78e56cb00f608020d1b3a7980f4ac6f4 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 14 Sep 2015 11:15:54 +0300 Subject: fix of API --- config/initializers/1_settings.rb | 2 +- lib/ci/api/entities.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 3893bd45cf5..339419559d1 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -180,7 +180,7 @@ Settings['gitlab_ci'] ||= Settingslogic.new({}) Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) -Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root + '/ci') +Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) # # Reply by email diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 2f0e9d36bc4..f5e601d4016 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -12,8 +12,12 @@ module Ci end class Build < Grape::Entity - expose :id, :commands, :path, :ref, :sha, :project_id, :repo_url, - :before_sha, :timeout, :allow_git_fetch, :project_name, :options + expose :id, :commands, :ref, :sha, :project_id, :repo_url, + :before_sha, :allow_git_fetch, :project_name, :options + + expose :timeout do |model| + model.timeout + end expose :variables end -- cgit v1.2.1 From 6014019ed49295178ebd0c4ce5f6f1d210219420 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 14 Sep 2015 12:06:52 +0300 Subject: monkey patch of Hash --- config/initializers/hash_patch.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 config/initializers/hash_patch.rb diff --git a/config/initializers/hash_patch.rb b/config/initializers/hash_patch.rb new file mode 100644 index 00000000000..9397d0d1829 --- /dev/null +++ b/config/initializers/hash_patch.rb @@ -0,0 +1,15 @@ +# We need this patch because of json format error in the CI API: +# IOError (not opened for reading) +# Details: http://stackoverflow.com/questions/19808921/upgrade-to-rails-4-got-ioerror-not-opened-for-reading +# It happens because of ActiveSupport's monkey patch of json formatters + +if defined?(ActiveSupport::JSON) + Hash.class_eval do + def to_json(*args) + super(args) + end + def as_json(*args) + super(args) + end + end +end \ No newline at end of file -- cgit v1.2.1 From 16e44ad7fcbbceb0b220dd88dba197b1db797498 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 11:27:05 +0200 Subject: Fix IOError when fetching a new build by runner --- lib/ci/api/entities.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index f5e601d4016..1277d68a364 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -11,9 +11,16 @@ module Ci expose :builds end + class BuildOptions < Grape::Entity + expose :image + expose :services + end + class Build < Grape::Entity expose :id, :commands, :ref, :sha, :project_id, :repo_url, - :before_sha, :allow_git_fetch, :project_name, :options + :before_sha, :allow_git_fetch, :project_name + + expose :options, using: BuildOptions expose :timeout do |model| model.timeout -- cgit v1.2.1 From 95037c9c559dae1d4482a2003ca2f0f778678d09 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 11:27:30 +0200 Subject: Revert "monkey patch of Hash" This reverts commit 6014019ed49295178ebd0c4ce5f6f1d210219420. --- config/initializers/hash_patch.rb | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 config/initializers/hash_patch.rb diff --git a/config/initializers/hash_patch.rb b/config/initializers/hash_patch.rb deleted file mode 100644 index 9397d0d1829..00000000000 --- a/config/initializers/hash_patch.rb +++ /dev/null @@ -1,15 +0,0 @@ -# We need this patch because of json format error in the CI API: -# IOError (not opened for reading) -# Details: http://stackoverflow.com/questions/19808921/upgrade-to-rails-4-got-ioerror-not-opened-for-reading -# It happens because of ActiveSupport's monkey patch of json formatters - -if defined?(ActiveSupport::JSON) - Hash.class_eval do - def to_json(*args) - super(args) - end - def as_json(*args) - super(args) - end - end -end \ No newline at end of file -- cgit v1.2.1 From 4c53cc0ebac36560d806732ff1fefba9206c75f3 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 14 Sep 2015 14:37:18 +0300 Subject: rubocop satisfy --- app/controllers/ci/admin/runners_controller.rb | 2 +- app/controllers/ci/builds_controller.rb | 14 ++++----- app/controllers/ci/charts_controller.rb | 8 ++--- app/controllers/ci/commits_controller.rb | 12 +++---- app/controllers/ci/events_controller.rb | 6 ++-- app/controllers/ci/lints_controller.rb | 2 +- app/controllers/ci/projects_controller.rb | 14 ++++----- app/controllers/ci/runner_projects_controller.rb | 6 ++-- app/controllers/ci/runners_controller.rb | 10 +++--- app/controllers/ci/services_controller.rb | 10 +++--- app/controllers/ci/triggers_controller.rb | 8 ++--- app/controllers/ci/variables_controller.rb | 8 ++--- app/controllers/ci/web_hooks_controller.rb | 8 ++--- lib/ci/backup/database.rb | 36 ++++++++++----------- spec/controllers/ci/projects_controller_spec.rb | 18 +++++------ spec/factories/ci/commits.rb | 4 +-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 40 ++++++++++++------------ spec/models/ci/build_spec.rb | 26 +++++++-------- spec/models/ci/mail_service_spec.rb | 16 +++++----- spec/models/ci/project_spec.rb | 16 +++++----- spec/requests/ci/api/builds_spec.rb | 20 ++++++------ spec/requests/ci/api/commits_spec.rb | 8 ++--- spec/requests/ci/api/forks_spec.rb | 12 +++---- spec/requests/ci/api/projects_spec.rb | 16 +++++----- spec/requests/ci/api/runners_spec.rb | 12 +++---- spec/requests/ci/api/triggers_spec.rb | 8 ++--- spec/services/ci/create_commit_service_spec.rb | 12 +++---- spec/support/stub_gitlab_calls.rb | 28 ++++++++--------- 28 files changed, 190 insertions(+), 190 deletions(-) diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb index 4f5f3776ddc..2aabe21ed66 100644 --- a/app/controllers/ci/admin/runners_controller.rb +++ b/app/controllers/ci/admin/runners_controller.rb @@ -1,6 +1,6 @@ module Ci class Admin::RunnersController < Ci::Admin::ApplicationController - before_filter :runner, except: :index + before_action :runner, except: :index def index @runners = Ci::Runner.order('id DESC') diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb index 9338b37e678..fbfb02af4fb 100644 --- a/app/controllers/ci/builds_controller.rb +++ b/app/controllers/ci/builds_controller.rb @@ -1,12 +1,12 @@ module Ci class BuildsController < Ci::ApplicationController - before_filter :authenticate_user!, except: [:status, :show] - before_filter :authenticate_public_page!, only: :show - before_filter :project - before_filter :authorize_access_project!, except: [:status, :show] - before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel] - before_filter :authorize_manage_builds!, only: [:retry, :cancel] - before_filter :build, except: [:show] + before_action :authenticate_user!, except: [:status, :show] + before_action :authenticate_public_page!, only: :show + before_action :project + before_action :authorize_access_project!, except: [:status, :show] + before_action :authorize_manage_project!, except: [:status, :show, :retry, :cancel] + before_action :authorize_manage_builds!, only: [:retry, :cancel] + before_action :build, except: [:show] layout 'ci/project' def show diff --git a/app/controllers/ci/charts_controller.rb b/app/controllers/ci/charts_controller.rb index 63326ef36cc..aa875e70987 100644 --- a/app/controllers/ci/charts_controller.rb +++ b/app/controllers/ci/charts_controller.rb @@ -1,9 +1,9 @@ module Ci class ChartsController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_access_project! - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_access_project! + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb index f0c0ff1bc11..e41f3487de4 100644 --- a/app/controllers/ci/commits_controller.rb +++ b/app/controllers/ci/commits_controller.rb @@ -1,11 +1,11 @@ module Ci class CommitsController < Ci::ApplicationController - before_filter :authenticate_user!, except: [:status, :show] - before_filter :authenticate_public_page!, only: :show - before_filter :project - before_filter :authorize_access_project!, except: [:status, :show, :cancel] - before_filter :authorize_manage_builds!, only: [:cancel] - before_filter :commit, only: :show + before_action :authenticate_user!, except: [:status, :show] + before_action :authenticate_public_page!, only: :show + before_action :project + before_action :authorize_access_project!, except: [:status, :show, :cancel] + before_action :authorize_manage_builds!, only: [:cancel] + before_action :commit, only: :show layout 'ci/project' def show diff --git a/app/controllers/ci/events_controller.rb b/app/controllers/ci/events_controller.rb index c515caabe63..89b784a1e89 100644 --- a/app/controllers/ci/events_controller.rb +++ b/app/controllers/ci/events_controller.rb @@ -2,9 +2,9 @@ module Ci class EventsController < Ci::ApplicationController EVENTS_PER_PAGE = 50 - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index 62c2ba86e86..a81e4e319ff 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -1,6 +1,6 @@ module Ci class LintsController < Ci::ApplicationController - before_filter :authenticate_user! + before_action :authenticate_user! def show end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 454810ca01f..6483a84ee91 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -2,13 +2,13 @@ module Ci class ProjectsController < Ci::ApplicationController PROJECTS_BATCH = 100 - before_filter :authenticate_user!, except: [:build, :badge, :index, :show] - before_filter :authenticate_public_page!, only: :show - before_filter :project, only: [:build, :integration, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] - before_filter :authorize_access_project!, except: [:build, :gitlab, :badge, :index, :show, :new, :create] - before_filter :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml] - before_filter :authenticate_token!, only: [:build] - before_filter :no_cache, only: [:badge] + before_action :authenticate_user!, except: [:build, :badge, :index, :show] + before_action :authenticate_public_page!, only: :show + before_action :project, only: [:build, :integration, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_action :authorize_access_project!, except: [:build, :gitlab, :badge, :index, :show, :new, :create] + before_action :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_action :authenticate_token!, only: [:build] + before_action :no_cache, only: [:badge] protect_from_forgery except: :build layout 'ci/project', except: [:index, :gitlab] diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb index 3a52087cc6b..5365f51082f 100644 --- a/app/controllers/ci/runner_projects_controller.rb +++ b/app/controllers/ci/runner_projects_controller.rb @@ -1,8 +1,8 @@ module Ci class RunnerProjectsController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/runners_controller.rb b/app/controllers/ci/runners_controller.rb index 0e9d576a15b..a672370302b 100644 --- a/app/controllers/ci/runners_controller.rb +++ b/app/controllers/ci/runners_controller.rb @@ -1,10 +1,10 @@ module Ci class RunnersController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] - before_filter :authorize_access_project! - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :authorize_access_project! + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/services_controller.rb b/app/controllers/ci/services_controller.rb index e99f40f3a0a..1495223786d 100644 --- a/app/controllers/ci/services_controller.rb +++ b/app/controllers/ci/services_controller.rb @@ -1,10 +1,10 @@ module Ci class ServicesController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_access_project! - before_filter :authorize_manage_project! - before_filter :service, only: [:edit, :update, :test] + before_action :authenticate_user! + before_action :project + before_action :authorize_access_project! + before_action :authorize_manage_project! + before_action :service, only: [:edit, :update, :test] respond_to :html diff --git a/app/controllers/ci/triggers_controller.rb b/app/controllers/ci/triggers_controller.rb index 6ba37cd843e..a39cc5d3a56 100644 --- a/app/controllers/ci/triggers_controller.rb +++ b/app/controllers/ci/triggers_controller.rb @@ -1,9 +1,9 @@ module Ci class TriggersController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_access_project! - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_access_project! + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/variables_controller.rb b/app/controllers/ci/variables_controller.rb index 6908e0877f0..9c6c775fde8 100644 --- a/app/controllers/ci/variables_controller.rb +++ b/app/controllers/ci/variables_controller.rb @@ -1,9 +1,9 @@ module Ci class VariablesController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_access_project! - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_access_project! + before_action :authorize_manage_project! layout 'ci/project' diff --git a/app/controllers/ci/web_hooks_controller.rb b/app/controllers/ci/web_hooks_controller.rb index eea4842c91c..24074a6d9ac 100644 --- a/app/controllers/ci/web_hooks_controller.rb +++ b/app/controllers/ci/web_hooks_controller.rb @@ -1,9 +1,9 @@ module Ci class WebHooksController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :project - before_filter :authorize_access_project! - before_filter :authorize_manage_project! + before_action :authenticate_user! + before_action :project + before_action :authorize_access_project! + before_action :authorize_manage_project! layout 'ci/project' diff --git a/lib/ci/backup/database.rb b/lib/ci/backup/database.rb index f7fa3f1833a..3f2277024e4 100644 --- a/lib/ci/backup/database.rb +++ b/lib/ci/backup/database.rb @@ -13,13 +13,13 @@ module Ci def dump success = case config["adapter"] - when /^mysql/ then - $progress.print "Dumping MySQL database #{config['database']} ... " - system('mysqldump', *mysql_args, config['database'], out: db_file_name) - when "postgresql" then - $progress.print "Dumping PostgreSQL database #{config['database']} ... " - pg_env - system('pg_dump', config['database'], out: db_file_name) + when /^mysql/ then + $progress.print "Dumping MySQL database #{config['database']} ... " + system('mysqldump', *mysql_args, config['database'], out: db_file_name) + when "postgresql" then + $progress.print "Dumping PostgreSQL database #{config['database']} ... " + pg_env + system('pg_dump', config['database'], out: db_file_name) end report_success(success) abort 'Backup failed' unless success @@ -27,17 +27,17 @@ module Ci def restore success = case config["adapter"] - when /^mysql/ then - $progress.print "Restoring MySQL database #{config['database']} ... " - system('mysql', *mysql_args, config['database'], in: db_file_name) - when "postgresql" then - $progress.print "Restoring PostgreSQL database #{config['database']} ... " - # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE - # statements like MySQL. - drop_all_tables - drop_all_postgres_sequences - pg_env - system('psql', config['database'], '-f', db_file_name) + when /^mysql/ then + $progress.print "Restoring MySQL database #{config['database']} ... " + system('mysql', *mysql_args, config['database'], in: db_file_name) + when "postgresql" then + $progress.print "Restoring PostgreSQL database #{config['database']} ... " + # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE + # statements like MySQL. + drop_all_tables + drop_all_postgres_sequences + pg_env + system('psql', config['database'], '-f', db_file_name) end report_success(success) abort 'Restore failed' unless success diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index 563064b0cef..f710e2a3808 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -8,12 +8,12 @@ describe Ci::ProjectsController do describe "POST #build" do it 'should respond 200 if params is ok' do post :build, id: @project.id, - ref: 'master', - before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', - after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', - token: @project.token, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', + token: @project.token, ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] + commits: [ { message: "Message" } ] expect(response).to be_success @@ -22,10 +22,10 @@ describe Ci::ProjectsController do it 'should respond 400 if push about removed branch' do post :build, id: @project.id, - ref: 'master', - before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', - after: '0000000000000000000000000000000000000000', - token: @project.token, + ref: 'master', + before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', + after: '0000000000000000000000000000000000000000', + token: @project.token, ci_yaml_file: gitlab_ci_yaml expect(response).not_to be_success diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index c1d42b607c3..70930c789c3 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -60,14 +60,14 @@ FactoryGirl.define do factory :ci_commit_with_one_job do after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }}) + commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" } }) commit.save end end factory :ci_commit_with_two_jobs do after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({rspec: { script: "ls" }, spinach: { script: "ls" }}) + commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) commit.save end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index efc05676676..c99add3f716 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -131,7 +131,7 @@ describe Ci::GitlabCiYamlProcessor do image: "ruby:2.1", services: ["mysql"], before_script: ["pwd"], - rspec: {image: "ruby:2.5", services: ["postgresql"], script: "rspec"} + rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -176,133 +176,133 @@ describe Ci::GitlabCiYamlProcessor do end it "returns errors if tags parameter is invalid" do - config = YAML.dump({rspec: {script: "test", tags: "mysql"}}) + config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") end it "returns errors if before_script parameter is invalid" do - config = YAML.dump({before_script: "bundle update", rspec: {script: "test"}}) + config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end it "returns errors if image parameter is invalid" do - config = YAML.dump({image: ["test"], rspec: {script: "test"}}) + config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") end it "returns errors if job image parameter is invalid" do - config = YAML.dump({rspec: {script: "test", image: ["test"]}}) + config = YAML.dump({rspec: { script: "test", image: ["test"] } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") end it "returns errors if services parameter is not an array" do - config = YAML.dump({services: "test", rspec: {script: "test"}}) + config = YAML.dump({ services: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if services parameter is not an array of strings" do - config = YAML.dump({services: [10, "test"], rspec: {script: "test"}}) + config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if job services parameter is not an array" do - config = YAML.dump({rspec: {script: "test", services: "test"}}) + config = YAML.dump({ rspec: { script: "test", services: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if job services parameter is not an array of strings" do - config = YAML.dump({rspec: {script: "test", services: [10, "test"]}}) + config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if there are unknown parameters" do - config = YAML.dump({extra: "bundle update"}) + config = YAML.dump({ extra: "bundle update" }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do - config = YAML.dump({extra: {services: "test"}}) + config = YAML.dump({ extra: {services: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there is no any jobs defined" do - config = YAML.dump({before_script: ["bundle update"]}) + config = YAML.dump({ before_script: ["bundle update"] }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") end it "returns errors if job allow_failure parameter is not an boolean" do - config = YAML.dump({rspec: {script: "test", allow_failure: "string"}}) + config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") end it "returns errors if job stage is not a string" do - config = YAML.dump({rspec: {script: "test", type: 1, allow_failure: "string"}}) + config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + config = YAML.dump({rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do - config = YAML.dump({types: ["build", "test"], rspec: {script: "test", type: "acceptance", allow_failure: "string"}}) + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") end it "returns errors if stages is not an array" do - config = YAML.dump({types: "test", rspec: {script: "test"}}) + config = YAML.dump({ types: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if stages is not an array of strings" do - config = YAML.dump({types: [true, "test"], rspec: {script: "test"}}) + config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if variables is not a map" do - config = YAML.dump({variables: "test", rspec: {script: "test"}}) + config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end it "returns errors if variables is not a map of key-valued strings" do - config = YAML.dump({variables: {test: false}, rspec: {script: "test"}}) + config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index b62c5862c0c..4f57003565a 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -211,14 +211,14 @@ describe Ci::Build do end describe :options do - let(:options) { + let(:options) do { - :image => "ruby:2.1", - :services => [ + image: "ruby:2.1", + services: [ "postgres" ] } - } + end subject { build.options } it { is_expected.to eq(options) } @@ -308,20 +308,20 @@ describe Ci::Build do context 'returns variables' do subject { build.variables } - let(:variables) { + let(:variables) do [ - {key: :DB_NAME, value: 'postgres', public: true} + { key: :DB_NAME, value: 'postgres', public: true } ] - } + end it { is_expected.to eq(variables) } context 'and secure variables' do - let(:secure_variables) { + let(:secure_variables) do [ - {key: 'SECRET_KEY', value: 'secret_value', public: false} + { key: 'SECRET_KEY', value: 'secret_value', public: false } ] - } + end before do build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') @@ -332,11 +332,11 @@ describe Ci::Build do context 'and trigger variables' do let(:trigger) { FactoryGirl.create :ci_trigger, project: project } let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger } - let(:trigger_variables) { + let(:trigger_variables) do [ - {key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false} + { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false } ] - } + end before do build.trigger_request = trigger_request diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index e3f326d783b..51511641afc 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -75,12 +75,12 @@ describe Ci::MailService do end describe 'successfull build and project has email_recipients' do - let(:project) { + let(:project) do FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false, email_recipients: "jeroen@example.com") - } + end let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } @@ -103,12 +103,12 @@ describe Ci::MailService do end describe 'successful build and notify only broken builds' do - let(:project) { + let(:project) do FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: true, email_recipients: "jeroen@example.com") - } + end let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } @@ -131,12 +131,12 @@ describe Ci::MailService do end describe 'successful build and can test service' do - let(:project) { + let(:project) do FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false, email_recipients: "jeroen@example.com") - } + end let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } @@ -153,12 +153,12 @@ describe Ci::MailService do end describe 'retried build should not receive email' do - let(:project) { + let(:project) do FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: true, email_recipients: "jeroen@example.com") - } + end let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 1be276a9ef8..48f76e11ce9 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -94,33 +94,33 @@ describe Ci::Project do end describe '#broken_or_success?' do - it { + it do project = FactoryGirl.create :ci_project, email_add_pusher: true allow(project).to receive(:broken?).and_return(true) allow(project).to receive(:success?).and_return(true) expect(project.broken_or_success?).to eq(true) - } + end - it { + it do project = FactoryGirl.create :ci_project, email_add_pusher: true allow(project).to receive(:broken?).and_return(true) allow(project).to receive(:success?).and_return(false) expect(project.broken_or_success?).to eq(true) - } + end - it { + it do project = FactoryGirl.create :ci_project, email_add_pusher: true allow(project).to receive(:broken?).and_return(false) allow(project).to receive(:success?).and_return(true) expect(project.broken_or_success?).to eq(true) - } + end - it { + it do project = FactoryGirl.create :ci_project, email_add_pusher: true allow(project).to receive(:broken?).and_return(false) allow(project).to receive(:success?).and_return(false) expect(project.broken_or_success?).to eq(false) - } + end end describe 'Project.parse' do diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c416ca98e1f..61f9d940c3b 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -20,7 +20,7 @@ describe Ci::API::API do commit.create_builds build = commit.builds.first - post api("/builds/register"), token: runner.token, info: {platform: :darwin} + post api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response['sha']).to eq(build.sha) @@ -55,10 +55,10 @@ describe Ci::API::API do commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds - post api("/builds/register"), token: runner.token, info: {platform: :darwin} + post api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) - expect(json_response["options"]).to eq({"image" => "ruby:2.1", "services" => ["postgres"]}) + expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] }) end it "returns variables" do @@ -66,12 +66,12 @@ describe Ci::API::API do commit.create_builds project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") - post api("/builds/register"), token: runner.token, info: {platform: :darwin} + post api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ - {"key" => "DB_NAME", "value" => "postgres", "public" => true}, - {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, + { "key" => "DB_NAME", "value" => "postgres", "public" => true }, + { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, ]) end @@ -83,13 +83,13 @@ describe Ci::API::API do commit.create_builds(trigger_request) project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") - post api("/builds/register"), token: runner.token, info: {platform: :darwin} + post api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ - {"key" => "DB_NAME", "value" => "postgres", "public" => true}, - {"key" => "SECRET_KEY", "value" => "secret_value", "public" => false}, - {"key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false}, + { "key" => "DB_NAME", "value" => "postgres", "public" => true }, + { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, + { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false }, ]) end end diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index 2ead68e2290..a4c2a507e88 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -6,12 +6,12 @@ describe Ci::API::API, 'Commits' do let(:project) { FactoryGirl.create(:ci_project) } let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:options) { + let(:options) do { project_token: project.token, project_id: project.id } - } + end describe "GET /commits" do before { commit } @@ -27,7 +27,7 @@ describe Ci::API::API, 'Commits' do end describe "POST /commits" do - let(:data) { + let(:data) do { "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", @@ -46,7 +46,7 @@ describe Ci::API::API, 'Commits' do ], ci_yaml_file: gitlab_ci_yaml } - } + end it "should create a build" do post api("/commits"), options.merge(data: data) diff --git a/spec/requests/ci/api/forks_spec.rb b/spec/requests/ci/api/forks_spec.rb index 27b9d045c8c..6f5dc0bc1d9 100644 --- a/spec/requests/ci/api/forks_spec.rb +++ b/spec/requests/ci/api/forks_spec.rb @@ -7,20 +7,20 @@ describe Ci::API::API do let(:gitlab_url) { GitlabCi.config.gitlab_server.url } let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - let(:options) { + let(:options) do { private_token: private_token, url: gitlab_url } - } + end - before { + before do stub_gitlab_calls - } + end describe "POST /forks" do - let(:project_info) { + let(:project_info) do { project_id: project.gitlab_id, project_token: project.token, @@ -32,7 +32,7 @@ describe Ci::API::API do ssh_url_to_repo: "git@example.com:gitlab-org/underscore" } } - } + end context "with valid info" do before do diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index bca2c48c752..05f6bd5f4f3 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -6,16 +6,16 @@ describe Ci::API::API do let(:gitlab_url) { GitlabCi.config.gitlab_server.url } let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - let(:options) { + let(:options) do { private_token: private_token, url: gitlab_url } - } + end - before { + before do stub_gitlab_calls - } + end context "requests for scoped projects" do # NOTE: These ids are tied to the actual projects on demo.gitlab.com @@ -75,7 +75,7 @@ describe Ci::API::API do end context "Invalid Webhook URL" do - let!(:webhook) { {web_hook: "ala_ma_kota" } } + let!(:webhook) { { web_hook: "ala_ma_kota" } } before do options.merge!(webhook) @@ -116,7 +116,7 @@ describe Ci::API::API do describe "PUT /projects/:id" do let!(:project) { FactoryGirl.create(:ci_project) } - let!(:project_info) { {name: "An updated name!" } } + let!(:project_info) { { name: "An updated name!" } } before do options.merge!(project_info) @@ -163,14 +163,14 @@ describe Ci::API::API do end describe "POST /projects" do - let(:project_info) { + let(:project_info) do { name: "My project", gitlab_id: 1, path: "testing/testing", ssh_url_to_repo: "ssh://example.com/testing/testing.git" } - } + end let(:invalid_project_info) { {} } diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 61ea3be870d..714e5a5a84f 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -4,19 +4,19 @@ describe Ci::API::API do include ApiHelpers include StubGitlabCalls - before { + before do stub_gitlab_calls - } + end describe "GET /runners" do let(:gitlab_url) { GitlabCi.config.gitlab_server.url } let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } - let(:options) { + let(:options) do { - :private_token => private_token, - :url => gitlab_url + private_token: private_token, + url: gitlab_url } - } + end before do 5.times { FactoryGirl.create(:ci_runner) } diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 56757c8f8c7..56799b5203a 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -50,9 +50,9 @@ describe Ci::API::API do end context 'Validates variables' do - let(:variables) { - {'TRIGGER_KEY' => 'TRIGGER_VALUE'} - } + let(:variables) do + { 'TRIGGER_KEY' => 'TRIGGER_VALUE' } + end it 'should validate variables to be a hash' do post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') @@ -61,7 +61,7 @@ describe Ci::API::API do end it 'should validate variables needs to be a map of key-valued strings' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: {key: %w(1 2)}) + post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index cc30b9e83f1..2bb8c5acb65 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -36,7 +36,7 @@ describe Ci::CreateCommitService do end it "creates commit if there is no appropriate job but deploy job has right ref setting" do - config = YAML.dump({deploy: {deploy: "ls", only: ["0_1"]}}) + config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) result = service.execute(project, ref: 'refs/heads/0_1', @@ -51,7 +51,7 @@ describe Ci::CreateCommitService do describe :ci_skip? do it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{message: "some message[ci skip]"}] + commits = [{ message: "some message[ci skip]" }] commit = service.execute(project, ref: 'refs/tags/0_1', before: '00000000', @@ -64,7 +64,7 @@ describe Ci::CreateCommitService do end it "does not skips builds creation if there is no [ci skip] tag in commit message" do - commits = [{message: "some message"}] + commits = [{ message: "some message" }] commit = service.execute(project, ref: 'refs/tags/0_1', @@ -78,7 +78,7 @@ describe Ci::CreateCommitService do end it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - commits = [{message: "some message[ci skip]"}] + commits = [{ message: "some message[ci skip]" }] commit = service.execute(project, ref: 'refs/tags/0_1', before: '00000000', @@ -92,7 +92,7 @@ describe Ci::CreateCommitService do end it "skips build creation if there are already builds" do - commits = [{message: "message"}] + commits = [{ message: "message" }] commit = service.execute(project, ref: 'refs/heads/master', before: '00000000', @@ -113,7 +113,7 @@ describe Ci::CreateCommitService do end it "creates commit with failed status if yaml is invalid" do - commits = [{message: "some message"}] + commits = [{ message: "some message" }] commit = service.execute(project, ref: 'refs/tags/0_1', diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index fadc3df412b..41e4c3e275b 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -23,21 +23,21 @@ module StubGitlabCalls f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json')) stub_request(:post, "#{gitlab_url}api/v3/session.json"). - with(:body => "{\"email\":\"test@test.com\",\"password\":\"123456\"}", - :headers => {'Content-Type'=>'application/json'}). - to_return(:status => 201, :body => f, :headers => {'Content-Type'=>'application/json'}) + with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}", + headers: {'Content-Type'=>'application/json'}). + to_return(status: 201, body: f, headers: {'Content-Type'=>'application/json'}) end def stub_user f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + with(headers: {'Content-Type'=>'application/json'}). + to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + with(headers: {'Content-Type'=>'application/json'}). + to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) end def stub_project_8 @@ -54,24 +54,24 @@ module StubGitlabCalls f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => f, :headers => {'Content-Type'=>'application/json'}) + with(headers: {'Content-Type'=>'application/json'}). + to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) end def stub_projects_owned stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => "", :headers => {}) + with(headers: {'Content-Type'=>'application/json'}). + to_return(status: 200, body: "", headers: {}) end def stub_ci_enable stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz"). - with(:headers => {'Content-Type'=>'application/json'}). - to_return(:status => 200, :body => "", :headers => {}) + with(headers: {'Content-Type'=>'application/json'}). + to_return(status: 200, body: "", headers: {}) end def project_hash_array f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) - return JSON.parse f + JSON.parse f end end -- cgit v1.2.1 From b15fcd405690f4141dc8b3fdeca1daddf8b32985 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 13:57:43 +0200 Subject: Remove unused user_sessions --- app/views/ci/user_sessions/show.html.haml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 app/views/ci/user_sessions/show.html.haml diff --git a/app/views/ci/user_sessions/show.html.haml b/app/views/ci/user_sessions/show.html.haml deleted file mode 100644 index 0710e205618..00000000000 --- a/app/views/ci/user_sessions/show.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -= image_tag user_avatar_url(current_user, 90), class: 'avatar avatar-inline avatar-tile s90', alt: '' -%h3 - Hi, #{@user.name} - - - if @user.is_admin? - %span.label.label-success Admin - -.profile-block - %p - %span.light Email: - %strong= @user.email - - %p - %span.light GitLab profile: - %strong= link_to @user.username, GitlabCi.config.gitlab_server.url + '/u/' + @user.username, target: "_blank" -- cgit v1.2.1 From 09bd8a8b0dbaed79e2a2b8b2416fe80809df8918 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 13:58:38 +0200 Subject: Fix navigation --- app/views/layouts/ci/_nav_admin.html.haml | 2 +- app/views/layouts/ci/_nav_dashboard.html.haml | 2 +- app/views/layouts/ci/_nav_project.html.haml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index 7d65405740c..c987ab876a3 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -14,7 +14,7 @@ = link_to ci_admin_events_path do %i.fa.fa-book Events - = nav_link path: 'runners#index' do + = nav_link path: ['runners#index', 'runners#show'] do = link_to ci_admin_runners_path do %i.fa.fa-cog Runners diff --git a/app/views/layouts/ci/_nav_dashboard.html.haml b/app/views/layouts/ci/_nav_dashboard.html.haml index 202d73d1713..fcff405d19d 100644 --- a/app/views/layouts/ci/_nav_dashboard.html.haml +++ b/app/views/layouts/ci/_nav_dashboard.html.haml @@ -5,7 +5,7 @@ %span Back to GitLab %li.separate-item - = nav_link path: 'projects#show' do + = nav_link path: 'projects#index' do = link_to ci_root_path do %i.fa.fa-home %span diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 9cef47eb4f7..ded60e4bf38 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -10,7 +10,7 @@ %i.fa.fa-bar-chart %span Charts - = nav_link path: ['runners#index', 'runners#show'] do + = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do = link_to ci_project_runners_path(@project) do %i.fa.fa-cog %span @@ -30,7 +30,7 @@ %i.fa.fa-retweet %span Triggers - = nav_link path: 'services#index' do + = nav_link path: ['services#index', 'services#edit'] do = link_to ci_project_services_path(@project) do %i.fa.fa-share %span -- cgit v1.2.1 From ec913402408d149ba3ecb79f4af511cd8ac63fe7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 13:59:19 +0200 Subject: Remove duplicate notices --- app/controllers/ci/services_controller.rb | 2 +- app/views/layouts/ci/_info.html.haml | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/controllers/ci/services_controller.rb b/app/controllers/ci/services_controller.rb index e99f40f3a0a..42ffd41fe42 100644 --- a/app/controllers/ci/services_controller.rb +++ b/app/controllers/ci/services_controller.rb @@ -20,7 +20,7 @@ module Ci def update if @service.update_attributes(service_params) - redirect_to edit_ci_project_service_path(@project, @service.to_param), notice: 'Service was successfully updated.' + redirect_to edit_ci_project_service_path(@project, @service.to_param) else render 'edit' end diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml index 9bc23cfad05..f7230f44242 100644 --- a/app/views/layouts/ci/_info.html.haml +++ b/app/views/layouts/ci/_info.html.haml @@ -1,9 +1,3 @@ .container - - if alert || notice - - if alert - .alert.alert-danger= alert - - if notice - .alert.alert-info= notice - - if current_user && current_user.is_admin? && Ci::Runner.count.zero? = render 'ci/shared/no_runners' -- cgit v1.2.1 From 40ccef76c9d824e7333dd1911bafc0b6887a813f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 14:00:20 +0200 Subject: Fix h3 page titles for triggers, variables and web hooks --- app/views/ci/triggers/index.html.haml | 2 +- app/views/ci/variables/show.html.haml | 4 +++- app/views/ci/web_hooks/index.html.haml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/ci/triggers/index.html.haml b/app/views/ci/triggers/index.html.haml index f04c116231d..44374a1a4d5 100644 --- a/app/views/ci/triggers/index.html.haml +++ b/app/views/ci/triggers/index.html.haml @@ -1,4 +1,4 @@ -%h3 +%h3.page-title Triggers %p.light diff --git a/app/views/ci/variables/show.html.haml b/app/views/ci/variables/show.html.haml index 5cced18a09f..ebf68341e08 100644 --- a/app/views/ci/variables/show.html.haml +++ b/app/views/ci/variables/show.html.haml @@ -1,4 +1,6 @@ -%h3 Secret Variables +%h3.page-title + Secret Variables + %p.light These variables will be set to environment by the runner and will be hidden in the build log. %br diff --git a/app/views/ci/web_hooks/index.html.haml b/app/views/ci/web_hooks/index.html.haml index 92c43cd1d9d..78e8203b25e 100644 --- a/app/views/ci/web_hooks/index.html.haml +++ b/app/views/ci/web_hooks/index.html.haml @@ -1,4 +1,4 @@ -%h3 +%h3.page-title Web hooks %p.light -- cgit v1.2.1 From d0019456cdcbcce6cb9de6fd8b6e033950f1a5bd Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 14:00:46 +0200 Subject: Fix runners administration --- app/controllers/ci/admin/runners_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb index 4f5f3776ddc..686fc5cc354 100644 --- a/app/controllers/ci/admin/runners_controller.rb +++ b/app/controllers/ci/admin/runners_controller.rb @@ -13,7 +13,7 @@ module Ci @builds = @runner.builds.order('id DESC').first(30) @projects = Ci::Project.all @projects = @projects.search(params[:search]) if params[:search].present? - @projects = @projects.where("projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? + @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? @projects = @projects.page(params[:page]).per(30) end -- cgit v1.2.1 From 81560ab259b07a65fd1bf9b51cff2e1dd1ea2974 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 14:01:17 +0200 Subject: Fix build triggers URL and slack notifications --- app/helpers/ci/routes_helper.rb | 4 ++-- app/helpers/ci/triggers_helper.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/ci/routes_helper.rb b/app/helpers/ci/routes_helper.rb index 299a96edee4..42cd54b064f 100644 --- a/app/helpers/ci/routes_helper.rb +++ b/app/helpers/ci/routes_helper.rb @@ -13,11 +13,11 @@ module Ci end def url_helpers - @url_helpers ||= Ci::Base.new + @url_helpers ||= Base.new end def self.method_missing(method, *args, &block) - @url_helpers ||= Ci::Base.new + @url_helpers ||= Base.new if @url_helpers.respond_to?(method) @url_helpers.send(method, *args, &block) diff --git a/app/helpers/ci/triggers_helper.rb b/app/helpers/ci/triggers_helper.rb index 0d62bbf36b8..0d2438928ce 100644 --- a/app/helpers/ci/triggers_helper.rb +++ b/app/helpers/ci/triggers_helper.rb @@ -1,6 +1,6 @@ module Ci module TriggersHelper - def build_trigger_url(project_id, ref_name) + def ci_build_trigger_url(project_id, ref_name) "#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" end end -- cgit v1.2.1 From d0b9a6fca2e73277c8a8e64c9a5f1e7cc02f0570 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 14:02:16 +0200 Subject: Make commits to hover when going to commits and builds --- app/views/layouts/ci/_nav_project.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index ded60e4bf38..2d9897fa864 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,5 +1,5 @@ %ul.nav.nav-sidebar - = nav_link path: 'projects#show' do + = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do = link_to ci_project_path(@project) do %i.fa.fa-list-alt %span -- cgit v1.2.1 From 1ce85dc4e2a1b162a0c30349f3594c1aab710708 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Sep 2015 14:03:02 +0200 Subject: Fix navigation and header tile Remove redundant navigation on commits and builds page --- app/controllers/ci/builds_controller.rb | 2 +- app/controllers/ci/commits_controller.rb | 2 +- app/helpers/ci/commits_helper.rb | 9 +++++++++ app/views/ci/builds/show.html.haml | 9 --------- app/views/ci/commits/show.html.haml | 8 -------- app/views/layouts/ci/_nav_build.html.haml | 3 +++ app/views/layouts/ci/_nav_commit.haml | 3 +++ app/views/layouts/ci/_nav_project.html.haml | 5 +++++ app/views/layouts/ci/build.html.haml | 11 +++++++++++ app/views/layouts/ci/commit.html.haml | 11 +++++++++++ app/views/layouts/ci/project.html.haml | 2 +- 11 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 app/views/layouts/ci/_nav_build.html.haml create mode 100644 app/views/layouts/ci/_nav_commit.haml create mode 100644 app/views/layouts/ci/build.html.haml create mode 100644 app/views/layouts/ci/commit.html.haml diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb index 9338b37e678..2bab562d6b3 100644 --- a/app/controllers/ci/builds_controller.rb +++ b/app/controllers/ci/builds_controller.rb @@ -7,7 +7,7 @@ module Ci before_filter :authorize_manage_project!, except: [:status, :show, :retry, :cancel] before_filter :authorize_manage_builds!, only: [:retry, :cancel] before_filter :build, except: [:show] - layout 'ci/project' + layout 'ci/build' def show if params[:id] =~ /\A\d+\Z/ diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb index f0c0ff1bc11..f79dbbe927a 100644 --- a/app/controllers/ci/commits_controller.rb +++ b/app/controllers/ci/commits_controller.rb @@ -6,7 +6,7 @@ module Ci before_filter :authorize_access_project!, except: [:status, :show, :cancel] before_filter :authorize_manage_builds!, only: [:cancel] before_filter :commit, only: :show - layout 'ci/project' + layout 'ci/commit' def show @builds = @commit.builds diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb index 86f254223cb..994157ed84b 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -26,5 +26,14 @@ module Ci def truncate_first_line(message, length = 50) truncate(message.each_line.first.chomp, length: length) if message end + + def ci_commit_title(commit) + content_tag :span do + link_to( + simple_sanitize(commit.project.name), ci_project_path(commit.project) + ) + ' @ ' + + gitlab_commit_link(@project, @commit.sha) + end + end end end diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index 1a07feeb20e..d1e955b5012 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -1,12 +1,3 @@ -%h4.page-title - = link_to @project.name, ci_project_path(@project) - @ - = @commit.short_sha - -%p - = link_to ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) do - ← Back to project commit -%hr #up-build-trace - if @commit.matrix? %ul.nav.nav-tabs.append-bottom-10 diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 72fda8fe949..1aeb557314a 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -1,11 +1,3 @@ -%h4.page-title - = @project.name - @ - #{gitlab_commit_link(@project, @commit.sha)} -%p - = link_to ci_project_path(@project) do - ← Back to project commits -%hr .commit-info %pre.commit-message #{@commit.git_commit_message} diff --git a/app/views/layouts/ci/_nav_build.html.haml b/app/views/layouts/ci/_nav_build.html.haml new file mode 100644 index 00000000000..732882726e7 --- /dev/null +++ b/app/views/layouts/ci/_nav_build.html.haml @@ -0,0 +1,3 @@ += render 'layouts/ci/nav_project', + back_title: 'Back to project commit', + back_url: ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) diff --git a/app/views/layouts/ci/_nav_commit.haml b/app/views/layouts/ci/_nav_commit.haml new file mode 100644 index 00000000000..19c526678d0 --- /dev/null +++ b/app/views/layouts/ci/_nav_commit.haml @@ -0,0 +1,3 @@ += render 'layouts/ci/nav_project', + back_title: 'Back to project commits', + back_url: ci_project_path(@project) diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 2d9897fa864..10b87e3a2b1 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,4 +1,9 @@ %ul.nav.nav-sidebar + = nav_link do + = link_to defined?(back_url) ? back_url : ci_root_path, title: defined?(back_title) ? back_title : 'Back to Dashboard', data: {placement: 'right'}, class: 'back-link' do + = icon('caret-square-o-left fw') + %span= defined?(back_title) ? back_title : 'Back to Dashboard' + %li.separate-item = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do = link_to ci_project_path(@project) do %i.fa.fa-list-alt diff --git a/app/views/layouts/ci/build.html.haml b/app/views/layouts/ci/build.html.haml new file mode 100644 index 00000000000..d404ecb894a --- /dev/null +++ b/app/views/layouts/ci/build.html.haml @@ -0,0 +1,11 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/head' + %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} + - header_title ci_commit_title(@commit) + - if current_user + = render "layouts/header/default", title: header_title + - else + = render "layouts/header/public", title: header_title + + = render 'layouts/ci/page', sidebar: 'nav_build' diff --git a/app/views/layouts/ci/commit.html.haml b/app/views/layouts/ci/commit.html.haml new file mode 100644 index 00000000000..5727f1b8e3e --- /dev/null +++ b/app/views/layouts/ci/commit.html.haml @@ -0,0 +1,11 @@ +!!! 5 +%html{ lang: "en"} + = render 'layouts/head' + %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} + - header_title ci_commit_title(@commit) + - if current_user + = render "layouts/header/default", title: header_title + - else + = render "layouts/header/public", title: header_title + + = render 'layouts/ci/page', sidebar: 'nav_commit' diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml index 23a4928fcc7..15478c3f5bc 100644 --- a/app/views/layouts/ci/project.html.haml +++ b/app/views/layouts/ci/project.html.haml @@ -2,7 +2,7 @@ %html{ lang: "en"} = render 'layouts/head' %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = @project.name + - header_title @project.name, ci_project_path(@project) - if current_user = render "layouts/header/default", title: header_title - else -- cgit v1.2.1 From 2c4daf1a68a23e6d4f17340b03415cfd715d5afc Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 14 Sep 2015 16:48:33 +0300 Subject: fix part of specs --- spec/requests/ci/builds_spec.rb | 2 +- spec/requests/ci/commits_spec.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 194 +++++++++++++------------ 3 files changed, 100 insertions(+), 98 deletions(-) diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb index 0d7650ef582..998c386ead4 100644 --- a/spec/requests/ci/builds_spec.rb +++ b/spec/requests/ci/builds_spec.rb @@ -9,7 +9,7 @@ describe "Builds" do describe "GET /:project/builds/:id/status.json" do before do - get status_project_build_path(@project, @build), format: :json + get status_ci_project_build_path(@project, @build), format: :json end it { expect(response.status).to eq(200) } diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb index fe7bd2de3e7..fb317670339 100644 --- a/spec/requests/ci/commits_spec.rb +++ b/spec/requests/ci/commits_spec.rb @@ -8,7 +8,7 @@ describe "Commits" do describe "GET /:project/refs/:ref_name/commits/:id/status.json" do before do - get status_project_ref_commit_path(@project, @commit.ref, @commit.sha), format: :json + get status_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), format: :json end it { expect(response.status).to eq(200) } diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 2bb8c5acb65..38d9943765a 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -1,84 +1,121 @@ require 'spec_helper' -describe Ci::CreateCommitService do - let(:service) { CreateCommitService.new } - let(:project) { FactoryGirl.create(:project) } - - describe :execute do - context 'valid params' do - let(:commit) do - service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] - ) +module Ci + describe CreateCommitService do + let(:service) { CreateCommitService.new } + let(:project) { FactoryGirl.create(:ci_project) } + + describe :execute do + context 'valid params' do + let(:commit) do + service.execute(project, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + end + + it { expect(commit).to be_kind_of(Commit) } + it { expect(commit).to be_valid } + it { expect(commit).to be_persisted } + it { expect(commit).to eq(project.commits.last) } + it { expect(commit.builds.first).to be_kind_of(Build) } end - it { commit.should be_kind_of(Commit) } - it { commit.should be_valid } - it { commit.should be_persisted } - it { commit.should == project.commits.last } - it { commit.builds.first.should be_kind_of(Build) } - end - - context "skip tag if there is no build for it" do - it "creates commit if there is appropriate job" do - result = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - ci_yaml_file: gitlab_ci_yaml, - commits: [ { message: "Message" } ] - ) - result.should be_persisted + context "skip tag if there is no build for it" do + it "creates commit if there is appropriate job" do + result = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: gitlab_ci_yaml, + commits: [ { message: "Message" } ] + ) + expect(result).to be_persisted + end + + it "creates commit if there is no appropriate job but deploy job has right ref setting" do + config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) + + result = service.execute(project, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + ci_yaml_file: config, + commits: [ { message: "Message" } ] + ) + expect(result).to be_persisted + end end - it "creates commit if there is no appropriate job but deploy job has right ref setting" do - config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) - - result = service.execute(project, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - ci_yaml_file: config, - commits: [ { message: "Message" } ] - ) - result.should be_persisted + describe :ci_skip? do + it "skips builds creation if there is [ci skip] tag in commit message" do + commits = [{ message: "some message[ci skip]" }] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + end + + it "does not skips builds creation if there is no [ci skip] tag in commit message" do + commits = [{ message: "some message" }] + + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: gitlab_ci_yaml + ) + + expect(commit.builds.first.name).to eq("staging") + end + + it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do + commits = [{ message: "some message[ci skip]" }] + commit = service.execute(project, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits, + ci_yaml_file: "invalid: file" + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + end end - end - describe :ci_skip? do - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: "some message[ci skip]" }] + it "skips build creation if there are already builds" do + commits = [{ message: "message" }] commit = service.execute(project, - ref: 'refs/tags/0_1', + ref: 'refs/heads/master', before: '00000000', after: '31das312', commits: commits, ci_yaml_file: gitlab_ci_yaml ) - commit.builds.any?.should be_false - commit.status.should == "skipped" - end - - it "does not skips builds creation if there is no [ci skip] tag in commit message" do - commits = [{ message: "some message" }] + expect(commit.builds.count(:all)).to eq(2) commit = service.execute(project, - ref: 'refs/tags/0_1', + ref: 'refs/heads/master', before: '00000000', after: '31das312', commits: commits, ci_yaml_file: gitlab_ci_yaml ) - - commit.builds.first.name.should == "staging" + expect(commit.builds.count(:all)).to eq(2) end - it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - commits = [{ message: "some message[ci skip]" }] + it "creates commit with failed status if yaml is invalid" do + commits = [{ message: "some message" }] + commit = service.execute(project, ref: 'refs/tags/0_1', before: '00000000', @@ -86,45 +123,10 @@ describe Ci::CreateCommitService do commits: commits, ci_yaml_file: "invalid: file" ) - commit.builds.any?.should be_false - commit.status.should == "skipped" - end - end - - it "skips build creation if there are already builds" do - commits = [{ message: "message" }] - commit = service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - commit.builds.count(:all).should == 2 - - commit = service.execute(project, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml - ) - commit.builds.count(:all).should == 2 - end - it "creates commit with failed status if yaml is invalid" do - commits = [{ message: "some message" }] - - commit = service.execute(project, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits, - ci_yaml_file: "invalid: file" - ) - - commit.status.should == "failed" - commit.builds.any?.should be_false + expect(commit.status).to eq("failed") + expect(commit.builds.any?).to be false + end end end end -- cgit v1.2.1 From 910bf96ec3d60194b2fe4444c1df24f141b8450b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 14 Sep 2015 18:14:17 +0300 Subject: fix specs. Stage 2 --- lib/api/helpers.rb | 5 +- lib/ci/api/api.rb | 2 + lib/ci/api/helpers.rb | 89 ++---------------------------------- spec/requests/ci/api/runners_spec.rb | 26 +++++------ spec/support/api_helpers.rb | 11 +++++ 5 files changed, 32 insertions(+), 101 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 76c9cc2e3a4..ef0f897a2fb 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -148,15 +148,14 @@ module API end end - def attributes_for_keys(keys) + def attributes_for_keys(keys, custom_params = nil) + params_hash = custom_params || params attrs = {} - keys.each do |key| if params[key].present? or (params.has_key?(key) and params[key] == false) attrs[key] = params[key] end end - ActionController::Parameters.new(attrs).permit! end diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 392fb548001..172c6f22164 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -3,6 +3,7 @@ Dir["#{Rails.root}/lib/ci/api/*.rb"].each {|file| require file} module Ci module API class API < Grape::API + include APIGuard version 'v1', using: :path rescue_from ActiveRecord::RecordNotFound do @@ -25,6 +26,7 @@ module Ci format :json helpers Helpers + helpers ::API::APIHelpers mount Builds mount Commits diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 3f58670fb49..9197f917d73 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -1,30 +1,6 @@ module Ci module API module Helpers - PRIVATE_TOKEN_PARAM = :private_token - PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" - ACCESS_TOKEN_PARAM = :access_token - ACCESS_TOKEN_HEADER = "HTTP_ACCESS_TOKEN" - UPDATE_RUNNER_EVERY = 60 - - def current_user - @current_user ||= begin - options = { - access_token: (params[ACCESS_TOKEN_PARAM] || env[ACCESS_TOKEN_HEADER]), - private_token: (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]), - } - Ci::UserSession.new.authenticate(options.compact) - end - end - - def current_runner - @runner ||= Ci::Runner.find_by_token(params[:token].to_s) - end - - def authenticate! - forbidden! unless current_user - end - def authenticate_runners! forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN end @@ -43,72 +19,15 @@ module Ci end end + def current_runner + @runner ||= Runner.find_by_token(params[:token].to_s) + end + def update_runner_info return unless params["info"].present? info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"]) current_runner.update(info) end - - # Checks the occurrences of required attributes, each attribute must be present in the params hash - # or a Bad Request error is invoked. - # - # Parameters: - # keys (required) - A hash consisting of keys that must be present - def required_attributes!(keys) - keys.each do |key| - bad_request!(key) unless params[key].present? - end - end - - def attributes_for_keys(keys, custom_params = nil) - params_hash = custom_params || params - attrs = {} - keys.each do |key| - attrs[key] = params_hash[key] if params_hash[key].present? - end - attrs - end - - # error helpers - - def forbidden! - render_api_error!('403 Forbidden', 403) - end - - def bad_request!(attribute) - message = ["400 (Bad request)"] - message << "\"" + attribute.to_s + "\" not given" - render_api_error!(message.join(' '), 400) - end - - def not_found!(resource = nil) - message = ["404"] - message << resource if resource - message << "Not Found" - render_api_error!(message.join(' '), 404) - end - - def unauthorized! - render_api_error!('401 Unauthorized', 401) - end - - def not_allowed! - render_api_error!('Method Not Allowed', 405) - end - - def render_api_error!(message, status) - error!({ 'message' => message }, status) - end - - private - - def abilities - @abilities ||= begin - abilities = Six.new - abilities << Ability - abilities - end - end end end end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 714e5a5a84f..11dc089e1f5 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -9,8 +9,8 @@ describe Ci::API::API do end describe "GET /runners" do - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } + let(:private_token) { create(:user).private_token } let(:options) do { private_token: private_token, @@ -23,7 +23,7 @@ describe Ci::API::API do end it "should retrieve a list of all runners" do - get api("/runners"), options + get ci_api("/runners", nil), options expect(response.status).to eq(200) expect(json_response.count).to eq(5) expect(json_response.last).to have_key("id") @@ -33,41 +33,41 @@ describe Ci::API::API do describe "POST /runners/register" do describe "should create a runner if token provided" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } + before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } it { expect(response.status).to eq(201) } end describe "should create a runner with description" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } + before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } it { expect(response.status).to eq(201) } - it { expect(Runner.first.description).to eq("server.hostname") } + it { expect(Ci::Runner.first.description).to eq("server.hostname") } end describe "should create a runner with tags" do - before { post api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } + before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } it { expect(response.status).to eq(201) } - it { expect(Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) } + it { expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) } end describe "should create a runner if project token provided" do let(:project) { FactoryGirl.create(:ci_project) } - before { post api("/runners/register"), token: project.token } + before { post ci_api("/runners/register"), token: project.token } it { expect(response.status).to eq(201) } it { expect(project.runners.size).to eq(1) } end it "should return 403 error if token is invalid" do - post api("/runners/register"), token: 'invalid' + post ci_api("/runners/register"), token: 'invalid' expect(response.status).to eq(403) end it "should return 400 error if no token" do - post api("/runners/register") + post ci_api("/runners/register") expect(response.status).to eq(400) end @@ -75,9 +75,9 @@ describe Ci::API::API do describe "DELETE /runners/delete" do let!(:runner) { FactoryGirl.create(:ci_runner) } - before { delete api("/runners/delete"), token: runner.token } + before { delete ci_api("/runners/delete"), token: runner.token } it { expect(response.status).to eq(200) } - it { expect(Runner.count).to eq(0) } + it { expect(Ci::Runner.count).to eq(0) } end end diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb index f63322776d4..1b3cafb497c 100644 --- a/spec/support/api_helpers.rb +++ b/spec/support/api_helpers.rb @@ -28,6 +28,17 @@ module ApiHelpers "&private_token=#{user.private_token}" : "") end + def ci_api(path, user = nil) + "/ci/api/v1/#{path}" + + + # Normalize query string + (path.index('?') ? '' : '?') + + + # Append private_token if given a User object + (user.respond_to?(:private_token) ? + "&private_token=#{user.private_token}" : "") + end + def json_response @_json_response ||= JSON.parse(response.body) end -- cgit v1.2.1 From 0d010088761a0116a5e4317f2dd0c61b510ee1b3 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 14 Sep 2015 18:10:23 +0200 Subject: Update gitlab_emoji to 0.1.1 --- Gemfile.lock | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f3091857b7e..29f7213bd67 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -253,7 +253,7 @@ GEM ruby-progressbar (~> 1.4) gemnasium-gitlab-service (0.2.6) rugged (~> 0.21) - gemojione (2.0.0) + gemojione (2.0.1) json gherkin-ruby (0.3.1) racc @@ -272,7 +272,7 @@ GEM charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) - gitlab_emoji (0.1.0) + gitlab_emoji (0.1.1) gemojione (~> 2.0) gitlab_git (7.2.15) activesupport (~> 4.0) @@ -889,6 +889,3 @@ DEPENDENCIES virtus webmock (~> 1.21.0) wikicloth (= 0.8.1) - -BUNDLED WITH - 1.10.5 -- cgit v1.2.1 From 789d06e48bd613e4d736ccd5504fb0d53d74b2f7 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 14 Sep 2015 11:20:18 -0500 Subject: Fix empty project UI --- app/views/projects/empty.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 798f1c47da5..185ebf23934 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -4,7 +4,7 @@ = render "home_panel" -.center.light-well +.gray-content-block.center %h3.page-title The repository for this project is empty %p -- cgit v1.2.1 From ebd06d7e7da9f4df846f1af2ca49c6c64f1f90b0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 21:00:06 +0200 Subject: Make small ui fixes for CI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/projects.scss | 6 ++++++ app/helpers/ci/commits_helper.rb | 2 +- app/views/ci/charts/show.html.haml | 2 +- app/views/ci/projects/_search.html.haml | 15 +++++++-------- app/views/ci/projects/gitlab.html.haml | 3 --- app/views/ci/projects/index.html.haml | 5 +++-- app/views/layouts/ci/_info.html.haml | 5 ++--- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index 167df54453a..b246fb9e07d 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -47,4 +47,10 @@ .loading{ font-size: 20px; } + + .ci-charts { + fieldset { + margin-bottom: 16px; + } + } } diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb index 994157ed84b..74de30e006e 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -1,7 +1,7 @@ module Ci module CommitsHelper def commit_status_alert_class(commit) - return unless commit + return 'alert-info' unless commit case commit.status when 'success' diff --git a/app/views/ci/charts/show.html.haml b/app/views/ci/charts/show.html.haml index b5fcfc1563c..0497f037721 100644 --- a/app/views/ci/charts/show.html.haml +++ b/app/views/ci/charts/show.html.haml @@ -1,4 +1,4 @@ -#charts +#charts.ci-charts = render 'builds' = render 'build_times' = render 'overall' diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml index e65aaa3870d..6d84b25a6af 100644 --- a/app/views/ci/projects/_search.html.haml +++ b/app/views/ci/projects/_search.html.haml @@ -1,16 +1,15 @@ .search - = form_tag "#", method: :get, class: 'navbar-form' do |f| - .form-group - .input-group - = search_field_tag "search", params[:search], placeholder: "Search", class: "search-input form-control" - .input-group-addon - %i.fa.fa-search + = form_tag "#", method: :get, class: 'ci-search-form' do |f| + .input-group + = search_field_tag "search", params[:search], placeholder: "Search", class: "search-input form-control" + .input-group-addon + %i.fa.fa-search :coffeescript - $('.search .navbar-form').submit -> + $('.ci-search-form').submit -> NProgress.start() - query = $('.search .navbar-form .search-input').val() + query = $('.ci-search-form .search-input').val() $.get '#{gitlab_ci_projects_path}', { search: query }, (data) -> $(".projects").html data.html NProgress.done() diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml index 690b6cf3cdb..f57dfcb0790 100644 --- a/app/views/ci/projects/gitlab.html.haml +++ b/app/views/ci/projects/gitlab.html.haml @@ -3,9 +3,6 @@ .pull-left.fetch-status - if params[:search].present? by keyword: "#{params[:search]}", - %br - - .pull-right #{@total_count} projects, #{@projects.size} of them added to CI %br diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 4c74610a575..085a70811ae 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -1,6 +1,7 @@ - if current_user - = render "search" - .projects + .gray-content-block.top-block + = render "search" + .projects.prepend-top-default %p.fetch-status.light %i.fa.fa-refresh.fa-spin :coffeescript diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml index f7230f44242..24c68a6dbf5 100644 --- a/app/views/layouts/ci/_info.html.haml +++ b/app/views/layouts/ci/_info.html.haml @@ -1,3 +1,2 @@ -.container - - if current_user && current_user.is_admin? && Ci::Runner.count.zero? - = render 'ci/shared/no_runners' +- if current_user && current_user.is_admin? && Ci::Runner.count.zero? + = render 'ci/shared/no_runners' -- cgit v1.2.1 From 9a3d0f1d92ee99792b40c401f83121adb8fd3387 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 00:28:11 +0200 Subject: Add rake task to migrate CI tags --- db/migrate/20150914215247_add_ci_tags.rb | 23 ++++++++++++++++++ db/schema.rb | 22 +++++++++++++++++- lib/tasks/ci/migrate.rake | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150914215247_add_ci_tags.rb create mode 100644 lib/tasks/ci/migrate.rake diff --git a/db/migrate/20150914215247_add_ci_tags.rb b/db/migrate/20150914215247_add_ci_tags.rb new file mode 100644 index 00000000000..df3390e8a82 --- /dev/null +++ b/db/migrate/20150914215247_add_ci_tags.rb @@ -0,0 +1,23 @@ +class AddCiTags < ActiveRecord::Migration + def change + create_table "ci_taggings", force: true do |t| + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" + t.string "context", limit: 128 + t.datetime "created_at" + end + + add_index "ci_taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "ci_taggings_idx", unique: true, using: :btree + add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree + + create_table "ci_tags", force: true do |t| + t.string "name" + t.integer "taggings_count", default: 0 + end + + add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree + end +end diff --git a/db/schema.rb b/db/schema.rb index 30b4832c1f3..5fd764bf698 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150902001023) do +ActiveRecord::Schema.define(version: 20150914215247) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -223,6 +223,26 @@ ActiveRecord::Schema.define(version: 20150902001023) do add_index "ci_sessions", ["session_id"], name: "index_ci_sessions_on_session_id", using: :btree add_index "ci_sessions", ["updated_at"], name: "index_ci_sessions_on_updated_at", using: :btree + create_table "ci_taggings", force: true do |t| + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" + t.string "context", limit: 128 + t.datetime "created_at" + end + + add_index "ci_taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "ci_taggings_idx", unique: true, using: :btree + add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree + + create_table "ci_tags", force: true do |t| + t.string "name" + t.integer "taggings_count", default: 0 + end + + add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree + create_table "ci_trigger_requests", force: true do |t| t.integer "trigger_id", null: false t.text "variables" diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake new file mode 100644 index 00000000000..c00b17f7a2d --- /dev/null +++ b/lib/tasks/ci/migrate.rake @@ -0,0 +1,40 @@ +namespace :ci do + namespace :migrate do + def list_objects(type) + ids = ActiveRecord::Base.connection.select_all( + 'select distinct taggable_id from ci_taggings where taggable_type = $1', + nil, [[nil, type]] + ) + ids.map { |id| id['taggable_id'] } + end + + def list_tags(type, id) + tags = ActiveRecord::Base.connection.select_all( + 'select ci_tags.name from ci_tags ' + + 'join ci_taggings on ci_tags.id = ci_taggings.tag_id ' + + 'where taggable_type = $1 and taggable_id = $2 and context = $3', + nil, [[nil, type], [nil, id], [nil, 'tags']] + ) + tags.map { |tag| tag['name'] } + end + + desc 'GITLAB | Migrate CI tags' + task tags: :environment do + list_objects('Runner').each do |id| + runner = Ci::Runner.find_by_id(id) + if runner + tags = list_tags('Runner', id) + runner.update_attributes(tag_list: tags) + end + end + + list_objects('Build').each do |id| + build = Ci::Build.find_by_id(id) + if build + tags = list_tags('Build', id) + build.update_attributes(tag_list: tags) + end + end + end + end +end -- cgit v1.2.1 From e2ece2bc350bf881e9716e51c01a59eecac65fc9 Mon Sep 17 00:00:00 2001 From: liyakun Date: Thu, 10 Sep 2015 16:18:40 +0200 Subject: Add "Replace" and "Upload" features Refactor upload and replace functionality Rename file and move CSS Fix typo Make dropzone a div Remove unnecessary file Change color of "upload existing one" Add missing changes --- CHANGELOG | 1 + .../javascripts/blob/blob_file_dropzone.js.coffee | 52 ++++++++++++++ app/assets/stylesheets/pages/tree.scss | 12 ++++ app/controllers/projects/blob_controller.rb | 28 ++++++-- app/services/files/create_service.rb | 4 +- app/views/projects/blob/_actions.html.haml | 6 +- app/views/projects/blob/_replace.html.haml | 28 ++++++++ app/views/projects/blob/_upload.html.haml | 28 ++++++++ app/views/projects/blob/new.html.haml | 10 ++- app/views/projects/blob/show.html.haml | 1 + config/routes.rb | 10 +++ features/project/source/browse_files.feature | 23 ++++++ features/steps/project/source/browse_files.rb | 83 +++++++++++++++++++++- 13 files changed, 274 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/blob/blob_file_dropzone.js.coffee create mode 100644 app/views/projects/blob/_replace.html.haml create mode 100644 app/views/projects/blob/_upload.html.haml diff --git a/CHANGELOG b/CHANGELOG index b41237f8679..2b1edf54c42 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.0.0 (unreleased) - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu) + - Add "replace" and "upload" functionalities to allow user replace existing file and upload new file into current repository - Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu) - Fix emoji URLs in Markdown when relative_url_root is used (Stan Hu) - Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU) diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee new file mode 100644 index 00000000000..090af9bb376 --- /dev/null +++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee @@ -0,0 +1,52 @@ +class @BlobFileDropzone + constructor: (form, method) -> + form_dropzone = form.find('.dropzone') + Dropzone.autoDiscover = false + dropzone = form_dropzone.dropzone( + autoDiscover: false + autoProcessQueue: false + url: form.attr('action') + # Rails uses a hidden input field for PUT + # http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails + method: method + clickable: true + uploadMultiple: false + paramName: "file" + maxFilesize: gon.max_file_size or 10 + parallelUploads: 1 + maxFiles: 1 + addRemoveLinks: true + previewsContainer: '.dropzone-previews' + headers: + "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") + + success: (header, response) -> + window.location.href = response.filePath + return + + error: (temp, errorMessage) -> + stripped = $("
    ").html(errorMessage).text(); + $('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show() + return + + maxfilesexceeded: (file) -> + @removeFile file + return + + removedfile: (file) -> + $('.dropzone-previews')[0].removeChild(file.previewTemplate) + $('.dropzone-alerts').html('').hide() + return true + + sending: (file, xhr, formData) -> + formData.append('commit_message', form.find('#commit_message').val()) + return + ) + + submitButton = form.find('#submit-all')[0] + submitButton.addEventListener 'click', (e) -> + e.preventDefault() + e.stopPropagation() + alert "Please select a file" if dropzone[0].dropzone.getQueuedFiles().length == 0 + dropzone[0].dropzone.processQueue() + return false diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 71ca37c0cd7..df7fab07a57 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -116,3 +116,15 @@ } #modal-remove-blob > .modal-dialog { width: 850px; } + +.blob-upload-dropzone-previews { + text-align: center; + border: 2px; + border-style: dashed; + min-height: 200px; +} + +.upload-link { + font-weight: normal; + color: #0000EE; +} diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 100d3d3b317..8776721d243 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -26,10 +26,16 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) + respond_to do |format| + format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } + format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } + end else flash[:alert] = result[:message] - render :new + respond_to do |format| + format.html { render :new } + format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } } + end end end @@ -45,10 +51,16 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to after_edit_path + respond_to do |format| + format.html { redirect_to after_edit_path } + format.json { render json: { message: "success", filePath: after_edit_path } } + end else flash[:alert] = result[:message] - render :edit + respond_to do |format| + format.html { render :edit } + format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } } + end end end @@ -146,11 +158,19 @@ class Projects::BlobController < Projects::ApplicationController @file_path = if action_name.to_s == 'create' + if params[:file].present? + params[:file_name] = params[:file].original_filename + end File.join(@path, File.basename(params[:file_name])) else @path end + if params[:file].present? + params[:content] = Base64.encode64(params[:file].read) + params[:encoding] = 'base64' + end + @commit_params = { file_path: @file_path, current_branch: @current_branch, diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 91d715b2d63..ffbb5993279 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -19,10 +19,12 @@ module Files end unless project.empty_repo? + @file_path.slice!(0) if @file_path.start_with?('/') + blob = repository.blob_at_branch(@current_branch, @file_path) if blob - raise_error("Your changes could not be committed, because file with such name exists") + raise_error("Your changes could not be committed because a file with the same name already exists") end end end diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 13f8271b979..5b61846fe6d 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -17,6 +17,6 @@ tree_join(@commit.sha, @path)), class: 'btn btn-sm' - if allowed_tree_edit? - = button_tag class: 'remove-blob btn btn-sm btn-remove', - 'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do - Remove + .btn-group{:role => "group"} + %button.btn.btn-default{class: 'btn-primary', href: '#modal-replace-blob', 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal'} Replace + %button.btn.btn-default{class: 'btn-remove', href: '#modal-remove-blob', 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal'} Remove diff --git a/app/views/projects/blob/_replace.html.haml b/app/views/projects/blob/_replace.html.haml new file mode 100644 index 00000000000..84abf0303d0 --- /dev/null +++ b/app/views/projects/blob/_replace.html.haml @@ -0,0 +1,28 @@ +#modal-replace-blob.modal + .modal-dialog + .modal-content + .modal-header + %a.close{href: "#", "data-dismiss" => "modal"} × + %h3.page-title Replace #{@blob.name} + %p.light + From branch + %strong= @ref + .modal-body + = form_tag namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'blob-file-upload-form-js form-horizontal' do + .dropzone + .dropzone-previews{class: "blob-upload-dropzone-previews"} + %p.dz-message{class: "hint"}< + Attach files by dragging & dropping or  + %a{href: '#', class: "markdown-selector"}>click to upload + %br + .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} + = render 'shared/commit_message_container', params: params, + placeholder: 'Replace this file because...' + .form-group + .col-sm-offset-2.col-sm-10 + = button_tag 'Replace file', class: 'btn btn-small btn-primary btn-replace-file', id: 'submit-all' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + +:coffeescript + disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-replace-file' + new BlobFileDropzone($('.blob-file-upload-form-js'), 'put') diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml new file mode 100644 index 00000000000..5a6a3358a17 --- /dev/null +++ b/app/views/projects/blob/_upload.html.haml @@ -0,0 +1,28 @@ +#modal-upload-blob.modal + .modal-dialog + .modal-content + .modal-header + %a.close{href: "#", "data-dismiss" => "modal"} × + %h3.page-title Upload + %p.light + From branch + %strong= @ref + .modal-body + = form_tag namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'blob-file-upload-form-js form-horizontal' do + .dropzone + .dropzone-previews{class: "blob-upload-dropzone-previews"} + %p.dz-message{class: "hint"}< + Attach files by dragging & dropping or  + %a{href: '#', class: "markdown-selector"}>click to upload + %br + .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} + = render 'shared/commit_message_container', params: params, + placeholder: 'Upload this file because...' + .form-group + .col-sm-offset-2.col-sm-10 + = button_tag 'Upload file', class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + +:coffeescript + disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file' + new BlobFileDropzone($('.blob-file-upload-form-js'), 'post') diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 7c2a4fece94..6fb46ea2040 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,5 +1,11 @@ -- page_title "New File", @ref -%h3.page-title New file +%h3.page-title< + Create new file or  + %a.upload-link{href: '#modal-upload-blob', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'}>upload existing one + +.file-title + = render 'projects/blob/upload' + %br + .file-editor = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do = render 'projects/blob/editor', ref: @ref diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index bd2fc43633c..19e876ec34c 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -10,3 +10,4 @@ - if allowed_tree_edit? = render 'projects/blob/remove' + = render 'projects/blob/replace' diff --git a/config/routes.rb b/config/routes.rb index 011af4825fa..ab1d8f7ce92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -356,6 +356,16 @@ Gitlab::Application.routes.draw do to: 'blob#destroy', constraints: { id: /.+/, format: false } ) + put( + '/blob/*id', + to: 'blob#update', + constraints: { id: /.+/, format: false } + ) + post( + '/blob/*id', + to: 'blob#create', + constraints: { id: /.+/, format: false } + ) end scope do diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index d3a77466a35..b5b6abe6aff 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -33,6 +33,29 @@ Feature: Project Source Browse Files And I click on "Commit Changes" Then I am redirected to the new file And I should see its new content + + @javascript + Scenario: I can upload file and commit + Given I click on "new file" link in repo + Then I can see new file page + And I can see "upload existing one" + And I click on "upload existing one" + And I upload a new text file + And I fill the upload file commit message + And I click on "Upload file" + Then I can see the new text file + And I can see the new commit message + + @javascript + Scenario: I can replace file and commit + Given I click on ".gitignore" file in repo + And I see the ".gitignore" + And I click on "Replace" + And I replace it with a text file + And I fill the replace file commit message + And I click on "Replace file" + Then I can see the new text file + And I can see the replacement commit message @javascript Scenario: I can create and commit file and specify new branch diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 5cb085db207..7a0ee4df45e 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -1,3 +1,4 @@ +# coding: utf-8 class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps include SharedAuthentication include SharedProject @@ -78,7 +79,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I fill the commit message' do - fill_in :commit_message, with: 'Not yet a commit message.' + fill_in :commit_message, with: 'Not yet a commit message.', visible: true end step 'I click link "Diff"' do @@ -97,6 +98,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps click_button 'Remove file' end + step 'I click on "Replace"' do + click_button "Replace" + end + + step 'I click on "Replace file"' do + click_button 'Replace file' + end + step 'I see diff' do expect(page).to have_css '.line_holder.new' end @@ -106,10 +115,55 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I can see new file page' do - expect(page).to have_content "New file" + expect(page).to have_content "new file" expect(page).to have_content "Commit message" end + step 'I can see "upload existing one"' do + expect(page).to have_content "upload existing one" + end + + step 'I click on "upload existing one"' do + click_link 'upload existing one' + end + + step 'I click on "Upload file"' do + click_button 'Upload file' + end + + step 'I can see the new commit message' do + expect(page).to have_content "New upload commit message" + end + + step 'I upload a new text file' do + drop_in_dropzone test_text_file + end + + step 'I fill the upload file commit message' do + page.within('#modal-upload-blob') do + fill_in :commit_message, with: 'New upload commit message' + end + end + + step 'I replace it with a text file' do + drop_in_dropzone test_text_file + end + + step 'I fill the replace file commit message' do + page.within('#modal-replace-blob') do + fill_in :commit_message, with: 'Replacement file commit message' + end + end + + step 'I can see the replacement commit message' do + expect(page).to have_content "Replacement file commit message" + end + + step 'I can see the new text file' do + expect(page).to have_content "Lorem ipsum dolor sit amet" + expect(page).to have_content "Sed ut perspiciatis unde omnis" + end + step 'I click on files directory' do click_link 'files' end @@ -232,4 +286,29 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps def new_file_name 'not_a_file.md' end + + def drop_in_dropzone(file_path) + # Generate a fake input selector + page.execute_script <<-JS + var fakeFileInput = window.$('').attr( + {id: 'fakeFileInput', type: 'file'} + ).appendTo('body'); + JS + # Attach the file to the fake input selector with Capybara + attach_file("fakeFileInput", file_path) + # Add the file to a fileList array and trigger the fake drop event + page.execute_script <<-JS + var fileList = [$('#fakeFileInput')[0].files[0]]; + var e = jQuery.Event('drop', { dataTransfer : { files : fileList } }); + $('.dropzone')[0].dropzone.listeners[0].events.drop(e); + JS + end + + def test_text_file + File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') + end + + def test_image_file + File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') + end end -- cgit v1.2.1 From 558084f403d2198569537729b471b93befbdbbae Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 15 Sep 2015 09:54:42 +0200 Subject: Improve project settings button on profile Signed-off-by: Dmitriy Zaporozhets --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index aa4e8722fb1..37d5dba0330 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -16,8 +16,8 @@ - if @user == current_user .pull-right.hidden-xs = link_to profile_path, class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o - Edit Profile settings + = icon('user') + Profile settings - elsif current_user .pull-right %span.dropdown -- cgit v1.2.1 From a635e1a395ec465319bf900734cbbf715f3b9e9a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 15 Sep 2015 11:02:37 +0200 Subject: Fix last push widget Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/blocks.scss | 5 +++++ app/views/events/_event_last_push.html.haml | 24 ++++++++++++------------ app/views/projects/_last_push.html.haml | 23 ++++++++++++----------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss index 1bd016e0df2..ce024272a30 100644 --- a/app/assets/stylesheets/generic/blocks.scss +++ b/app/assets/stylesheets/generic/blocks.scss @@ -36,6 +36,11 @@ margin-bottom: 0; } + &.clear-block { + margin-bottom: $gl-padding - 1px; + padding-bottom: $gl-padding; + } + &.second-block { margin-top: -1px; margin-bottom: 0; diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index 6a0c6cba41b..ffc37ad6178 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -1,14 +1,14 @@ - if show_last_push_widget?(event) - .event-last-push - .event-last-push-text - %span You pushed to - = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do - %strong= event.ref_name - %span at - %strong= link_to_project event.project - #{time_ago_with_tooltip(event.created_at)} + .gray-content-block.clear-block + .event-last-push + .event-last-push-text + %span You pushed to + = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do + %strong= event.ref_name + %span at + %strong= link_to_project event.project + #{time_ago_with_tooltip(event.created_at)} - .pull-right - = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do - Create Merge Request - %hr + .pull-right + = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do + Create Merge Request diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index 30622d8a910..f0a3e416db7 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -1,14 +1,15 @@ - if event = last_push_event - if show_last_push_widget?(event) - .hidden-xs.center - .slead - %span You pushed to - = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do - %strong= event.ref_name - branch - #{time_ago_with_tooltip(event.created_at)} - %div - = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do - Create Merge Request - %hr + .gray-content-block.top-block.clear-block.hidden-xs + .event-last-push + .event-last-push-text + %span You pushed to + = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do + %strong= event.ref_name + branch + #{time_ago_with_tooltip(event.created_at)} + + .pull-right + = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-info btn-sm" do + Create Merge Request -- cgit v1.2.1 From dacff8f014d822fce13bc3bb64582e6db45672d0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 15 Sep 2015 11:17:22 +0200 Subject: Fix UI for web editor Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/common.scss | 4 ++++ app/assets/stylesheets/pages/editor.scss | 18 +++++++++++++++--- app/views/projects/blob/edit.html.haml | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 5e191d5dd4a..3a237bf3228 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -377,4 +377,8 @@ table { height: 56px; margin-top: -$gl-padding; padding-top: $gl-padding; + + &.no-bottom { + margin-bottom: 0; + } } diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 759ba6b1c22..1d565477dd4 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -9,6 +9,10 @@ width: 100%; } + .ace_gutter-cell { + background-color: $background-color; + } + .cancel-btn { color: #B94A48; &:hover { @@ -32,14 +36,12 @@ .file-title { @extend .monospace; - font-size: 14px; - padding: 5px; } .editor-ref { background: $background-color; padding: 11px 15px; - border-right: 1px solid #CCC; + border-right: 1px solid $border-color; display: inline-block; margin: -5px -5px; margin-right: 10px; @@ -50,5 +52,15 @@ display: inline-block; width: 200px; } + + .form-control { + margin-top: -3px; + } + } + + .form-actions { + margin: -$gl-padding; + margin-top: 0; + padding: $gl-padding } } diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index a12cd660fc1..648f15418e0 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -1,6 +1,6 @@ - page_title "Edit", @blob.path, @ref .file-editor - %ul.nav.nav-tabs.js-edit-mode + %ul.center-top-menu.no-bottom.js-edit-mode %li.active = link_to '#editor' do %i.fa.fa-edit -- cgit v1.2.1 From 22bf844869bde4e480d981b2f267bc692e701eb4 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 13:50:24 +0300 Subject: fix specs. Stage 3 --- lib/api/helpers.rb | 38 ++++++++++++++++ lib/api/projects.rb | 36 ---------------- lib/ci/api/entities.rb | 6 ++- lib/ci/api/projects.rb | 27 ++++++------ spec/requests/ci/api/projects_spec.rb | 81 +++++++++++++++++++++-------------- 5 files changed, 106 insertions(+), 82 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index ef0f897a2fb..7fada98fcdc 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -245,6 +245,44 @@ module API error!({ 'message' => message }, status) end + # Projects helpers + + def filter_projects(projects) + # If the archived parameter is passed, limit results accordingly + if params[:archived].present? + projects = projects.where(archived: parse_boolean(params[:archived])) + end + + if params[:search].present? + projects = projects.search(params[:search]) + end + + if params[:ci_enabled_first].present? + projects.includes(:gitlab_ci_service). + reorder("services.active DESC, projects.#{project_order_by} #{project_sort}") + else + projects.reorder(project_order_by => project_sort) + end + end + + def project_order_by + order_fields = %w(id name path created_at updated_at last_activity_at) + + if order_fields.include?(params['order_by']) + params['order_by'] + else + 'created_at' + end + end + + def project_sort + if params["sort"] == 'asc' + :asc + else + :desc + end + end + private def add_pagination_headers(paginated, per_page) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 1f2251c9b9c..c2fb36b4143 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -11,42 +11,6 @@ module API attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true attrs end - - def filter_projects(projects) - # If the archived parameter is passed, limit results accordingly - if params[:archived].present? - projects = projects.where(archived: parse_boolean(params[:archived])) - end - - if params[:search].present? - projects = projects.search(params[:search]) - end - - if params[:ci_enabled_first].present? - projects.includes(:gitlab_ci_service). - reorder("services.active DESC, projects.#{project_order_by} #{project_sort}") - else - projects.reorder(project_order_by => project_sort) - end - end - - def project_order_by - order_fields = %w(id name path created_at updated_at last_activity_at) - - if order_fields.include?(params['order_by']) - params['order_by'] - else - 'created_at' - end - end - - def project_sort - if params["sort"] == 'asc' - :asc - else - :desc - end - end end # Get a projects list for authenticated user diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 1277d68a364..07f25129663 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -34,8 +34,12 @@ module Ci end class Project < Grape::Entity - expose :id, :name, :timeout, :token, :default_ref, :gitlab_url, :path, + expose :id, :name, :token, :default_ref, :gitlab_url, :path, :always_build, :polling_interval, :public, :ssh_url_to_repo, :gitlab_id + + expose :timeout do |model| + model.timeout + end end class RunnerProject < Grape::Entity diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index 556de3bff9f..138667c980f 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -17,7 +17,7 @@ module Ci project = Ci::Project.find(params[:project_id]) - unauthorized! unless current_user.can_manage_project?(project.gitlab_id) + unauthorized! unless can?(current_user, :manage_project, project.gl_project) web_hook = project.web_hooks.new({ url: params[:web_hook] }) @@ -34,9 +34,10 @@ module Ci # Example Request: # GET /projects get do - gitlab_projects = Ci::Project.from_gitlab( - current_user, :authorized, { page: params[:page], per_page: params[:per_page], ci_enabled_first: true } - ) + gitlab_projects = current_user.authorized_projects + gitlab_projects = filter_projects(gitlab_projects) + gitlab_projects = paginate gitlab_projects + ids = gitlab_projects.map { |project| project.id } projects = Ci::Project.where("gitlab_id IN (?)", ids).load @@ -48,9 +49,10 @@ module Ci # Example Request: # GET /projects/owned get "owned" do - gitlab_projects = Ci::Project.from_gitlab( - current_user, :owned, { page: params[:page], per_page: params[:per_page], ci_enabled_first: true } - ) + gitlab_projects = current_user.owned_projects + gitlab_projects = filter_projects(gitlab_projects) + gitlab_projects = paginate gitlab_projects + ids = gitlab_projects.map { |project| project.id } projects = Ci::Project.where("gitlab_id IN (?)", ids).load @@ -65,8 +67,7 @@ module Ci # GET /projects/:id get ":id" do project = Ci::Project.find(params[:id]) - - unauthorized! unless can?(current_user, :read_project, gl_project) + unauthorized! unless can?(current_user, :read_project, project.gl_project) present project, with: Entities::Project end @@ -118,7 +119,7 @@ module Ci put ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :manage_project, gl_project) + unauthorized! unless can?(current_user, :manage_project, project.gl_project) attrs = attributes_for_keys [:name, :gitlab_id, :path, :gitlab_url, :default_ref, :ssh_url_to_repo] @@ -144,7 +145,7 @@ module Ci delete ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :manage_project, gl_project) + unauthorized! unless can?(current_user, :manage_project, project.gl_project) project.destroy end @@ -160,7 +161,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless can?(current_user, :manage_project, gl_project) + unauthorized! unless can?(current_user, :manage_project, project.gl_project) options = { project_id: project.id, @@ -188,7 +189,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless can?(current_user, :manage_project, gl_project) + unauthorized! unless can?(current_user, :manage_project, project.gl_project) options = { project_id: project.id, diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 05f6bd5f4f3..02ac58dbfc3 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -3,8 +3,9 @@ require 'spec_helper' describe Ci::API::API do include ApiHelpers - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } + let(:user) { create(:user) } + let(:private_token) { user.private_token } let(:options) do { @@ -20,11 +21,16 @@ describe Ci::API::API do context "requests for scoped projects" do # NOTE: These ids are tied to the actual projects on demo.gitlab.com describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:ci_project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:ci_project, name: "gitlab-ci", gitlab_id: 4) } + let!(:project1) { FactoryGirl.create(:ci_project) } + let!(:project2) { FactoryGirl.create(:ci_project) } + + before do + project1.gl_project.team << [user, :developer] + project2.gl_project.team << [user, :developer] + end it "should return all projects on the CI instance" do - get api("/projects"), options + get ci_api("/projects"), options expect(response.status).to eq(200) expect(json_response.count).to eq(2) expect(json_response.first["id"]).to eq(project1.id) @@ -33,15 +39,21 @@ describe Ci::API::API do end describe "GET /projects/owned" do - # NOTE: This user doesn't own any of these projects on demo.gitlab.com - let!(:project1) { FactoryGirl.create(:ci_project, name: "gitlabhq", gitlab_id: 3) } - let!(:project2) { FactoryGirl.create(:ci_project, name: "random-project", gitlab_id: 9898) } + let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)} + let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)} + let!(:project1) { FactoryGirl.create(:ci_project, gl_project: gl_project1) } + let!(:project2) { FactoryGirl.create(:ci_project, gl_project: gl_project2) } + + before do + project1.gl_project.team << [user, :developer] + project2.gl_project.team << [user, :developer] + end it "should return all projects on the CI instance" do - get api("/projects/owned"), options + get ci_api("/projects/owned"), options expect(response.status).to eq(200) - expect(json_response.count).to eq(0) + expect(json_response.count).to eq(2) end end end @@ -54,22 +66,23 @@ describe Ci::API::API do before do options.merge!(webhook) + project.gl_project.team << [user, :master] end it "should create webhook for specified project" do - post api("/projects/#{project.id}/webhooks"), options + post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(201) expect(json_response["url"]).to eq(webhook[:web_hook]) end it "fails to create webhook for non existsing project" do - post api("/projects/non-existant-id/webhooks"), options + post ci_api("/projects/non-existant-id/webhooks"), options expect(response.status).to eq(404) end it "non-manager is not authorized" do allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/webhooks"), options + post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(401) end end @@ -82,14 +95,14 @@ describe Ci::API::API do end it "fails to create webhook for not valid url" do - post api("/projects/#{project.id}/webhooks"), options + post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(400) end end context "Missed web_hook parameter" do it "fails to create webhook for not provided url" do - post api("/projects/#{project.id}/webhooks"), options + post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(400) end end @@ -98,9 +111,13 @@ describe Ci::API::API do describe "GET /projects/:id" do let!(:project) { FactoryGirl.create(:ci_project) } + before do + project.gl_project.team << [user, :developer] + end + context "with an existing project" do it "should retrieve the project info" do - get api("/projects/#{project.id}"), options + get ci_api("/projects/#{project.id}"), options expect(response.status).to eq(200) expect(json_response['id']).to eq(project.id) end @@ -108,7 +125,7 @@ describe Ci::API::API do context "with a non-existing project" do it "should return 404 error if project not found" do - get api("/projects/non_existent_id"), options + get ci_api("/projects/non_existent_id"), options expect(response.status).to eq(404) end end @@ -123,19 +140,19 @@ describe Ci::API::API do end it "should update a specific project's information" do - put api("/projects/#{project.id}"), options + put ci_api("/projects/#{project.id}"), options expect(response.status).to eq(200) expect(json_response["name"]).to eq(project_info[:name]) end it "fails to update a non-existing project" do - put api("/projects/non-existant-id"), options + put ci_api("/projects/non-existant-id"), options expect(response.status).to eq(404) end it "non-manager is not authorized" do allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - put api("/projects/#{project.id}"), options + put ci_api("/projects/#{project.id}"), options expect(response.status).to eq(401) end end @@ -144,7 +161,7 @@ describe Ci::API::API do let!(:project) { FactoryGirl.create(:ci_project) } it "should delete a specific project" do - delete api("/projects/#{project.id}"), options + delete ci_api("/projects/#{project.id}"), options expect(response.status).to eq(200) expect { project.reload }.to raise_error @@ -152,12 +169,12 @@ describe Ci::API::API do it "non-manager is not authorized" do allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - delete api("/projects/#{project.id}"), options + delete ci_api("/projects/#{project.id}"), options expect(response.status).to eq(401) end it "is getting not found error" do - delete api("/projects/not-existing_id"), options + delete ci_api("/projects/not-existing_id"), options expect(response.status).to eq(404) end end @@ -180,7 +197,7 @@ describe Ci::API::API do end it "should create a project with valid data" do - post api("/projects"), options + post ci_api("/projects"), options expect(response.status).to eq(201) expect(json_response['name']).to eq(project_info[:name]) end @@ -192,7 +209,7 @@ describe Ci::API::API do end it "should error with invalid data" do - post api("/projects"), options + post ci_api("/projects"), options expect(response.status).to eq(400) end end @@ -202,7 +219,7 @@ describe Ci::API::API do let(:runner) { FactoryGirl.create(:ci_runner) } it "should add the project to the runner" do - post api("/projects/#{project.id}/runners/#{runner.id}"), options + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(201) project.reload @@ -210,16 +227,16 @@ describe Ci::API::API do end it "should fail if it tries to link a non-existing project or runner" do - post api("/projects/#{project.id}/runners/non-existing"), options + post ci_api("/projects/#{project.id}/runners/non-existing"), options expect(response.status).to eq(404) - post api("/projects/non-existing/runners/#{runner.id}"), options + post ci_api("/projects/non-existing/runners/#{runner.id}"), options expect(response.status).to eq(404) end it "non-manager is not authorized" do allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/runners/#{runner.id}"), options + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(401) end end @@ -229,12 +246,12 @@ describe Ci::API::API do let(:runner) { FactoryGirl.create(:ci_runner) } before do - post api("/projects/#{project.id}/runners/#{runner.id}"), options + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options end it "should remove the project from the runner" do expect(project.runners).to be_present - delete api("/projects/#{project.id}/runners/#{runner.id}"), options + delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(200) project.reload @@ -243,7 +260,7 @@ describe Ci::API::API do it "non-manager is not authorized" do allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post api("/projects/#{project.id}/runners/#{runner.id}"), options + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(401) end end -- cgit v1.2.1 From 16ba41a186d5ff393f7d1e3ac1b94fba485aad12 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 14:45:59 +0300 Subject: fix specs. Stage 4 --- lib/ci/api/forks.rb | 5 +---- lib/ci/api/projects.rb | 10 +++++----- spec/requests/ci/api/commits_spec.rb | 6 +++--- spec/requests/ci/api/forks_spec.rb | 11 +++++------ spec/requests/ci/api/projects_spec.rb | 19 +++++++++---------- spec/requests/ci/api/triggers_spec.rb | 16 ++++++++-------- 6 files changed, 31 insertions(+), 36 deletions(-) diff --git a/lib/ci/api/forks.rb b/lib/ci/api/forks.rb index 4ce944df054..152883a599f 100644 --- a/lib/ci/api/forks.rb +++ b/lib/ci/api/forks.rb @@ -18,11 +18,8 @@ module Ci project = Ci::Project.find_by!(gitlab_id: params[:project_id]) authenticate_project_token!(project) - user_session = Ci::UserSession.new - user = user_session.authenticate(private_token: params[:private_token]) - fork = Ci::CreateProjectService.new.execute( - user, + current_user, params[:data], Ci::RoutesHelper.ci_project_url(":project_id"), project diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index 138667c980f..66bcf65e8c4 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -17,7 +17,7 @@ module Ci project = Ci::Project.find(params[:project_id]) - unauthorized! unless can?(current_user, :manage_project, project.gl_project) + unauthorized! unless can?(current_user, :admin_project, project.gl_project) web_hook = project.web_hooks.new({ url: params[:web_hook] }) @@ -119,7 +119,7 @@ module Ci put ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :manage_project, project.gl_project) + unauthorized! unless can?(current_user, :admin_project, project.gl_project) attrs = attributes_for_keys [:name, :gitlab_id, :path, :gitlab_url, :default_ref, :ssh_url_to_repo] @@ -145,7 +145,7 @@ module Ci delete ":id" do project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :manage_project, project.gl_project) + unauthorized! unless can?(current_user, :admin_project, project.gl_project) project.destroy end @@ -161,7 +161,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless can?(current_user, :manage_project, project.gl_project) + unauthorized! unless can?(current_user, :admin_project, project.gl_project) options = { project_id: project.id, @@ -189,7 +189,7 @@ module Ci project = Ci::Project.find(params[:id]) runner = Ci::Runner.find(params[:runner_id]) - unauthorized! unless can?(current_user, :manage_project, project.gl_project) + unauthorized! unless can?(current_user, :admin_project, project.gl_project) options = { project_id: project.id, diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index a4c2a507e88..e89b6651499 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -17,7 +17,7 @@ describe Ci::API::API, 'Commits' do before { commit } it "should return commits per project" do - get api("/commits"), options + get ci_api("/commits"), options expect(response.status).to eq(200) expect(json_response.count).to eq(1) @@ -49,14 +49,14 @@ describe Ci::API::API, 'Commits' do end it "should create a build" do - post api("/commits"), options.merge(data: data) + post ci_api("/commits"), options.merge(data: data) expect(response.status).to eq(201) expect(json_response['sha']).to eq("da1560886d4f094c3e6c9ef40349f7d38b5d27d7") end it "should return 400 error if no data passed" do - post api("/commits"), options + post ci_api("/commits"), options expect(response.status).to eq(400) expect(json_response['message']).to eq("400 (Bad request) \"data\" not given") diff --git a/spec/requests/ci/api/forks_spec.rb b/spec/requests/ci/api/forks_spec.rb index 6f5dc0bc1d9..37fa1e82f25 100644 --- a/spec/requests/ci/api/forks_spec.rb +++ b/spec/requests/ci/api/forks_spec.rb @@ -4,13 +4,12 @@ describe Ci::API::API do include ApiHelpers let(:project) { FactoryGirl.create(:ci_project) } - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let(:private_token) { Network.new.authenticate(access_token: "some_token")["private_token"] } + let(:private_token) { create(:user).private_token } let(:options) do { private_token: private_token, - url: gitlab_url + url: GitlabCi.config.gitlab_ci.url } end @@ -25,7 +24,7 @@ describe Ci::API::API do project_id: project.gitlab_id, project_token: project.token, data: { - id: 2, + id: create(:empty_project).id, name_with_namespace: "Gitlab.org / Underscore", path_with_namespace: "gitlab-org/underscore", default_branch: "master", @@ -40,7 +39,7 @@ describe Ci::API::API do end it "should create a project with valid data" do - post api("/forks"), options + post ci_api("/forks"), options expect(response.status).to eq(201) expect(json_response['name']).to eq("Gitlab.org / Underscore") end @@ -52,7 +51,7 @@ describe Ci::API::API do end it "should error with invalid data" do - post api("/forks"), options + post ci_api("/forks"), options expect(response.status).to eq(400) end end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 02ac58dbfc3..a0072b62fbf 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -66,10 +66,10 @@ describe Ci::API::API do before do options.merge!(webhook) - project.gl_project.team << [user, :master] end it "should create webhook for specified project" do + project.gl_project.team << [user, :master] post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(201) expect(json_response["url"]).to eq(webhook[:web_hook]) @@ -81,7 +81,6 @@ describe Ci::API::API do end it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(401) end @@ -95,6 +94,7 @@ describe Ci::API::API do end it "fails to create webhook for not valid url" do + project.gl_project.team << [user, :master] post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(400) end @@ -102,6 +102,7 @@ describe Ci::API::API do context "Missed web_hook parameter" do it "fails to create webhook for not provided url" do + project.gl_project.team << [user, :master] post ci_api("/projects/#{project.id}/webhooks"), options expect(response.status).to eq(400) end @@ -140,6 +141,7 @@ describe Ci::API::API do end it "should update a specific project's information" do + project.gl_project.team << [user, :master] put ci_api("/projects/#{project.id}"), options expect(response.status).to eq(200) expect(json_response["name"]).to eq(project_info[:name]) @@ -151,7 +153,6 @@ describe Ci::API::API do end it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) put ci_api("/projects/#{project.id}"), options expect(response.status).to eq(401) end @@ -161,14 +162,13 @@ describe Ci::API::API do let!(:project) { FactoryGirl.create(:ci_project) } it "should delete a specific project" do + project.gl_project.team << [user, :master] delete ci_api("/projects/#{project.id}"), options expect(response.status).to eq(200) - expect { project.reload }.to raise_error end it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) delete ci_api("/projects/#{project.id}"), options expect(response.status).to eq(401) end @@ -219,6 +219,7 @@ describe Ci::API::API do let(:runner) { FactoryGirl.create(:ci_runner) } it "should add the project to the runner" do + project.gl_project.team << [user, :master] post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(201) @@ -245,11 +246,10 @@ describe Ci::API::API do let(:project) { FactoryGirl.create(:ci_project) } let(:runner) { FactoryGirl.create(:ci_runner) } - before do + it "should remove the project from the runner" do + project.gl_project.team << [user, :master] post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - end - it "should remove the project from the runner" do expect(project.runners).to be_present delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(200) @@ -259,8 +259,7 @@ describe Ci::API::API do end it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options expect(response.status).to eq(401) end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 56799b5203a..97847bec2af 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -16,17 +16,17 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do - post api("/projects/#{project.id}/refs/master/trigger") + post ci_api("/projects/#{project.id}/refs/master/trigger") expect(response.status).to eq(400) end it 'should return not found if project is not found' do - post api('/projects/0/refs/master/trigger'), options + post ci_api('/projects/0/refs/master/trigger'), options expect(response.status).to eq(404) end it 'should return unauthorized if token is for different project' do - post api("/projects/#{project2.id}/refs/master/trigger"), options + post ci_api("/projects/#{project2.id}/refs/master/trigger"), options expect(response.status).to eq(401) end end @@ -37,14 +37,14 @@ describe Ci::API::API do end it 'should create builds' do - post api("/projects/#{project.id}/refs/master/trigger"), options + post ci_api("/projects/#{project.id}/refs/master/trigger"), options expect(response.status).to eq(201) @commit.builds.reload expect(@commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do - post api("/projects/#{project.id}/refs/other-branch/trigger"), options + post ci_api("/projects/#{project.id}/refs/other-branch/trigger"), options expect(response.status).to eq(400) expect(json_response['message']).to eq('No builds created') end @@ -55,19 +55,19 @@ describe Ci::API::API do end it 'should validate variables to be a hash' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') + post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) + post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do - post api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) + post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) expect(response.status).to eq(201) @commit.builds.reload expect(@commit.builds.first.trigger_request.variables).to eq(variables) -- cgit v1.2.1 From e2cbb36ba9751021174fd188d1c29e736f7b5d3d Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 15:51:03 +0300 Subject: fix specs. Stage 5 --- .rubocop.yml | 1 + app/controllers/ci/admin/application_controller.rb | 4 +-- lib/ci/api/entities.rb | 9 ++--- spec/controllers/ci/projects_controller_spec.rb | 13 +++++--- spec/features/ci/runners_spec.rb | 4 +-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 38 +++++++++++----------- spec/models/ci/build_spec.rb | 2 +- .../ci/project_services/hip_chat_service_spec.rb | 1 - spec/models/ci/service_spec.rb | 6 ++-- spec/models/ci/web_hook_spec.rb | 6 ++-- spec/requests/ci/api/builds_spec.rb | 24 +++++++------- spec/requests/ci/api/projects_spec.rb | 2 +- spec/requests/ci/api/triggers_spec.rb | 4 +-- spec/services/ci/event_service_spec.rb | 4 +-- spec/services/ci/web_hook_service_spec.rb | 8 ++--- spec/support/stub_gitlab_calls.rb | 20 ++++++------ 16 files changed, 72 insertions(+), 74 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ea4d365761e..f372e2f6ba8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -998,6 +998,7 @@ AllCops: - 'tmp/**/*' - 'bin/**/*' - 'lib/backup/**/*' + - 'lib/ci/backup/**/*' - 'lib/tasks/**/*' - 'lib/email_validator.rb' - 'lib/gitlab/upgrader.rb' diff --git a/app/controllers/ci/admin/application_controller.rb b/app/controllers/ci/admin/application_controller.rb index 430fae14c7d..4ec2dc9c2cf 100644 --- a/app/controllers/ci/admin/application_controller.rb +++ b/app/controllers/ci/admin/application_controller.rb @@ -1,8 +1,8 @@ module Ci module Admin class ApplicationController < Ci::ApplicationController - before_filter :authenticate_user! - before_filter :authenticate_admin! + before_action :authenticate_user! + before_action :authenticate_admin! layout "ci/admin" end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 07f25129663..f47bc1236b8 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -11,16 +11,13 @@ module Ci expose :builds end - class BuildOptions < Grape::Entity - expose :image - expose :services - end - class Build < Grape::Entity expose :id, :commands, :ref, :sha, :project_id, :repo_url, :before_sha, :allow_git_fetch, :project_name - expose :options, using: BuildOptions + expose :options do |model| + model.options + end expose :timeout do |model| model.timeout diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index f710e2a3808..b7eb4eac673 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -7,26 +7,29 @@ describe Ci::ProjectsController do describe "POST #build" do it 'should respond 200 if params is ok' do - post :build, id: @project.id, + post :build, { + id: @project.id, ref: 'master', before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', after: '1c8a9df454ef68c22c2a33cca8232bb50849e5c5', token: @project.token, ci_yaml_file: gitlab_ci_yaml, commits: [ { message: "Message" } ] - + } expect(response).to be_success expect(response.code).to eq('201') end it 'should respond 400 if push about removed branch' do - post :build, id: @project.id, + post :build, { + id: @project.id, ref: 'master', before: '2aa371379db71ac89ae20843fcff3b3477cf1a1d', after: '0000000000000000000000000000000000000000', token: @project.token, ci_yaml_file: gitlab_ci_yaml + } expect(response).not_to be_success expect(response.code).to eq('400') @@ -49,7 +52,7 @@ describe Ci::ProjectsController do let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let (:user_data) do + let(:user_data) do data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) data.merge("url" => gitlab_url) end @@ -85,7 +88,7 @@ describe Ci::ProjectsController do describe "GET /gitlab" do let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - let (:user_data) do + let(:user_data) do data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) data.merge("url" => gitlab_url) end diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb index ea28170bb2c..8eea0c4441f 100644 --- a/spec/features/ci/runners_spec.rb +++ b/spec/features/ci/runners_spec.rb @@ -13,8 +13,8 @@ describe "Runners" do # all projects should be authorized for user allow_any_instance_of(Network).to receive(:projects).and_return([ - OpenStruct.new({id: @project.gitlab_id}), - OpenStruct.new({id: @project2.gitlab_id}) + OpenStruct.new({ id: @project.gitlab_id }), + OpenStruct.new({ id: @project2.gitlab_id }) ]) @shared_runner = FactoryGirl.create :shared_runner diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index c99add3f716..b60b4505145 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' describe Ci::GitlabCiYamlProcessor do describe "#builds_for_ref" do - let (:type) { 'test' } + let(:type) { 'test' } it "returns builds if no branch specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec"} + rspec: { script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -29,7 +29,7 @@ describe Ci::GitlabCiYamlProcessor do it "does not return builds if only has another branch" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec", only: ["deploy"]} + rspec: { script: "rspec", only: ["deploy"] } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -40,7 +40,7 @@ describe Ci::GitlabCiYamlProcessor do it "does not return builds if only has regexp with another branch" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec", only: ["/^deploy$/"]} + rspec: { script: "rspec", only: ["/^deploy$/"] } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -51,7 +51,7 @@ describe Ci::GitlabCiYamlProcessor do it "returns builds if only has specified this branch" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec", only: ["master"]} + rspec: { script: "rspec", only: ["master"] } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -62,7 +62,7 @@ describe Ci::GitlabCiYamlProcessor do it "does not build tags" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec", except: ["tags"]} + rspec: { script: "rspec", except: ["tags"] } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -73,7 +73,7 @@ describe Ci::GitlabCiYamlProcessor do it "returns builds if only has a list of branches including specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: {script: "rspec", type: type, only: ["master", "deploy"]} + rspec: { script: "rspec", type: type, only: ["master", "deploy"] } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -85,10 +85,10 @@ describe Ci::GitlabCiYamlProcessor do config = YAML.dump({ before_script: ["pwd"], - build: {script: "build", type: "build", only: ["master", "deploy"]}, - rspec: {script: "rspec", type: type, only: ["master", "deploy"]}, - staging: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, - production: {script: "deploy", type: "deploy", only: ["master", "deploy"]}, + build: { script: "build", type: "build", only: ["master", "deploy"] }, + rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, + staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, }) config_processor = GitlabCiYamlProcessor.new(config) @@ -105,7 +105,7 @@ describe Ci::GitlabCiYamlProcessor do image: "ruby:2.1", services: ["mysql"], before_script: ["pwd"], - rspec: {script: "rspec"} + rspec: { script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -128,10 +128,10 @@ describe Ci::GitlabCiYamlProcessor do it "returns image and service when overridden for job" do config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], + image: "ruby:2.1", + services: ["mysql"], before_script: ["pwd"], - rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } + rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -162,7 +162,7 @@ describe Ci::GitlabCiYamlProcessor do config = YAML.dump({ variables: variables, before_script: ["pwd"], - rspec: {script: "rspec"} + rspec: { script: "rspec" } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -197,7 +197,7 @@ describe Ci::GitlabCiYamlProcessor do end it "returns errors if job image parameter is invalid" do - config = YAML.dump({rspec: { script: "test", image: ["test"] } }) + config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") @@ -239,7 +239,7 @@ describe Ci::GitlabCiYamlProcessor do end it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do - config = YAML.dump({ extra: {services: "test" } }) + config = YAML.dump({ extra: { services: "test" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") @@ -267,7 +267,7 @@ describe Ci::GitlabCiYamlProcessor do end it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 4f57003565a..dacdf38a875 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -56,7 +56,7 @@ describe Ci::Build do end let(:create_from_build) { Ci::Build.create_from build } - it ('there should be a pending task') do + it 'there should be a pending task' do expect(Ci::Build.pending.count(:all)).to eq 0 create_from_build expect(Ci::Build.pending.count(:all)).to be > 0 diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index 33a3a8109e5..59deb19c23a 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -72,4 +72,3 @@ describe Ci::HipChatService do end end end - diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 8c4df391555..04807a705eb 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -29,9 +29,9 @@ describe Ci::Service do end describe "Testable" do - let (:project) { FactoryGirl.create :ci_project } - let (:commit) { FactoryGirl.create :ci_commit, project: project } - let (:build) { FactoryGirl.create :ci_build, commit: commit } + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit } before do allow(@service).to receive_messages( diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index bb58f645caf..66170d111a9 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -36,7 +36,7 @@ describe Ci::WebHook do before(:each) do @web_hook = FactoryGirl.create(:ci_web_hook) @project = @web_hook.project - @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} + @data = { before: 'oldrev', after: 'newrev', ref: 'ref' } WebMock.stub_request(:post, @web_hook.url) end @@ -56,9 +56,7 @@ describe Ci::WebHook do it "catches exceptions" do expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") - expect { - @web_hook.execute(@data) - }.to raise_error + expect{ @web_hook.execute(@data) }.to raise_error end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 61f9d940c3b..c25d1823306 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -11,7 +11,7 @@ describe Ci::API::API do let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } before do - FactoryGirl.create :runner_project, project_id: project.id, runner_id: runner.id + FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id end describe "POST /builds/register" do @@ -20,7 +20,7 @@ describe Ci::API::API do commit.create_builds build = commit.builds.first - post api("/builds/register"), token: runner.token, info: { platform: :darwin } + post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response['sha']).to eq(build.sha) @@ -28,7 +28,7 @@ describe Ci::API::API do end it "should return 404 error if no pending build found" do - post api("/builds/register"), token: runner.token + post ci_api("/builds/register"), token: runner.token expect(response.status).to eq(404) end @@ -37,7 +37,7 @@ describe Ci::API::API do commit = FactoryGirl.create(:ci_commit, project: shared_project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) - post api("/builds/register"), token: runner.token + post ci_api("/builds/register"), token: runner.token expect(response.status).to eq(404) end @@ -46,7 +46,7 @@ describe Ci::API::API do commit = FactoryGirl.create(:ci_commit, project: project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) - post api("/builds/register"), token: shared_runner.token + post ci_api("/builds/register"), token: shared_runner.token expect(response.status).to eq(404) end @@ -55,7 +55,7 @@ describe Ci::API::API do commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds - post api("/builds/register"), token: runner.token, info: { platform: :darwin } + post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] }) @@ -64,9 +64,9 @@ describe Ci::API::API do it "returns variables" do commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds - project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") - post api("/builds/register"), token: runner.token, info: { platform: :darwin } + post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ @@ -81,9 +81,9 @@ describe Ci::API::API do trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds(trigger_request) - project.variables << Variable.new(key: "SECRET_KEY", value: "secret_value") + project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") - post api("/builds/register"), token: runner.token, info: { platform: :darwin } + post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ @@ -100,14 +100,14 @@ describe Ci::API::API do it "should update a running build" do build.run! - put api("/builds/#{build.id}"), token: runner.token + put ci_api("/builds/#{build.id}"), token: runner.token expect(response.status).to eq(200) end it 'Should not override trace information when no trace is given' do build.run! build.update!(trace: 'hello_world') - put api("/builds/#{build.id}"), token: runner.token + put ci_api("/builds/#{build.id}"), token: runner.token expect(build.reload.trace).to eq 'hello_world' end end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index a0072b62fbf..2adae52e79e 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -62,7 +62,7 @@ describe Ci::API::API do let!(:project) { FactoryGirl.create(:ci_project) } context "Valid Webhook URL" do - let!(:webhook) { {web_hook: "http://example.com/sth/1/ala_ma_kota" } } + let!(:webhook) { { web_hook: "http://example.com/sth/1/ala_ma_kota" } } before do options.merge!(webhook) diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 97847bec2af..ff6fdbdd6f1 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -8,11 +8,11 @@ describe Ci::API::API do let!(:project) { FactoryGirl.create(:ci_project) } let!(:project2) { FactoryGirl.create(:ci_project) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } - let(:options) { + let(:options) do { token: trigger_token } - } + end context 'Handles errors' do it 'should return bad request if token is missing' do diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index b6ad262152d..bdebab1ac24 100644 --- a/spec/services/ci/event_service_spec.rb +++ b/spec/services/ci/event_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe Ci::EventService do - let (:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } - let (:user) { double(username: "root", id: 1) } + let(:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } + let(:user) { double(username: "root", id: 1) } before do Event.destroy_all diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index 6f882e6fdad..d2f08959cb1 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' describe Ci::WebHookService do - let (:project) { FactoryGirl.create :project } - let (:commit) { FactoryGirl.create :commit, project: project } - let (:build) { FactoryGirl.create :build, commit: commit } - let (:hook) { FactoryGirl.create :web_hook, project: project } + let(:project) { FactoryGirl.create :project } + let(:commit) { FactoryGirl.create :commit, project: project } + let(:build) { FactoryGirl.create :build, commit: commit } + let(:hook) { FactoryGirl.create :web_hook, project: project } describe :execute do it "should execute successfully" do diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 41e4c3e275b..5e6744afda1 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -24,20 +24,20 @@ module StubGitlabCalls stub_request(:post, "#{gitlab_url}api/v3/session.json"). with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}", - headers: {'Content-Type'=>'application/json'}). - to_return(status: 201, body: f, headers: {'Content-Type'=>'application/json'}) + headers: { 'Content-Type'=>'application/json' }). + to_return(status: 201, body: f, headers: { 'Content-Type'=>'application/json' }) end def stub_user f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz"). - with(headers: {'Content-Type'=>'application/json'}). - to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) + with(headers: { 'Content-Type'=>'application/json' }). + to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' }) stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token"). - with(headers: {'Content-Type'=>'application/json'}). - to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) + with(headers: { 'Content-Type'=>'application/json' }). + to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' }) end def stub_project_8 @@ -54,19 +54,19 @@ module StubGitlabCalls f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json')) stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(headers: {'Content-Type'=>'application/json'}). - to_return(status: 200, body: f, headers: {'Content-Type'=>'application/json'}) + with(headers: { 'Content-Type'=>'application/json' }). + to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' }) end def stub_projects_owned stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz"). - with(headers: {'Content-Type'=>'application/json'}). + with(headers: { 'Content-Type'=>'application/json' }). to_return(status: 200, body: "", headers: {}) end def stub_ci_enable stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz"). - with(headers: {'Content-Type'=>'application/json'}). + with(headers: { 'Content-Type'=>'application/json' }). to_return(status: 200, body: "", headers: {}) end -- cgit v1.2.1 From 88b3195ecfd2d0d74c4e76ce79961cb6db2f8643 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 16:00:45 +0300 Subject: fix specs. Stage 6 --- spec/mailers/ci/notify_spec.rb | 12 +- .../ci/project_services/hip_chat_service_spec.rb | 4 +- spec/services/ci/create_project_service_spec.rb | 12 +- .../ci/create_trigger_request_service_spec.rb | 14 +- spec/services/ci/event_service_spec.rb | 6 +- spec/services/ci/image_for_build_service_spec.rb | 86 ++++++------ spec/services/ci/register_build_service_spec.rb | 154 +++++++++++---------- spec/services/ci/web_hook_service_spec.rb | 4 +- 8 files changed, 148 insertions(+), 144 deletions(-) diff --git a/spec/mailers/ci/notify_spec.rb b/spec/mailers/ci/notify_spec.rb index 6a2c845cd0e..20d8ddcd135 100644 --- a/spec/mailers/ci/notify_spec.rb +++ b/spec/mailers/ci/notify_spec.rb @@ -1,17 +1,17 @@ require 'spec_helper' -describe Notify do +describe Ci::Notify do include EmailSpec::Helpers include EmailSpec::Matchers before do - @project = FactoryGirl.create :project - @commit = FactoryGirl.create :commit, project: @project - @build = FactoryGirl.create :build, commit: @commit + @project = FactoryGirl.create :ci_project + @commit = FactoryGirl.create :ci_commit, project: @project + @build = FactoryGirl.create :ci_build, commit: @commit end describe 'build success' do - subject { Notify.build_success_email(@build.id, 'wow@example.com') } + subject { Ci::Notify.build_success_email(@build.id, 'wow@example.com') } it 'has the correct subject' do should have_subject /Build success for/ @@ -23,7 +23,7 @@ describe Notify do end describe 'build fail' do - subject { Notify.build_fail_email(@build.id, 'wow@example.com') } + subject { Ci::Notify.build_fail_email(@build.id, 'wow@example.com') } it 'has the correct subject' do should have_subject /Build failed for/ diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index 59deb19c23a..063d46b84d4 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -53,13 +53,13 @@ describe Ci::HipChatService do it "should call the HipChat API" do service.execute(build) - HipChatNotifierWorker.drain + Ci::HipChatNotifierWorker.drain expect( WebMock ).to have_requested(:post, api_url).once end it "calls the worker with expected arguments" do - expect( HipChatNotifierWorker ).to receive(:perform_async) \ + expect( Ci::HipChatNotifierWorker ).to receive(:perform_async) \ .with(an_instance_of(String), hash_including( token: 'a1b2c3d4e5f6', room: 123, diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index c4b62e4fa9e..234a778f8cc 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -5,14 +5,14 @@ describe Ci::CreateProjectService do let(:current_user) { double.as_null_object } let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - before { Network.any_instance.stub(enable_ci: true) } + before { allow_any_instance_of(Network).to receive_messages(enable_ci: true) } describe :execute do context 'valid params' do let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } - it { project.should be_kind_of(Project) } - it { project.should be_persisted } + it { expect(project).to be_kind_of(Project) } + it { expect(project).to be_persisted } end context 'without project dump' do @@ -31,9 +31,9 @@ describe Ci::CreateProjectService do project = service.execute(current_user, project_dump, 'http://localhost/projects/:project_id', origin_project) - project.shared_runners_enabled.should be_true - project.public.should be_true - project.allow_git_fetch.should be_true + expect(project.shared_runners_enabled).to be_truthy + expect(project.public).to be_truthy + expect(project.allow_git_fetch).to be_truthy end end end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index c874697c456..9082c741ead 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -13,14 +13,14 @@ describe Ci::CreateTriggerRequestService do @commit = FactoryGirl.create :commit, project: project end - it { subject.should be_kind_of(TriggerRequest) } - it { subject.commit.should == @commit } + it { expect(subject).to be_kind_of(TriggerRequest) } + it { expect(subject.commit).to eq(@commit) } end context 'no commit for ref' do subject { service.execute(project, trigger, 'other-branch') } - it { subject.should be_nil } + it { expect(subject).to be_nil } end context 'no builds created' do @@ -30,7 +30,7 @@ describe Ci::CreateTriggerRequestService do FactoryGirl.create :commit_without_jobs, project: project end - it { subject.should be_nil } + it { expect(subject).to be_nil } end context 'for multiple commits' do @@ -43,9 +43,9 @@ describe Ci::CreateTriggerRequestService do end context 'retries latest one' do - it { subject.should be_kind_of(TriggerRequest) } - it { subject.should be_persisted } - it { subject.commit.should == @commit2 } + it { expect(subject).to be_kind_of(TriggerRequest) } + it { expect(subject).to be_persisted } + it { expect(subject.commit).to eq(@commit2) } end end end diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index bdebab1ac24..c8c4c45cc31 100644 --- a/spec/services/ci/event_service_spec.rb +++ b/spec/services/ci/event_service_spec.rb @@ -12,7 +12,7 @@ describe Ci::EventService do it "creates event" do EventService.new.remove_project(user, project) - Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been removed by root" + expect(Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been removed by root") end end @@ -20,7 +20,7 @@ describe Ci::EventService do it "creates event" do EventService.new.create_project(user, project) - Event.admin.last.description.should == "Project \"GitLab / gitlab-shell\" has been created by root" + expect(Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been created by root") end end @@ -28,7 +28,7 @@ describe Ci::EventService do it "creates event" do EventService.new.change_project_settings(user, project) - Event.last.description.should == "User \"root\" updated projects settings" + expect(Event.last.description).to eq("User \"root\" updated projects settings") end end end diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index dadc919bae1..fdeb754d689 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -1,46 +1,48 @@ require 'spec_helper' -describe Ci::ImageForBuildService do - let(:service) { ImageForBuildService.new } - let(:project) { FactoryGirl.create(:project) } - let(:commit) { FactoryGirl.create(:commit, project: project, ref: 'master') } - let(:build) { FactoryGirl.create(:build, commit: commit) } - - describe :execute do - before { build } - - context 'branch name' do - before { build.run! } - let(:image) { service.execute(project, ref: 'master') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-running.svg') } - it { image.name.should == 'build-running.svg' } - end - - context 'unknown branch name' do - let(:image) { service.execute(project, ref: 'feature') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-unknown.svg') } - it { image.name.should == 'build-unknown.svg' } - end - - context 'commit sha' do - before { build.run! } - let(:image) { service.execute(project, sha: build.sha) } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-running.svg') } - it { image.name.should == 'build-running.svg' } - end - - context 'unknown commit sha' do - let(:image) { service.execute(project, sha: '0000000') } - - it { image.should be_kind_of(OpenStruct) } - it { image.path.to_s.should include('public/build-unknown.svg') } - it { image.name.should == 'build-unknown.svg' } +module Ci + describe ImageForBuildService do + let(:service) { ImageForBuildService.new } + let(:project) { FactoryGirl.create(:ci_project) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project, ref: 'master') } + let(:build) { FactoryGirl.create(:ci_build, commit: commit) } + + describe :execute do + before { build } + + context 'branch name' do + before { build.run! } + let(:image) { service.execute(project, ref: 'master') } + + it { expect(image).to be_kind_of(OpenStruct) } + it { expect(image.path.to_s).to include('public/ci/build-running.svg') } + it { expect(image.name).to eq('build-running.svg') } + end + + context 'unknown branch name' do + let(:image) { service.execute(project, ref: 'feature') } + + it { expect(image).to be_kind_of(OpenStruct) } + it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') } + it { expect(image.name).to eq('build-unknown.svg') } + end + + context 'commit sha' do + before { build.run! } + let(:image) { service.execute(project, sha: build.sha) } + + it { expect(image).to be_kind_of(OpenStruct) } + it { expect(image.path.to_s).to include('public/ci/build-running.svg') } + it { expect(image.name).to eq('build-running.svg') } + end + + context 'unknown commit sha' do + let(:image) { service.execute(project, sha: '0000000') } + + it { expect(image).to be_kind_of(OpenStruct) } + it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') } + it { expect(image.name).to eq('build-unknown.svg') } + end end end -end +end \ No newline at end of file diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 6d0ae76a241..7d665d9a112 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -1,88 +1,90 @@ -require 'spec_helper' - -describe Ci::RegisterBuildService do - let!(:service) { RegisterBuildService.new } - let!(:project) { FactoryGirl.create :project } - let!(:commit) { FactoryGirl.create :commit, project: project } - let!(:pending_build) { FactoryGirl.create :build, project: project, commit: commit } - let!(:shared_runner) { FactoryGirl.create(:runner, is_shared: true) } - let!(:specific_runner) { FactoryGirl.create(:runner, is_shared: false) } - - before do - specific_runner.assign_to(project) - end - - describe :execute do - context 'runner follow tag list' do - it "picks build with the same tag" do - pending_build.tag_list = ["linux"] - pending_build.save - specific_runner.tag_list = ["linux"] - service.execute(specific_runner).should == pending_build - end - - it "does not pick build with different tag" do - pending_build.tag_list = ["linux"] - pending_build.save - specific_runner.tag_list = ["win32"] - service.execute(specific_runner).should be_false - end - - it "picks build without tag" do - service.execute(specific_runner).should == pending_build - end - - it "does not pick build with tag" do - pending_build.tag_list = ["linux"] - pending_build.save - service.execute(specific_runner).should be_false - end - - it "pick build without tag" do - specific_runner.tag_list = ["win32"] - service.execute(specific_runner).should == pending_build - end + require 'spec_helper' + +module Ci + describe RegisterBuildService do + let!(:service) { RegisterBuildService.new } + let!(:project) { FactoryGirl.create :ci_project } + let!(:commit) { FactoryGirl.create :ci_commit, project: project } + let!(:pending_build) { FactoryGirl.create :ci_build, project: project, commit: commit } + let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } + let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } + + before do + specific_runner.assign_to(project) end - context 'allow shared runners' do - before do - project.shared_runners_enabled = true - project.save - end - - context 'shared runner' do - let(:build) { service.execute(shared_runner) } - - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == shared_runner } + describe :execute do + context 'runner follow tag list' do + it "picks build with the same tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["linux"] + expect(service.execute(specific_runner)).to eq(pending_build) + end + + it "does not pick build with different tag" do + pending_build.tag_list = ["linux"] + pending_build.save + specific_runner.tag_list = ["win32"] + expect(service.execute(specific_runner)).to be_falsey + end + + it "picks build without tag" do + expect(service.execute(specific_runner)).to eq(pending_build) + end + + it "does not pick build with tag" do + pending_build.tag_list = ["linux"] + pending_build.save + expect(service.execute(specific_runner)).to be_falsey + end + + it "pick build without tag" do + specific_runner.tag_list = ["win32"] + expect(service.execute(specific_runner)).to eq(pending_build) + end end - context 'specific runner' do - let(:build) { service.execute(specific_runner) } - - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == specific_runner } + context 'allow shared runners' do + before do + project.shared_runners_enabled = true + project.save + end + + context 'shared runner' do + let(:build) { service.execute(shared_runner) } + + it { expect(build).to be_kind_of(Build) } + it { expect(build).to be_valid } + it { expect(build).to be_running } + it { expect(build.runner).to eq(shared_runner) } + end + + context 'specific runner' do + let(:build) { service.execute(specific_runner) } + + it { expect(build).to be_kind_of(Build) } + it { expect(build).to be_valid } + it { expect(build).to be_running } + it { expect(build.runner).to eq(specific_runner) } + end end - end - context 'disallow shared runners' do - context 'shared runner' do - let(:build) { service.execute(shared_runner) } + context 'disallow shared runners' do + context 'shared runner' do + let(:build) { service.execute(shared_runner) } - it { build.should be_nil } - end + it { expect(build).to be_nil } + end - context 'specific runner' do - let(:build) { service.execute(specific_runner) } + context 'specific runner' do + let(:build) { service.execute(specific_runner) } - it { build.should be_kind_of(Build) } - it { build.should be_valid } - it { build.should be_running } - it { build.runner.should == specific_runner } + it { expect(build).to be_kind_of(Build) } + it { expect(build).to be_valid } + it { expect(build).to be_running } + it { expect(build.runner).to eq(specific_runner) } + end end end end diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index d2f08959cb1..b893b1f23f2 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -9,13 +9,13 @@ describe Ci::WebHookService do describe :execute do it "should execute successfully" do stub_request(:post, hook.url).to_return(status: 200) - WebHookService.new.build_end(build).should be_true + expect(WebHookService.new.build_end(build)).to be_truthy end end context 'build_data' do it "contains all needed fields" do - build_data(build).should include( + expect(build_data(build)).to include( :build_id, :project_id, :ref, -- cgit v1.2.1 From 4d2b67acc0bbd3337e8d96ac9878a3dc08e23ac0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 14 Sep 2015 22:31:45 -0700 Subject: Don't display "git clone --bare " if no URL is available Introduced in !1231 --- app/views/projects/imports/show.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index 39fe0fc1c4f..91ecd82c04b 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -4,7 +4,8 @@ %h2 %i.fa.fa-spinner.fa-spin Import in progress. - %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} + - if @project.import_url.present? + %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} %p Please wait while we import the repository for you. Refresh at will. :javascript new ProjectImport(); -- cgit v1.2.1 From 2abe7338665a87581ac99db93c3c60e5fc95ac1a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 15 Sep 2015 06:45:03 -0700 Subject: Display "Forking in Progress" when importing a forked project --- app/views/projects/imports/show.html.haml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index 91ecd82c04b..06886d215a3 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -3,8 +3,11 @@ .center %h2 %i.fa.fa-spinner.fa-spin - Import in progress. - - if @project.import_url.present? + - if @project.forked? + Forking in progress. + - else + Import in progress. + - unless @project.forked? %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} %p Please wait while we import the repository for you. Refresh at will. :javascript -- cgit v1.2.1 From b87ca7500f174cc9a4e90b262b02aa9bf695fbee Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 16:42:02 +0300 Subject: fix specs. Stage 7 --- app/models/project_services/ci/hip_chat_message.rb | 2 +- app/models/project_services/ci/slack_message.rb | 4 +- spec/features/ci/admin/builds_spec.rb | 40 +- spec/features/ci/admin/events_spec.rb | 4 +- spec/features/ci/admin/projects_spec.rb | 6 +- spec/features/ci/admin/runners_spec.rb | 22 +- spec/features/ci/events_spec.rb | 4 +- spec/features/ci/lint_spec.rb | 4 +- spec/features/ci/runners_spec.rb | 28 +- spec/features/ci/triggers_spec.rb | 6 +- spec/features/ci/variables_spec.rb | 4 +- spec/lib/ci/charts_spec.rb | 10 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 602 +++++++++++---------- spec/models/ci/mail_service_spec.rb | 2 +- .../ci/project_services/slack_message_spec.rb | 4 +- .../ci/project_services/slack_service_spec.rb | 4 +- spec/models/ci/runner_spec.rb | 4 +- spec/models/ci/service_spec.rb | 2 +- spec/services/ci/create_project_service_spec.rb | 6 +- .../ci/create_trigger_request_service_spec.rb | 20 +- spec/services/ci/event_service_spec.rb | 14 +- spec/services/ci/web_hook_service_spec.rb | 12 +- spec/support/login_helpers.rb | 4 + 23 files changed, 406 insertions(+), 402 deletions(-) diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb index 3e9f99e7eaf..58825fe066c 100644 --- a/app/models/project_services/ci/hip_chat_message.rb +++ b/app/models/project_services/ci/hip_chat_message.rb @@ -11,7 +11,7 @@ module Ci lines.push("#{project.name} - ") if commit.matrix? - lines.push("Commit ##{commit.id}
    ") + lines.push("Commit ##{commit.id}
    ") else first_build = commit.builds_without_retry.first lines.push("Build '#{first_build.name}' ##{first_build.id}
    ") diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb index 7d884849bf3..491ace50111 100644 --- a/app/models/project_services/ci/slack_message.rb +++ b/app/models/project_services/ci/slack_message.rb @@ -46,10 +46,10 @@ module Ci def attachment_message out = "<#{Ci::RoutesHelper.ci_project_url(project)}|#{project_name}>: " if commit.matrix? - out << "Commit <#{Ci::RoutesHelper.ci_project_ref_commit_url(project, commit.ref, commit.sha)}|\##{commit.id}> " + out << "Commit <#{Ci::RoutesHelper.ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> " else build = commit.builds_without_retry.first - out << "Build <#{Ci::RoutesHelper.ci_project_build_url(project, build)}|\##{build.id}> " + out << "Build <#{Ci::RoutesHelper.ci_project_build_path(project, build)}|\##{build.id}> " end out << "(<#{commit_sha_link}|#{commit.short_sha}>) " out << "of <#{commit_ref_link}|#{commit.ref}> " diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/ci/admin/builds_spec.rb index d524dc29795..88ef9c144af 100644 --- a/spec/features/ci/admin/builds_spec.rb +++ b/spec/features/ci/admin/builds_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' describe "Admin Builds" do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit } + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit } before do - skip_admin_auth + skip_ci_admin_auth login_as :user end describe "GET /admin/builds" do before do build - visit admin_builds_path + visit ci_admin_builds_path end it { expect(page).to have_content "All builds" } @@ -22,23 +22,23 @@ describe "Admin Builds" do describe "Tabs" do it "shows all builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit admin_builds_path + visit ci_admin_builds_path expect(page.all(".build-link").size).to eq(4) end it "shows pending builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit admin_builds_path + visit ci_admin_builds_path within ".nav.nav-tabs" do click_on "Pending" @@ -51,12 +51,12 @@ describe "Admin Builds" do end it "shows running builds" do - build = FactoryGirl.create :build, commit: commit, status: "pending" - build1 = FactoryGirl.create :build, commit: commit, status: "running" - build2 = FactoryGirl.create :build, commit: commit, status: "success" - build3 = FactoryGirl.create :build, commit: commit, status: "failed" + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - visit admin_builds_path + visit ci_admin_builds_path within ".nav.nav-tabs" do click_on "Running" diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb index 4f5dbd9fb6b..6b6ace06c53 100644 --- a/spec/features/ci/admin/events_spec.rb +++ b/spec/features/ci/admin/events_spec.rb @@ -4,14 +4,14 @@ describe "Admin Events" do let(:event) { FactoryGirl.create :admin_event } before do - skip_admin_auth + skip_ci_admin_auth login_as :user end describe "GET /admin/events" do before do event - visit admin_events_path + visit ci_admin_events_path end it { expect(page).to have_content "Events" } diff --git a/spec/features/ci/admin/projects_spec.rb b/spec/features/ci/admin/projects_spec.rb index 9113300077d..b88f55a6807 100644 --- a/spec/features/ci/admin/projects_spec.rb +++ b/spec/features/ci/admin/projects_spec.rb @@ -1,17 +1,17 @@ require 'spec_helper' describe "Admin Projects" do - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } before do - skip_admin_auth + skip_ci_admin_auth login_as :user end describe "GET /admin/projects" do before do project - visit admin_projects_path + visit ci_admin_projects_path end it { expect(page).to have_content "Projects" } diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index fa3beb7b915..644d48ac298 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -2,16 +2,16 @@ require 'spec_helper' describe "Admin Runners" do before do - skip_admin_auth + skip_ci_admin_auth login_as :user end describe "Runners page" do before do - runner = FactoryGirl.create(:runner) - commit = FactoryGirl.create(:commit) - FactoryGirl.create(:build, commit: commit, runner_id: runner.id) - visit admin_runners_path + runner = FactoryGirl.create(:ci_runner) + commit = FactoryGirl.create(:ci_commit) + FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) + visit ci_admin_runners_path end it { page.has_text? "Manage Runners" } @@ -20,8 +20,8 @@ describe "Admin Runners" do describe 'search' do before do - FactoryGirl.create :runner, description: 'foo' - FactoryGirl.create :runner, description: 'bar' + FactoryGirl.create :ci_runner, description: 'foo' + FactoryGirl.create :ci_runner, description: 'bar' fill_in 'search', with: 'foo' click_button 'Search' @@ -33,12 +33,12 @@ describe "Admin Runners" do end describe "Runner show page" do - let(:runner) { FactoryGirl.create :runner } + let(:runner) { FactoryGirl.create :ci_runner } before do - FactoryGirl.create(:project, name: "foo") - FactoryGirl.create(:project, name: "bar") - visit admin_runner_path(runner) + FactoryGirl.create(:ci_project, name: "foo") + FactoryGirl.create(:ci_project, name: "bar") + visit ci_admin_runner_path(runner) end describe 'runner info' do diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb index d1bcf493eaa..dd13d7d66dc 100644 --- a/spec/features/ci/events_spec.rb +++ b/spec/features/ci/events_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe "Events" do - let(:project) { FactoryGirl.create :project } + let(:project) { FactoryGirl.create :ci_project } let(:event) { FactoryGirl.create :admin_event, project: project } before do @@ -11,7 +11,7 @@ describe "Events" do describe "GET /project/:id/events" do before do event - visit project_events_path(project) + visit ci_project_events_path(project) end it { expect(page).to have_content "Events" } diff --git a/spec/features/ci/lint_spec.rb b/spec/features/ci/lint_spec.rb index 820ed5b4716..5d8f56e2cfb 100644 --- a/spec/features/ci/lint_spec.rb +++ b/spec/features/ci/lint_spec.rb @@ -7,7 +7,7 @@ describe "Lint" do it "Yaml parsing", js: true do content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - visit lint_path + visit ci_lint_path fill_in "content", with: content click_on "Validate" within "table" do @@ -19,7 +19,7 @@ describe "Lint" do end it "Yaml parsing with error", js: true do - visit lint_path + visit ci_lint_path fill_in "content", with: "" click_on "Validate" expect(page).to have_content("Status: syntax is incorrect") diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb index 8eea0c4441f..86ccac29c74 100644 --- a/spec/features/ci/runners_spec.rb +++ b/spec/features/ci/runners_spec.rb @@ -7,8 +7,8 @@ describe "Runners" do describe "specific runners" do before do - @project = FactoryGirl.create :project - @project2 = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project + @project2 = FactoryGirl.create :ci_project stub_js_gitlab_calls # all projects should be authorized for user @@ -17,22 +17,22 @@ describe "Runners" do OpenStruct.new({ id: @project2.gitlab_id }) ]) - @shared_runner = FactoryGirl.create :shared_runner - @specific_runner = FactoryGirl.create :specific_runner - @specific_runner2 = FactoryGirl.create :specific_runner + @shared_runner = FactoryGirl.create :ci_shared_runner + @specific_runner = FactoryGirl.create :ci_specific_runner + @specific_runner2 = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner @project2.runners << @specific_runner2 end it "places runners in right places" do - visit project_runners_path(@project) + visit ci_project_runners_path(@project) expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name) expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name) expect(page.find(".available-shared-runners")).to have_content(@shared_runner.display_name) end it "enables specific runner for project" do - visit project_runners_path(@project) + visit ci_project_runners_path(@project) within ".available-specific-runners" do click_on "Enable for this project" @@ -44,7 +44,7 @@ describe "Runners" do it "disables specific runner for project" do @project2.runners << @specific_runner - visit project_runners_path(@project) + visit ci_project_runners_path(@project) within ".activated-specific-runners" do click_on "Disable for this project" @@ -54,7 +54,7 @@ describe "Runners" do end it "removes specific runner for project if this is last project for that runners" do - visit project_runners_path(@project) + visit ci_project_runners_path(@project) within ".activated-specific-runners" do click_on "Remove runner" @@ -66,12 +66,12 @@ describe "Runners" do describe "shared runners" do before do - @project = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project stub_js_gitlab_calls end it "enables shared runners" do - visit project_runners_path(@project) + visit ci_project_runners_path(@project) click_on "Enable shared runners" @@ -81,14 +81,14 @@ describe "Runners" do describe "show page" do before do - @project = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project stub_js_gitlab_calls - @specific_runner = FactoryGirl.create :specific_runner + @specific_runner = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner end it "shows runner information" do - visit project_runners_path(@project) + visit ci_project_runners_path(@project) click_on @specific_runner.short_sha diff --git a/spec/features/ci/triggers_spec.rb b/spec/features/ci/triggers_spec.rb index 39ef67578fb..c65bea9dbcf 100644 --- a/spec/features/ci/triggers_spec.rb +++ b/spec/features/ci/triggers_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe 'Variables' do +describe 'Triggers' do before do login_as :user - @project = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project stub_js_gitlab_calls - visit project_triggers_path(@project) + visit ci_project_triggers_path(@project) end context 'create a trigger' do diff --git a/spec/features/ci/variables_spec.rb b/spec/features/ci/variables_spec.rb index 2e75c9fa1a7..84d6bfa0f32 100644 --- a/spec/features/ci/variables_spec.rb +++ b/spec/features/ci/variables_spec.rb @@ -7,12 +7,12 @@ describe "Variables" do describe "specific runners" do before do - @project = FactoryGirl.create :project + @project = FactoryGirl.create :ci_project stub_js_gitlab_calls end it "creates variable", js: true do - visit project_variables_path(@project) + visit ci_project_variables_path(@project) click_on "Add a variable" fill_in "Key", with: "SECRET_KEY" fill_in "Value", with: "SECRET_VALUE" diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb index 236cfc2a1f6..24894e81983 100644 --- a/spec/lib/ci/charts_spec.rb +++ b/spec/lib/ci/charts_spec.rb @@ -4,14 +4,14 @@ describe "Charts" do context "build_times" do before do - @project = FactoryGirl.create(:project) - @commit = FactoryGirl.create(:commit, project: @project) - FactoryGirl.create(:build, commit: @commit) + @project = FactoryGirl.create(:ci_project) + @commit = FactoryGirl.create(:ci_commit, project: @project) + FactoryGirl.create(:ci_build, commit: @commit) end it 'should return build times in minutes' do - chart = Charts::BuildTime.new(@project) - chart.build_times.should == [2] + chart = Ci::Charts::BuildTime.new(@project) + expect(chart.build_times).to eq([2]) end end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index b60b4505145..49482ac2b12 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -1,311 +1,313 @@ require 'spec_helper' -describe Ci::GitlabCiYamlProcessor do - - describe "#builds_for_ref" do - let(:type) { 'test' } - - it "returns builds if no branch specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec" } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 - config_processor.builds_for_stage_and_ref(type, "master").first.should == { - stage: "test", - except: nil, - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: {}, - allow_failure: false - } +module Ci + describe GitlabCiYamlProcessor do + + describe "#builds_for_ref" do + let(:type) { 'test' } + + it "returns builds if no branch specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({ + stage: "test", + except: nil, + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: {}, + allow_failure: false + }) + end + + it "does not return builds if only has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["deploy"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end + + it "does not return builds if only has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["/^deploy$/"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end + + it "returns builds if only has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["master"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end + + it "does not build tags" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["tags"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "0-1", true).size).to eq(0) + end + + it "returns builds if only has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["master", "deploy"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "returns build only for specified type" do + + config = YAML.dump({ + before_script: ["pwd"], + build: { script: "build", type: "build", only: ["master", "deploy"] }, + rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, + staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + end end - it "does not return builds if only has another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["deploy"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 - end - - it "does not return builds if only has regexp with another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["/^deploy$/"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 0 - end - - it "returns builds if only has specified this branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["master"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "master").size.should == 1 - end - - it "does not build tags" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", except: ["tags"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "0-1", true).size.should == 0 - end - - it "returns builds if only has a list of branches including specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["master", "deploy"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 - end - - it "returns build only for specified type" do - - config = YAML.dump({ - before_script: ["pwd"], - build: { script: "build", type: "build", only: ["master", "deploy"] }, - rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, - staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("production", "deploy").size.should == 0 - config_processor.builds_for_stage_and_ref(type, "deploy").size.should == 1 - config_processor.builds_for_stage_and_ref("deploy", "deploy").size.should == 2 - end - end - - describe "Image and service handling" do - it "returns image and service when defined" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: { script: "rspec" } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 - config_processor.builds_for_stage_and_ref("test", "master").first.should == { - except: nil, - stage: "test", - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: { - image: "ruby:2.1", - services: ["mysql"] - }, - allow_failure: false - } - end - - it "returns image and service when overridden for job" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - config_processor.builds_for_stage_and_ref("test", "master").size.should == 1 - config_processor.builds_for_stage_and_ref("test", "master").first.should == { - except: nil, - stage: "test", - name: :rspec, - only: nil, - script: "pwd\nrspec", - tags: [], - options: { - image: "ruby:2.5", - services: ["postgresql"] - }, - allow_failure: false - } - end - end - - describe "Variables" do - it "returns variables when defined" do - variables = { - var1: "value1", - var2: "value2", - } - config = YAML.dump({ - variables: variables, - before_script: ["pwd"], - rspec: { script: "rspec" } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - config_processor.variables.should == variables - end - end - - describe "Error handling" do - it "indicates that object is invalid" do - expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) - end - - it "returns errors if tags parameter is invalid" do - config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") - end - - it "returns errors if before_script parameter is invalid" do - config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") - end - - it "returns errors if image parameter is invalid" do - config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") - end - - it "returns errors if job image parameter is invalid" do - config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") - end - - it "returns errors if services parameter is not an array" do - config = YAML.dump({ services: "test", rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") - end - - it "returns errors if services parameter is not an array of strings" do - config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") - end - - it "returns errors if job services parameter is not an array" do - config = YAML.dump({ rspec: { script: "test", services: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") - end - - it "returns errors if job services parameter is not an array of strings" do - config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") - end - - it "returns errors if there are unknown parameters" do - config = YAML.dump({ extra: "bundle update" }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") - end - - it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do - config = YAML.dump({ extra: { services: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") - end - - it "returns errors if there is no any jobs defined" do - config = YAML.dump({ before_script: ["bundle update"] }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") - end - - it "returns errors if job allow_failure parameter is not an boolean" do - config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") - end - - it "returns errors if job stage is not a string" do - config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") - end - - it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") - end - - it "returns errors if job stage is not a defined stage" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") - end - - it "returns errors if stages is not an array" do - config = YAML.dump({ types: "test", rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") - end - - it "returns errors if stages is not an array of strings" do - config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + describe "Image and service handling" do + it "returns image and service when defined" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: { script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.1", + services: ["mysql"] + }, + allow_failure: false + }) + end + + it "returns image and service when overridden for job" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + except: nil, + stage: "test", + name: :rspec, + only: nil, + script: "pwd\nrspec", + tags: [], + options: { + image: "ruby:2.5", + services: ["postgresql"] + }, + allow_failure: false + }) + end end - it "returns errors if variables is not a map" do - config = YAML.dump({ variables: "test", rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + describe "Variables" do + it "returns variables when defined" do + variables = { + var1: "value1", + var2: "value2", + } + config = YAML.dump({ + variables: variables, + before_script: ["pwd"], + rspec: { script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.variables).to eq(variables) + end end - it "returns errors if variables is not a map of key-valued strings" do - config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) - expect do - GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + describe "Error handling" do + it "indicates that object is invalid" do + expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) + end + + it "returns errors if tags parameter is invalid" do + config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") + end + + it "returns errors if before_script parameter is invalid" do + config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") + end + + it "returns errors if image parameter is invalid" do + config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") + end + + it "returns errors if job image parameter is invalid" do + config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") + end + + it "returns errors if services parameter is not an array" do + config = YAML.dump({ services: "test", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if services parameter is not an array of strings" do + config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") + end + + it "returns errors if job services parameter is not an array" do + config = YAML.dump({ rspec: { script: "test", services: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if job services parameter is not an array of strings" do + config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") + end + + it "returns errors if there are unknown parameters" do + config = YAML.dump({ extra: "bundle update" }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do + config = YAML.dump({ extra: { services: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") + end + + it "returns errors if there is no any jobs defined" do + config = YAML.dump({ before_script: ["bundle update"] }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") + end + + it "returns errors if job allow_failure parameter is not an boolean" do + config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") + end + + it "returns errors if job stage is not a string" do + config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a pre-defined stage" do + config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") + end + + it "returns errors if job stage is not a defined stage" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") + end + + it "returns errors if stages is not an array" do + config = YAML.dump({ types: "test", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if stages is not an array of strings" do + config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") + end + + it "returns errors if variables is not a map" do + config = YAML.dump({ variables: "test", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end + + it "returns errors if variables is not a map of key-valued strings" do + config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end end end end diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index 51511641afc..564c2941bb5 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -169,7 +169,7 @@ describe Ci::MailService do end it do - Build.retry(build) + Ci::Build.retry(build) should_email(commit.git_author_email) should_email("jeroen@example.com") mail.execute(build) if mail.can_execute?(build) diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index ef0714909d5..f5335903728 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::SlackMessage do - subject { SlackMessage.new(commit) } + subject { Ci::SlackMessage.new(commit) } let(:project) { FactoryGirl.create :ci_project } @@ -43,7 +43,7 @@ describe Ci::SlackMessage do end context "Several builds" do - let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs, project: project) } context 'when all matrix builds succeeded' do let(:color) { 'good' } diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index ae577adfb75..0524f472432 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -30,7 +30,7 @@ describe Ci::SlackService do end describe "Execute" do - let(:slack) { SlackService.new } + let(:slack) { Ci::SlackService.new } let(:project) { FactoryGirl.create :ci_project } let(:commit) { FactoryGirl.create :ci_commit, project: project } let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } @@ -50,7 +50,7 @@ describe Ci::SlackService do it "should call Slack API" do slack.execute(build) - SlackNotifierWorker.drain + Ci::SlackNotifierWorker.drain expect(WebMock).to have_requested(:post, webhook_url).once end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index c6130b69964..757593a7ab8 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -22,7 +22,7 @@ require 'spec_helper' describe Ci::Runner do describe '#display_name' do it 'should return the description if it has a value' do - runner = FactoryGirl.build(:runner, description: 'Linux/Ruby-1.9.3-p448') + runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448') expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448' end @@ -32,7 +32,7 @@ describe Ci::Runner do end it 'should return the token if the description is an empty string' do - runner = FactoryGirl.build(:runner, description: '') + runner = FactoryGirl.build(:ci_runner, description: '') expect(runner.display_name).to eq runner.token end end diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 04807a705eb..2c575056b08 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -25,7 +25,7 @@ describe Ci::Service do describe "Test Button" do before do - @service = Service.new + @service = Ci::Service.new end describe "Testable" do diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index 234a778f8cc..ce5b131f192 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -1,12 +1,10 @@ require 'spec_helper' describe Ci::CreateProjectService do - let(:service) { CreateProjectService.new } + let(:service) { Ci::CreateProjectService.new } let(:current_user) { double.as_null_object } let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - before { allow_any_instance_of(Network).to receive_messages(enable_ci: true) } - describe :execute do context 'valid params' do let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } @@ -23,7 +21,7 @@ describe Ci::CreateProjectService do context "forking" do it "uses project as a template for settings and jobs" do - origin_project = FactoryGirl.create(:project) + origin_project = FactoryGirl.create(:ci_project) origin_project.shared_runners_enabled = true origin_project.public = true origin_project.allow_git_fetch = true diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index 9082c741ead..d12cd9773dc 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService do - let(:service) { CreateTriggerRequestService.new } - let(:project) { FactoryGirl.create :project } - let(:trigger) { FactoryGirl.create :trigger, project: project } + let(:service) { Ci::CreateTriggerRequestService.new } + let(:project) { FactoryGirl.create :ci_project } + let(:trigger) { FactoryGirl.create :ci_trigger, project: project } describe :execute do context 'valid params' do subject { service.execute(project, trigger, 'master') } before do - @commit = FactoryGirl.create :commit, project: project + @commit = FactoryGirl.create :ci_commit, project: project end - it { expect(subject).to be_kind_of(TriggerRequest) } + it { expect(subject).to be_kind_of(Ci::TriggerRequest) } it { expect(subject.commit).to eq(@commit) } end @@ -27,7 +27,7 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - FactoryGirl.create :commit_without_jobs, project: project + FactoryGirl.create :ci_commit_without_jobs, project: project end it { expect(subject).to be_nil } @@ -37,13 +37,13 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - @commit1 = FactoryGirl.create :commit, committed_at: 2.hour.ago, project: project - @commit2 = FactoryGirl.create :commit, committed_at: 1.hour.ago, project: project - @commit3 = FactoryGirl.create :commit, committed_at: 3.hour.ago, project: project + @commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + @commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + @commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, project: project end context 'retries latest one' do - it { expect(subject).to be_kind_of(TriggerRequest) } + it { expect(subject).to be_kind_of(Ci::TriggerRequest) } it { expect(subject).to be_persisted } it { expect(subject.commit).to eq(@commit2) } end diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index c8c4c45cc31..9b330a90ae2 100644 --- a/spec/services/ci/event_service_spec.rb +++ b/spec/services/ci/event_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::EventService do - let(:project) { FactoryGirl.create :project, name: "GitLab / gitlab-shell" } + let(:project) { FactoryGirl.create :ci_project, name: "GitLab / gitlab-shell" } let(:user) { double(username: "root", id: 1) } before do @@ -10,25 +10,25 @@ describe Ci::EventService do describe :remove_project do it "creates event" do - EventService.new.remove_project(user, project) + Ci::EventService.new.remove_project(user, project) - expect(Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been removed by root") + expect(Ci::Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been removed by root") end end describe :create_project do it "creates event" do - EventService.new.create_project(user, project) + Ci::EventService.new.create_project(user, project) - expect(Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been created by root") + expect(Ci::Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been created by root") end end describe :change_project_settings do it "creates event" do - EventService.new.change_project_settings(user, project) + Ci::EventService.new.change_project_settings(user, project) - expect(Event.last.description).to eq("User \"root\" updated projects settings") + expect(Ci::Event.last.description).to eq("User \"root\" updated projects settings") end end end diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index b893b1f23f2..cebdd145e40 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -1,15 +1,15 @@ require 'spec_helper' describe Ci::WebHookService do - let(:project) { FactoryGirl.create :project } - let(:commit) { FactoryGirl.create :commit, project: project } - let(:build) { FactoryGirl.create :build, commit: commit } - let(:hook) { FactoryGirl.create :web_hook, project: project } + let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:build) { FactoryGirl.create :ci_build, commit: commit } + let(:hook) { FactoryGirl.create :ci_web_hook, project: project } describe :execute do it "should execute successfully" do stub_request(:post, hook.url).to_return(status: 200) - expect(WebHookService.new.build_end(build)).to be_truthy + expect(Ci::WebHookService.new.build_end(build)).to be_truthy end end @@ -31,6 +31,6 @@ describe Ci::WebHookService do end def build_data(build) - WebHookService.new.send :build_data, build + Ci::WebHookService.new.send :build_data, build end end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index ffe30a4246c..cd9fdc6f18e 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -44,4 +44,8 @@ module LoginHelpers def logout_direct page.driver.submit :delete, '/users/sign_out', {} end + + def skip_ci_admin_auth + allow_any_instance_of(Ci::Admin::ApplicationController).to receive_messages(authenticate_admin!: true) + end end -- cgit v1.2.1 From b9cc41cf34d2a5b347689bfafa72f12ae2979aac Mon Sep 17 00:00:00 2001 From: fscherwi Date: Tue, 15 Sep 2015 18:07:28 +0200 Subject: change coverage image to svg [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52e12bb66ad..99d5bc0b6ca 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![build status](https://ci.gitlab.com/projects/1/status.png?ref=master)](https://ci.gitlab.com/projects/1?ref=master) [![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) -[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) +[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) ## Canonical source -- cgit v1.2.1 From d420b608e6aa5fb9803e9412826c9087c44cb72f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 15 Sep 2015 13:24:38 -0400 Subject: Minor editing of the 7.14-to-8.0 update guide --- doc/update/7.14-to-8.0.md | 68 ++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 29a38d07b3d..e3d131d3227 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -10,9 +10,9 @@ months after this vulnerability became known the GitLab installation guide still contained instructions that would install the outdated, 'vulnerable' Git version 2.1.2. -Run the following command to get your current Git version. +Run the following command to get your current Git version: -``` +```sh /usr/local/bin/git --version ``` @@ -63,28 +63,33 @@ sudo -u git -H git checkout 8-0-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.4 +sudo -u git -H git checkout v2.6.5 ``` ### 5. Install gitlab-git-http-server -First we download Go 1.5 and install it into /usr/local/go. +First we download Go 1.5 and install it into `/usr/local/go`: - curl -O --progress https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz - echo '5817fa4b2252afdb02e11e8b9dc1d9173ef3bd5a go1.5.linux-amd64.tar.gz' | shasum -c - && \ - sudo tar -C /usr/local -xzf go1.5.linux-amd64.tar.gz - sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ - rm go1.5.linux-amd64.tar.gz +```bash +curl -O --progress https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz +echo '5817fa4b2252afdb02e11e8b9dc1d9173ef3bd5a go1.5.linux-amd64.tar.gz' | shasum -c - && \ + sudo tar -C /usr/local -xzf go1.5.linux-amd64.tar.gz +sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ +rm go1.5.linux-amd64.tar.gz +``` -Now we download gitlab-git-http-server and install it in /home/git/gitlab-git-http-server. +Now we download `gitlab-git-http-server` and install it in `/home/git/gitlab-git-http-server`: - cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git - cd gitlab-git-http-server - sudo -u git -H make +```bash +cd /home/git +sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git +cd gitlab-git-http-server +sudo -u git -H make +``` -If you put your Git repositories in a directory different from /home/git/repositories, you need to tell gitlab-git-http-server about it via /etc/gitlab/default. -See lib/support/init.d/gitlab.default.example for the options. +If your Git repositories are in a directory other than `/home/git/repositories`, +you need to tell `gitlab-git-http-server` about it via `/etc/gitlab/default`. +See `lib/support/init.d/gitlab.default.example` for the options. ### 6. Install libs, migrations, etc. @@ -111,20 +116,27 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: -``` +```sh git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example -`````` +``` -#### New NGINX configuration +#### New Nginx configuration -Because of the new gitlab-git-http-server you need to update your NGINX configuration. -If you skip this step 'git clone' and 'git push' over HTTP(S) will stop working. +Because of the new `gitlab-git-http-server` you need to update your Nginx +configuration. If you skip this step 'git clone' and 'git push' over HTTP(S) +will stop working. -``` -# Remove '-ssl' twice in the diff command below if you use HTTP instead of HTTPS +View changes between the previous recommended Nginx configuration and the +current one: + +```sh +# For HTTPS configurations git diff origin/7-14-stable:lib/support/nginx/gitlab-ssl origin/8-0-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/7-14-stable:lib/support/nginx/gitlab origin/8-0-stable:lib/support/nginx/gitlab ``` ### 8. Start application @@ -138,7 +150,7 @@ Check if GitLab and its environment are configured correctly: sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production -To make sure you didn't miss anything run a more thorough check with: +To make sure you didn't miss anything run a more thorough check: sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production @@ -147,13 +159,15 @@ If all items are green, then congratulations, the upgrade is complete! ## Things went south? Revert to previous version (7.14) ### 1. Revert the code to the previous version + Follow the [upgrade guide from 7.13 to 7.14](7.13-to-7.14.md), except for the database migration (The backup is already migrated to the previous version) -### 2. Restore from the backup: +### 2. Restore from the backup ```bash cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` -If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. -- cgit v1.2.1 From f01aaba778a908ea3bc8ae5438547083e1196b14 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 15 Sep 2015 13:41:29 -0400 Subject: Reorder the `--without` arugments so the database comes first Hopefully this will make it a bit clearer that we're excluding a certain database type. [ci skip] --- doc/update/7.14-to-8.0.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index e3d131d3227..3ae0f9616ac 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -96,11 +96,11 @@ See `lib/support/init.d/gitlab.default.example` for the options. ```bash cd /home/git/gitlab -# MySQL installations (note: the line below states '--without ... postgres') -sudo -u git -H bundle install --without development test postgres --deployment +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment -# PostgreSQL installations (note: the line below states '--without ... mysql') -sudo -u git -H bundle install --without development test mysql --deployment +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production -- cgit v1.2.1 From ed18e04bb3d921d14f1bfb9adf7e0a4ad4dfc0b2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 10:24:30 +0200 Subject: Cleanup CI backup => migrate with GitLab --- lib/backup/builds.rb | 30 +++++++ lib/ci/backup/builds.rb | 32 ------- lib/ci/backup/database.rb | 94 -------------------- lib/ci/backup/manager.rb | 158 ---------------------------------- lib/tasks/ci/backup.rake | 62 ------------- lib/tasks/gitlab/backup.rake | 21 +++++ spec/tasks/gitlab/backup_rake_spec.rb | 4 + 7 files changed, 55 insertions(+), 346 deletions(-) create mode 100644 lib/backup/builds.rb delete mode 100644 lib/ci/backup/builds.rb delete mode 100644 lib/ci/backup/database.rb delete mode 100644 lib/ci/backup/manager.rb delete mode 100644 lib/tasks/ci/backup.rake diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb new file mode 100644 index 00000000000..4280438e86c --- /dev/null +++ b/lib/backup/builds.rb @@ -0,0 +1,30 @@ +module Backup + class Builds + attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir + + def initialize + @app_builds_dir = File.realpath(Rails.root.join('ci/builds')) + @backup_dir = GitlabCi.config.backup.path + @backup_builds_dir = File.join(GitlabCi.config.backup.path, 'ci/builds') + end + + # Copy builds from builds directory to backup/builds + def dump + FileUtils.mkdir_p(backup_builds_dir) + FileUtils.cp_r(app_builds_dir, backup_dir) + end + + def restore + backup_existing_builds_dir + + FileUtils.cp_r(backup_builds_dir, app_builds_dir) + end + + def backup_existing_builds_dir + timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}") + if File.exists?(app_builds_dir) + FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path)) + end + end + end +end diff --git a/lib/ci/backup/builds.rb b/lib/ci/backup/builds.rb deleted file mode 100644 index 832a5ab8fdc..00000000000 --- a/lib/ci/backup/builds.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Ci - module Backup - class Builds - attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir - - def initialize - @app_builds_dir = File.realpath(Rails.root.join('ci/builds')) - @backup_dir = GitlabCi.config.backup.path - @backup_builds_dir = File.join(GitlabCi.config.backup.path, 'ci/builds') - end - - # Copy builds from builds directory to backup/builds - def dump - FileUtils.mkdir_p(backup_builds_dir) - FileUtils.cp_r(app_builds_dir, backup_dir) - end - - def restore - backup_existing_builds_dir - - FileUtils.cp_r(backup_builds_dir, app_builds_dir) - end - - def backup_existing_builds_dir - timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}") - if File.exists?(app_builds_dir) - FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path)) - end - end - end - end -end diff --git a/lib/ci/backup/database.rb b/lib/ci/backup/database.rb deleted file mode 100644 index 3f2277024e4..00000000000 --- a/lib/ci/backup/database.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'yaml' - -module Ci - module Backup - class Database - attr_reader :config, :db_dir - - def initialize - @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] - @db_dir = File.join(GitlabCi.config.backup.path, 'db') - FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir) - end - - def dump - success = case config["adapter"] - when /^mysql/ then - $progress.print "Dumping MySQL database #{config['database']} ... " - system('mysqldump', *mysql_args, config['database'], out: db_file_name) - when "postgresql" then - $progress.print "Dumping PostgreSQL database #{config['database']} ... " - pg_env - system('pg_dump', config['database'], out: db_file_name) - end - report_success(success) - abort 'Backup failed' unless success - end - - def restore - success = case config["adapter"] - when /^mysql/ then - $progress.print "Restoring MySQL database #{config['database']} ... " - system('mysql', *mysql_args, config['database'], in: db_file_name) - when "postgresql" then - $progress.print "Restoring PostgreSQL database #{config['database']} ... " - # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE - # statements like MySQL. - drop_all_tables - drop_all_postgres_sequences - pg_env - system('psql', config['database'], '-f', db_file_name) - end - report_success(success) - abort 'Restore failed' unless success - end - - protected - - def db_file_name - File.join(db_dir, 'database.sql') - end - - def mysql_args - args = { - 'host' => '--host', - 'port' => '--port', - 'socket' => '--socket', - 'username' => '--user', - 'encoding' => '--default-character-set', - 'password' => '--password' - } - args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact - end - - def pg_env - ENV['PGUSER'] = config["username"] if config["username"] - ENV['PGHOST'] = config["host"] if config["host"] - ENV['PGPORT'] = config["port"].to_s if config["port"] - ENV['PGPASSWORD'] = config["password"].to_s if config["password"] - end - - def report_success(success) - if success - $progress.puts '[DONE]'.green - else - $progress.puts '[FAILED]'.red - end - end - - def drop_all_tables - connection = ActiveRecord::Base.connection - connection.tables.each do |table| - connection.drop_table(table) - end - end - - def drop_all_postgres_sequences - connection = ActiveRecord::Base.connection - connection.execute("SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';").each do |sequence| - connection.execute("DROP SEQUENCE #{sequence['relname']}") - end - end - end - end -end diff --git a/lib/ci/backup/manager.rb b/lib/ci/backup/manager.rb deleted file mode 100644 index 2e9d6df7139..00000000000 --- a/lib/ci/backup/manager.rb +++ /dev/null @@ -1,158 +0,0 @@ -module Ci - module Backup - class Manager - def pack - # saving additional informations - s = {} - s[:db_version] = "#{ActiveRecord::Migrator.current_version}" - s[:backup_created_at] = Time.now - s[:gitlab_version] = GitlabCi::VERSION - s[:tar_version] = tar_version - tar_file = "#{s[:backup_created_at].to_i}_gitlab_ci_backup.tar.gz" - - Dir.chdir(GitlabCi.config.backup.path) do - File.open("#{GitlabCi.config.backup.path}/backup_information.yml", - "w+") do |file| - file << s.to_yaml.gsub(/^---\n/,'') - end - - FileUtils.chmod(0700, ["db", "builds"]) - - # create archive - $progress.print "Creating backup archive: #{tar_file} ... " - orig_umask = File.umask(0077) - if Kernel.system('tar', '-czf', tar_file, *backup_contents) - $progress.puts "done".green - else - puts "creating archive #{tar_file} failed".red - abort 'Backup failed' - end - File.umask(orig_umask) - - upload(tar_file) - end - end - - def upload(tar_file) - remote_directory = GitlabCi.config.backup.upload.remote_directory - $progress.print "Uploading backup archive to remote storage #{remote_directory} ... " - - connection_settings = GitlabCi.config.backup.upload.connection - if connection_settings.blank? - $progress.puts "skipped".yellow - return - end - - connection = ::Fog::Storage.new(connection_settings) - directory = connection.directories.get(remote_directory) - - if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, - multipart_chunk_size: GitlabCi.config.backup.upload.multipart_chunk_size) - $progress.puts "done".green - else - puts "uploading backup to #{remote_directory} failed".red - abort 'Backup failed' - end - end - - def cleanup - $progress.print "Deleting tmp directories ... " - - backup_contents.each do |dir| - next unless File.exist?(File.join(GitlabCi.config.backup.path, dir)) - - if FileUtils.rm_rf(File.join(GitlabCi.config.backup.path, dir)) - $progress.puts "done".green - else - puts "deleting tmp directory '#{dir}' failed".red - abort 'Backup failed' - end - end - end - - def remove_old - # delete backups - $progress.print "Deleting old backups ... " - keep_time = GitlabCi.config.backup.keep_time.to_i - - if keep_time > 0 - removed = 0 - - Dir.chdir(GitlabCi.config.backup.path) do - file_list = Dir.glob('*_gitlab_ci_backup.tar.gz') - file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_ci_backup.tar.gz/ } - file_list.sort.each do |timestamp| - if Time.at(timestamp) < (Time.now - keep_time) - if Kernel.system(*%W(rm #{timestamp}_gitlab_ci_backup.tar.gz)) - removed += 1 - end - end - end - end - - $progress.puts "done. (#{removed} removed)".green - else - $progress.puts "skipping".yellow - end - end - - def unpack - Dir.chdir(GitlabCi.config.backup.path) - - # check for existing backups in the backup dir - file_list = Dir.glob("*_gitlab_ci_backup.tar.gz").each.map { |f| f.split(/_/).first.to_i } - puts "no backups found" if file_list.count == 0 - - if file_list.count > 1 && ENV["BACKUP"].nil? - puts "Found more than one backup, please specify which one you want to restore:" - puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup" - exit 1 - end - - tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_ci_backup.tar.gz") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar.gz") - - unless File.exists?(tar_file) - puts "The specified backup doesn't exist!" - exit 1 - end - - $progress.print "Unpacking backup ... " - - unless Kernel.system(*%W(tar -xzf #{tar_file})) - puts "unpacking backup failed".red - exit 1 - else - $progress.puts "done".green - end - - ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 - - # restoring mismatching backups can lead to unexpected problems - if settings[:gitlab_version] != GitlabCi::VERSION - puts "GitLab CI version mismatch:".red - puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI version in the backup!".red - puts " Please switch to the following version and try again:".red - puts " version: #{settings[:gitlab_version]}".red - puts - puts "Hint: git checkout v#{settings[:gitlab_version]}" - exit 1 - end - end - - def tar_version - tar_version = `tar --version` - tar_version.force_encoding('locale').split("\n").first - end - - private - - def backup_contents - ["db", "builds", "backup_information.yml"] - end - - def settings - @settings ||= YAML.load_file("backup_information.yml") - end - end - end -end diff --git a/lib/tasks/ci/backup.rake b/lib/tasks/ci/backup.rake deleted file mode 100644 index 1cb2e43f875..00000000000 --- a/lib/tasks/ci/backup.rake +++ /dev/null @@ -1,62 +0,0 @@ -namespace :ci do - namespace :backup do - - desc "GITLAB | Create a backup of the GitLab CI database" - task create: :environment do - configure_cron_mode - - $progress.puts "Dumping database ... ".blue - Ci::Backup::Database.new.dump - $progress.puts "done".green - - $progress.puts "Dumping builds ... ".blue - Ci::Backup::Builds.new.dump - $progress.puts "done".green - - backup = Ci::Backup::Manager.new - backup.pack - backup.cleanup - backup.remove_old - end - - desc "GITLAB | Restore a previously created backup" - task restore: :environment do - configure_cron_mode - - backup = Ci::Backup::Manager.new - backup.unpack - - $progress.puts "Restoring database ... ".blue - Ci::Backup::Database.new.restore - $progress.puts "done".green - - $progress.puts "Restoring builds ... ".blue - Ci::Backup::Builds.new.restore - $progress.puts "done".green - - backup.cleanup - end - - def configure_cron_mode - if ENV['CRON'] - # We need an object we can say 'puts' and 'print' to; let's use a - # StringIO. - require 'stringio' - $progress = StringIO.new - else - $progress = $stdout - end - end - end - - # Disable colors for CRON - unless STDOUT.isatty - module Colored - extend self - - def colorize(string, options={}) - string - end - end - end -end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 4c73f90bbf2..f20c7f71ba5 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -11,6 +11,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:db:create"].invoke Rake::Task["gitlab:backup:repo:create"].invoke Rake::Task["gitlab:backup:uploads:create"].invoke + Rake::Task["gitlab:backup:builds:create"].invoke backup = Backup::Manager.new backup.pack @@ -30,6 +31,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:db:restore"].invoke unless backup.skipped?("db") Rake::Task["gitlab:backup:repo:restore"].invoke unless backup.skipped?("repositories") Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads") + Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds") Rake::Task["gitlab:shell:setup"].invoke backup.cleanup @@ -73,6 +75,25 @@ namespace :gitlab do end end + namespace :builds do + task create: :environment do + $progress.puts "Dumping builds ... ".blue + + if ENV["SKIP"] && ENV["SKIP"].include?("builds") + $progress.puts "[SKIPPED]".cyan + else + Backup::Builds.new.dump + $progress.puts "done".green + end + end + + task restore: :environment do + $progress.puts "Restoring builds ... ".blue + Backup::Builds.new.restore + $progress.puts "done".green + end + end + namespace :uploads do task create: :environment do $progress.puts "Dumping uploads ... ".blue diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 23f322e0a62..32adcbec71f 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -54,6 +54,7 @@ describe 'gitlab:app namespace rake task' do and_return({ gitlab_version: gitlab_version }) expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke) + expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke) expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end @@ -117,6 +118,7 @@ describe 'gitlab:app namespace rake task' do expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads/') expect(tar_contents).to match('repositories/') + expect(tar_contents).to match('builds/') expect(tar_contents).not_to match(/^.{4,9}[rwx].* (db|uploads|repositories)\/$/) end @@ -163,6 +165,7 @@ describe 'gitlab:app namespace rake task' do expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads/') + expect(tar_contents).to match('builds/') expect(tar_contents).not_to match('repositories/') end @@ -173,6 +176,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke + expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end -- cgit v1.2.1 From 7e07bc06980e56cabb90538c4c1ebd3dd027ed50 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 10:25:49 +0200 Subject: Remove unused tasks --- lib/tasks/ci/setup.rake | 7 ------- lib/tasks/ci/sidekiq.rake | 13 ------------- 2 files changed, 20 deletions(-) delete mode 100644 lib/tasks/ci/setup.rake delete mode 100644 lib/tasks/ci/sidekiq.rake diff --git a/lib/tasks/ci/setup.rake b/lib/tasks/ci/setup.rake deleted file mode 100644 index ab83581ec1b..00000000000 --- a/lib/tasks/ci/setup.rake +++ /dev/null @@ -1,7 +0,0 @@ -namespace :ci do - desc "GitLab CI | Setup gitlab db" - task :setup do - Rake::Task["db:setup"].invoke - Rake::Task["ci:add_limits_mysql"].invoke - end -end diff --git a/lib/tasks/ci/sidekiq.rake b/lib/tasks/ci/sidekiq.rake deleted file mode 100644 index 12fd3635933..00000000000 --- a/lib/tasks/ci/sidekiq.rake +++ /dev/null @@ -1,13 +0,0 @@ -namespace :ci do - namespace :sidekiq do - desc "GitLab CI | Stop sidekiq" - task :stop do - exec({'RAILS_ENV' => Rails.env}, 'script/background_jobs stop') - end - - desc "GitLab CI | Start sidekiq" - task :start do - exec({'RAILS_ENV' => Rails.env}, 'script/background_jobs start') - end - end -end -- cgit v1.2.1 From 269b9224cf6a6c00c163ecff0b503db2d1f88ec8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 10:48:52 +0200 Subject: Use GitLab instead of GITLAB for rake task --- lib/tasks/ci/migrate.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake index c00b17f7a2d..7d99664dcf3 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -18,7 +18,7 @@ namespace :ci do tags.map { |tag| tag['name'] } end - desc 'GITLAB | Migrate CI tags' + desc 'GitLab | Migrate CI tags' task tags: :environment do list_objects('Runner').each do |id| runner = Ci::Runner.find_by_id(id) -- cgit v1.2.1 From 8549d709fbccfe1290d0ed59ee090c692518c776 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 20:40:52 +0200 Subject: Fix ci/projects_controller_spec.rb --- spec/controllers/ci/projects_controller_spec.rb | 36 +++++++------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index b7eb4eac673..015788a05e1 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -49,24 +49,17 @@ describe Ci::ProjectsController do end describe "POST /projects" do - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - - let(:user_data) do - data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) - data.merge("url" => gitlab_url) - end + let(:project_dump) { OpenStruct.new({ id: @project.gitlab_id }) } let(:user) do create(:user) end - it "creates project" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - allow_any_instance_of(Ci::Network).to receive(:enable_ci).and_return(true) - allow_any_instance_of(Ci::Network).to receive(:project_hooks).and_return(true) + before do + sign_in(user) + end + it "creates project" do post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access expect(response.code).to eq('302') @@ -74,10 +67,6 @@ describe Ci::ProjectsController do end it "shows error" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access expect(response.code).to eq('302') @@ -86,22 +75,15 @@ describe Ci::ProjectsController do end describe "GET /gitlab" do - let(:gitlab_url) { GitlabCi.config.gitlab_server.url } - - let(:user_data) do - data = JSON.parse File.read(Rails.root.join('spec/support/gitlab_stubs/user.json')) - data.merge("url" => gitlab_url) - end - let(:user) do create(:user) end - it "searches projects" do - allow(controller).to receive(:reset_cache) { true } - allow(controller).to receive(:current_user) { user } - allow_any_instance_of(Ci::Network).to receive(:projects).with(hash_including(search: 'str'), :authorized) + before do + sign_in(user) + end + it "searches projects" do xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access expect(response).to be_success -- cgit v1.2.1 From 34e1ce130f829605fc6ba34bc12ea7e0412d9bba Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Tue, 15 Sep 2015 20:46:11 +0200 Subject: Add a help text to the Slack Service When setting up the service I was not sure which webhook I should use, by adding this Help text I hope we can help people in the future with setting up this hook Signed-off-by: Jeroen van Baarsen --- app/models/project_services/slack_service.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 36d9874edd3..7cd5e892507 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -34,6 +34,12 @@ class SlackService < Service 'slack' end + def help + 'This service sends notifications to your Slack channel.
    + To setup this Service you need to create a new "Incoming webhook" in your Slack integration panel, + and enter the Webhook URL below.' + end + def fields [ { type: 'text', name: 'webhook', -- cgit v1.2.1 From 6d0980b011656b1570c94be8b3baf78c089f6bbd Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 20:51:03 +0200 Subject: Fix: ci_*_build factory should be valid --- spec/factories/ci/builds.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 3fe9a89ad1b..99da5a18776 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -37,6 +37,8 @@ FactoryGirl.define do } end + commit factory: :ci_commit + factory :ci_not_started_build do started_at nil finished_at nil -- cgit v1.2.1 From a598bd91f9b5ec70d04d70f481c4f5b277f05904 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 20:56:20 +0200 Subject: Refactor ci_admin_event and fix features/ci/events_spec.rb --- spec/factories/ci/events.rb | 2 +- spec/features/ci/admin/events_spec.rb | 2 +- spec/features/ci/events_spec.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/factories/ci/events.rb b/spec/factories/ci/events.rb index 03450751596..9638618a400 100644 --- a/spec/factories/ci/events.rb +++ b/spec/factories/ci/events.rb @@ -17,7 +17,7 @@ FactoryGirl.define do "updated project settings#{n}" end - factory :admin_event do + factory :ci_admin_event do is_admin true end end diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb index 6b6ace06c53..a7e75cc4f6b 100644 --- a/spec/features/ci/admin/events_spec.rb +++ b/spec/features/ci/admin/events_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe "Admin Events" do - let(:event) { FactoryGirl.create :admin_event } + let(:event) { FactoryGirl.create :ci_admin_event } before do skip_ci_admin_auth diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb index dd13d7d66dc..8142eaff274 100644 --- a/spec/features/ci/events_spec.rb +++ b/spec/features/ci/events_spec.rb @@ -2,13 +2,13 @@ require 'spec_helper' describe "Events" do let(:project) { FactoryGirl.create :ci_project } - let(:event) { FactoryGirl.create :admin_event, project: project } + let(:event) { FactoryGirl.create :ci_admin_event, project: project } before do login_as :user end - describe "GET /project/:id/events" do + describe "GET /ci/project/:id/events" do before do event visit ci_project_events_path(project) -- cgit v1.2.1 From 9621f433a7ba7b1f4083c27aac1b41642f82f352 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:06:12 +0200 Subject: Fix: features/ci/projects_spec.rb --- spec/features/ci/projects_spec.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb index 8f6c238743f..ff17aeca447 100644 --- a/spec/features/ci/projects_spec.rb +++ b/spec/features/ci/projects_spec.rb @@ -1,33 +1,36 @@ require 'spec_helper' describe "Projects" do + let(:user) { create(:user) } + before do - login_as :user - @project = FactoryGirl.create :project, name: "GitLab / gitlab-shell" + login_as(user) + @project = FactoryGirl.create :ci_project, name: "GitLab / gitlab-shell" + @project.gl_project.team << [user, :master] end - describe "GET /projects", js: true do + describe "GET /ci/projects", js: true do before do stub_js_gitlab_calls - visit projects_path + visit ci_projects_path end it { expect(page).to have_content "GitLab / gitlab-shell" } it { expect(page).to have_selector ".search input#search" } end - describe "GET /projects/:id" do + describe "GET /ci/projects/:id" do before do - visit project_path(@project) + visit ci_project_path(@project) end it { expect(page).to have_content @project.name } it { expect(page).to have_content 'All commits' } end - describe "GET /projects/:id/edit" do + describe "GET /ci/projects/:id/edit" do before do - visit edit_project_path(@project) + visit edit_ci_project_path(@project) end it { expect(page).to have_content @project.name } @@ -43,9 +46,9 @@ describe "Projects" do end end - describe "GET /projects/:id/charts" do + describe "GET /ci/projects/:id/charts" do before do - visit project_charts_path(@project) + visit ci_project_charts_path(@project) end it { expect(page).to have_content 'Overall' } -- cgit v1.2.1 From 5285e01d54cff68db99aaa4b15a87d8d4c0333ab Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:21:40 +0200 Subject: Fix: features/ci/runners_spec.rb --- app/controllers/ci/runner_projects_controller.rb | 2 +- app/models/user.rb | 9 +++++++++ spec/features/ci/runners_spec.rb | 20 +++++++++----------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb index 5365f51082f..a8bdd5bb362 100644 --- a/app/controllers/ci/runner_projects_controller.rb +++ b/app/controllers/ci/runner_projects_controller.rb @@ -9,7 +9,7 @@ module Ci def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - return head(403) unless current_user.authorized_runners.include?(@runner) + return head(403) unless current_user.ci_authorized_runners.include?(@runner) if @runner.assign_to(project, current_user) redirect_to ci_project_runners_path(project) diff --git a/app/models/user.rb b/app/models/user.rb index bff8eeed96d..25371f9138a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -753,4 +753,13 @@ class User < ActiveRecord::Base def can_be_removed? !solo_owned_groups.present? end + + def ci_authorized_projects + @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects) + end + + def ci_authorized_runners + Ci::Runner.specific.includes(:runner_projects). + where(ci_runner_projects: { project_id: ci_authorized_projects } ) + end end diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb index 86ccac29c74..15147f15eb3 100644 --- a/spec/features/ci/runners_spec.rb +++ b/spec/features/ci/runners_spec.rb @@ -1,21 +1,19 @@ require 'spec_helper' describe "Runners" do + let(:user) { create(:user) } + before do - login_as :user + login_as(user) end describe "specific runners" do before do @project = FactoryGirl.create :ci_project - @project2 = FactoryGirl.create :ci_project - stub_js_gitlab_calls + @project.gl_project.team << [user, :master] - # all projects should be authorized for user - allow_any_instance_of(Network).to receive(:projects).and_return([ - OpenStruct.new({ id: @project.gitlab_id }), - OpenStruct.new({ id: @project2.gitlab_id }) - ]) + @project2 = FactoryGirl.create :ci_project + @project2.gl_project.team << [user, :master] @shared_runner = FactoryGirl.create :ci_shared_runner @specific_runner = FactoryGirl.create :ci_specific_runner @@ -60,14 +58,14 @@ describe "Runners" do click_on "Remove runner" end - expect(Runner.exists?(id: @specific_runner)).to be_falsey + expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey end end describe "shared runners" do before do @project = FactoryGirl.create :ci_project - stub_js_gitlab_calls + @project.gl_project.team << [user, :master] end it "enables shared runners" do @@ -82,7 +80,7 @@ describe "Runners" do describe "show page" do before do @project = FactoryGirl.create :ci_project - stub_js_gitlab_calls + @project.gl_project.team << [user, :master] @specific_runner = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner end -- cgit v1.2.1 From 64ef1b3dcdd2cfb37da26da55f265b7c24dac04a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:22:51 +0200 Subject: Fix: features/ci/triggers_spec.rb --- spec/features/ci/triggers_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/features/ci/triggers_spec.rb b/spec/features/ci/triggers_spec.rb index c65bea9dbcf..c6afeb74628 100644 --- a/spec/features/ci/triggers_spec.rb +++ b/spec/features/ci/triggers_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' describe 'Triggers' do + let(:user) { create(:user) } + before do - login_as :user + login_as(user) @project = FactoryGirl.create :ci_project - stub_js_gitlab_calls + @project.gl_project.team << [user, :master] visit ci_project_triggers_path(@project) end -- cgit v1.2.1 From 93bb8f14ef2b19128c078250b36c62f681f30ee0 Mon Sep 17 00:00:00 2001 From: Jared Szechy Date: Tue, 15 Sep 2015 15:23:30 -0400 Subject: Use fixed version of fogbugz gem This allows us to properly handle authentication errors. --- Gemfile | 2 +- Gemfile.lock | 6 +++--- app/controllers/import/fogbugz_controller.rb | 8 +++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index d609ff6610f..5510d7ee639 100644 --- a/Gemfile +++ b/Gemfile @@ -158,7 +158,7 @@ gem "slack-notifier", "~> 1.0.0" gem 'asana', '~> 0.0.6' # FogBugz integration -gem 'ruby-fogbugz' +gem 'ruby-fogbugz', '~> 0.2.0' # d3 gem 'd3_rails', '~> 3.5.5' diff --git a/Gemfile.lock b/Gemfile.lock index 29f7213bd67..4f16e14c897 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -575,8 +575,8 @@ GEM powerpack (~> 0.0.6) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) - ruby-fogbugz (0.1.1) - crack + ruby-fogbugz (0.2.0) + crack (~> 0.4) ruby-progressbar (1.7.1) ruby-saml (1.0.0) nokogiri (>= 1.5.10) @@ -849,7 +849,7 @@ DEPENDENCIES rqrcode-rails3 rspec-rails (~> 3.3.0) rubocop (= 0.28.0) - ruby-fogbugz + ruby-fogbugz (~> 0.2.0) sanitize (~> 2.0) sass-rails (~> 4.0.5) sdoc diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index bda534fb4de..849646cd665 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -2,7 +2,6 @@ class Import::FogbugzController < Import::BaseController before_action :verify_fogbugz_import_enabled before_action :user_map, only: [:new_user_map, :create_user_map] - # Doesn't work yet due to bug in ruby-fogbugz, see below rescue_from Fogbugz::AuthenticationException, with: :fogbugz_unauthorized def new @@ -13,8 +12,8 @@ class Import::FogbugzController < Import::BaseController begin res = Gitlab::FogbugzImport::Client.new(import_params.symbolize_keys) rescue - # Needed until https://github.com/firmafon/ruby-fogbugz/pull/9 is merged - return redirect_to :back, alert: 'Could not authenticate with FogBugz, check your URL, email, and password' + # If the URI is invalid various errors can occur + return redirect_to new_import_fogbugz_path, alert: 'Could not connect to FogBugz, check your URL' end session[:fogbugz_token] = res.get_token session[:fogbugz_uri] = params[:uri] @@ -92,8 +91,7 @@ class Import::FogbugzController < Import::BaseController end def fogbugz_unauthorized(exception) - flash[:alert] = exception.message - redirect_to new_import_fogbugz_path + redirect_to new_import_fogbugz_path, alert: exception.message end def import_params -- cgit v1.2.1 From 6cc15cf721d9e52868c6f4e55b64b4cc4c18aba2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:23:45 +0200 Subject: Fix: features/ci/triggers_spec.rb --- spec/features/ci/variables_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/features/ci/variables_spec.rb b/spec/features/ci/variables_spec.rb index 84d6bfa0f32..e387b3be555 100644 --- a/spec/features/ci/variables_spec.rb +++ b/spec/features/ci/variables_spec.rb @@ -1,14 +1,16 @@ require 'spec_helper' describe "Variables" do + let(:user) { create(:user) } + before do - login_as :user + login_as(user) end describe "specific runners" do before do @project = FactoryGirl.create :ci_project - stub_js_gitlab_calls + @project.gl_project.team << [user, :master] end it "creates variable", js: true do -- cgit v1.2.1 From 5f315bd565a6fb4c778a32199ef276fafda56f20 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:24:57 +0200 Subject: Fix: helpers/ci/application_helper_spec.rb --- spec/helpers/ci/application_helper_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/helpers/ci/application_helper_spec.rb b/spec/helpers/ci/application_helper_spec.rb index 478c0266770..6a216715b7f 100644 --- a/spec/helpers/ci/application_helper_spec.rb +++ b/spec/helpers/ci/application_helper_spec.rb @@ -11,12 +11,12 @@ describe Ci::ApplicationHelper do } intervals_in_words.each do |interval, expectation| - duration_in_words(Time.now + interval, Time.now).should == expectation + expect(duration_in_words(Time.now + interval, Time.now)).to eq(expectation) end end it "calculates interval from now if there is no finished_at" do - duration_in_words(nil, Time.now - 5).should == "5 seconds" + expect(duration_in_words(nil, Time.now - 5)).to eq("5 seconds") end end @@ -30,7 +30,7 @@ describe Ci::ApplicationHelper do } intervals_in_words.each do |interval, expectation| - time_interval_in_words(interval).should == expectation + expect(time_interval_in_words(interval)).to eq(expectation) end end end -- cgit v1.2.1 From 10cf1050d08c672e0ee2c9823db31a51e7b54782 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:27:04 +0200 Subject: Fix: helpers/ci/runners_helper_spec.rb --- spec/helpers/ci/runners_helper_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb index e7681df10bd..6d0e2d3d1e1 100644 --- a/spec/helpers/ci/runners_helper_spec.rb +++ b/spec/helpers/ci/runners_helper_spec.rb @@ -2,17 +2,17 @@ require 'spec_helper' describe Ci::RunnersHelper do it "returns - not contacted yet" do - runner = FactoryGirl.build :runner - runner_status_icon(runner).should include("not connected yet") + runner = FactoryGirl.build :ci_runner + expect(runner_status_icon(runner)).to include("not connected yet") end it "returns offline text" do - runner = FactoryGirl.build(:runner, contacted_at: 1.day.ago, active: true) - runner_status_icon(runner).should include("Runner is offline") + runner = FactoryGirl.build(:ci_runner, contacted_at: 1.day.ago, active: true) + expect(runner_status_icon(runner)).to include("Runner is offline") end it "returns online text" do - runner = FactoryGirl.build(:runner, contacted_at: 1.hour.ago, active: true) - runner_status_icon(runner).should include("Runner is online") + runner = FactoryGirl.build(:ci_runner, contacted_at: 1.hour.ago, active: true) + expect(runner_status_icon(runner)).to include("Runner is online") end end -- cgit v1.2.1 From d495e4dff6aa62a2636b2a271e1c586dbdbdfee7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:31:29 +0200 Subject: Fix: models/ci/build_spec.rb --- spec/models/ci/build_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index dacdf38a875..ce801152042 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -175,7 +175,7 @@ describe Ci::Build do before { build.trace = text } it { is_expected.to include(text) } - it { is_expected.to have_at_least(text.length).items } + it { expect(subject.length).to be >= text.length } end end -- cgit v1.2.1 From 345ff6cbf93eca5d61218f28d5f4d9eb2d4abf67 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:47:01 +0200 Subject: Fix: models/ci/mail_service_spec.rb --- spec/models/ci/mail_service_spec.rb | 51 +++++++++++++------------------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index 564c2941bb5..316c374fd5f 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -29,6 +29,11 @@ describe Ci::MailService do describe 'Sends email for' do let(:mail) { Ci::MailService.new } + let(:deliveries) { ActionMailer::Base.deliveries} + + before(:each) do + deliveries.clear + end describe 'failed build' do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } @@ -42,13 +47,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") mail.execute(build) - end - - def should_email(email) - expect(Notify).to receive(:build_fail_email).with(build.id, email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(deliveries.count).to eq(1) + expect(deliveries[0].subject).to include('Build failed for') + expect(deliveries[0].to).to eq(["git@example.com"]) end end @@ -64,13 +66,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") mail.execute(build) - end - - def should_email(email) - expect(Notify).to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(deliveries.count).to eq(1) + expect(deliveries[0].subject).to include('Build success for') + expect(deliveries[0].to).to eq(["git@example.com"]) end end @@ -91,14 +90,12 @@ describe Ci::MailService do end it do - should_email("git@example.com") - should_email("jeroen@example.com") mail.execute(build) - end - - def should_email(email) - expect(Notify).to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(deliveries.count).to eq(2) + expect(deliveries[0].subject).to include('Build success for') + expect(deliveries[0].to).to eq(["jeroen@example.com"]) + expect(deliveries[1].subject).to include('Build success for') + expect(deliveries[1].to).to eq(["git@example.com"]) end end @@ -119,14 +116,8 @@ describe Ci::MailService do end it do - should_email(commit.git_author_email) - should_email("jeroen@example.com") mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(deliveries.count).to eq(0) end end @@ -170,14 +161,8 @@ describe Ci::MailService do it do Ci::Build.retry(build) - should_email(commit.git_author_email) - should_email("jeroen@example.com") mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(deliveries.count).to eq(0) end end end -- cgit v1.2.1 From 209c72c4a1acc103cff5d9f732865ad2dadea871 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 21:53:04 +0200 Subject: Fix: services/ci/create_project_service_spec.rb --- spec/services/ci/create_project_service_spec.rb | 26 ++++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index ce5b131f192..9f464d20fe7 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' describe Ci::CreateProjectService do let(:service) { Ci::CreateProjectService.new } let(:current_user) { double.as_null_object } - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } + let(:project) { FactoryGirl.create :project } describe :execute do context 'valid params' do - let(:project) { service.execute(current_user, project_dump, 'http://localhost/projects/:project_id') } + subject { service.execute(current_user, project, 'http://localhost/projects/:project_id') } - it { expect(project).to be_kind_of(Project) } - it { expect(project).to be_persisted } + it { is_expected.to be_kind_of(Ci::Project) } + it { is_expected.to be_persisted } end context 'without project dump' do @@ -20,18 +20,16 @@ describe Ci::CreateProjectService do end context "forking" do - it "uses project as a template for settings and jobs" do - origin_project = FactoryGirl.create(:ci_project) - origin_project.shared_runners_enabled = true - origin_project.public = true - origin_project.allow_git_fetch = true - origin_project.save! + let (:ci_origin_project) { + FactoryGirl.create(:ci_project, shared_runners_enabled: true, public: true, allow_git_fetch: true) + } - project = service.execute(current_user, project_dump, 'http://localhost/projects/:project_id', origin_project) + subject { service.execute(current_user, project, 'http://localhost/projects/:project_id', ci_origin_project) } - expect(project.shared_runners_enabled).to be_truthy - expect(project.public).to be_truthy - expect(project.allow_git_fetch).to be_truthy + it "uses project as a template for settings and jobs" do + expect(subject.shared_runners_enabled).to be_truthy + expect(subject.public).to be_truthy + expect(subject.allow_git_fetch).to be_truthy end end end -- cgit v1.2.1 From 8d5c9935ffb8fa41f831742ac287fd010a006b42 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:00:17 +0200 Subject: Fix: models/ci/project_spec.rb --- spec/models/ci/project_spec.rb | 18 ++++++---------- spec/support/gitlab_stubs/raw_project.yml | 36 ------------------------------- 2 files changed, 7 insertions(+), 47 deletions(-) delete mode 100644 spec/support/gitlab_stubs/raw_project.yml diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 48f76e11ce9..7c0fbbd60bb 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -124,19 +124,15 @@ describe Ci::Project do end describe 'Project.parse' do - let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) } - let(:parsed_project) { Ci::Project.parse(project_dump) } + let(:project) { FactoryGirl.create :project } + subject { Ci::Project.parse(project) } - it { expect(parsed_project).to be_valid } - it { expect(parsed_project).to be_kind_of(Ci::Project) } - it { expect(parsed_project.name).to eq("GitLab / api.gitlab.org") } - it { expect(parsed_project.gitlab_id).to eq(189) } - it { expect(parsed_project.gitlab_url).to eq("http://demo.gitlab.com/gitlab/api-gitlab-org") } - - it "parses plain hash" do - expect(Ci::Project.parse(project_dump).name).to eq("GitLab / api.gitlab.org") - end + it { is_expected.to be_valid } + it { is_expected.to be_kind_of(Ci::Project) } + it { expect(subject.name).to eq(project.name_with_namespace) } + it { expect(subject.gitlab_id).to eq(4) } + it { expect(subject.gitlab_url).to eq("http://localhost/namespace5/gitlabhq") } end describe :repo_url_with_auth do diff --git a/spec/support/gitlab_stubs/raw_project.yml b/spec/support/gitlab_stubs/raw_project.yml deleted file mode 100644 index df2ce223d1f..00000000000 --- a/spec/support/gitlab_stubs/raw_project.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- !ruby/object:OpenStruct -table: - :id: 189 - :description: Website at http://api.gitlab.org/ - :default_branch: master - :public: false - :visibility_level: 0 - :ssh_url_to_repo: dzaporozhets@localhost:gitlab/api-gitlab-org.git - :http_url_to_repo: http://localhost:3000/gitlab/api-gitlab-org.git - :web_url: http://localhost:3000/gitlab/api-gitlab-org - :owner: - id: 1 - name: GitLab - created_at: '2012-10-03T09:59:57.000Z' - :name: api.gitlab.org - :name_with_namespace: GitLab / api.gitlab.org - :path: api-gitlab-org - :path_with_namespace: gitlab/api-gitlab-org - :issues_enabled: true - :merge_requests_enabled: true - :wall_enabled: false - :wiki_enabled: false - :snippets_enabled: false - :created_at: '2013-06-06T12:29:39.000Z' - :last_activity_at: '2013-12-06T20:29:42.000Z' - :namespace: - id: 1 - name: GitLab - path: gitlab - owner_id: 1 - created_at: '2012-10-03T09:59:57.000Z' - updated_at: '2014-01-28T08:49:53.000Z' - description: Self hosted Git management software - avatar: - url: /uploads/group/avatar/1/0-vader-profile.jpg - -- cgit v1.2.1 From 874166d80f5dc946fc7b7715a16c9d39b6af77d3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:01:31 +0200 Subject: Fix models/ci/web_hook_spec.rb --- spec/models/ci/web_hook_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index 66170d111a9..c4c0b007c11 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -54,7 +54,7 @@ describe Ci::WebHook do end it "catches exceptions" do - expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") + expect(Ci::WebHook).to receive(:post).and_raise("Some HTTP Post error") expect{ @web_hook.execute(@data) }.to raise_error end -- cgit v1.2.1 From 29b3279a954825541bb0cfb14b6fe0a92c867c4e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:04:42 +0200 Subject: Fix: features/ci/events_spec.rb once again --- spec/features/ci/events_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb index 8142eaff274..5b9fd404159 100644 --- a/spec/features/ci/events_spec.rb +++ b/spec/features/ci/events_spec.rb @@ -1,11 +1,13 @@ require 'spec_helper' describe "Events" do + let(:user) { create(:user) } let(:project) { FactoryGirl.create :ci_project } - let(:event) { FactoryGirl.create :ci_admin_event, project: project } - + let(:event) { FactoryGirl.create :ci_admin_event, project: project } + before do - login_as :user + login_as(user) + project.gl_project.team << [user, :master] end describe "GET /ci/project/:id/events" do -- cgit v1.2.1 From e14aa19e39baf67bbc5bb4099627ae28844a4f5b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 15 Sep 2015 14:56:42 +0200 Subject: Cleanup some html/css for upload feature Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/common.scss | 8 ++++++++ app/assets/stylesheets/pages/tree.scss | 3 ++- app/views/projects/blob/_actions.html.haml | 6 +++--- app/views/projects/blob/_replace.html.haml | 8 ++++---- app/views/projects/blob/_upload.html.haml | 9 +++++---- app/views/projects/blob/new.html.haml | 11 +++++------ 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 3a237bf3228..48fad7701ef 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -382,3 +382,11 @@ table { margin-bottom: 0; } } + +.dropzone .dz-preview .dz-progress { + border-color: $border-color !important; +} + +.dropzone .dz-preview .dz-progress .dz-upload { + background: $gl-success !important; +} diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index df7fab07a57..271cc547e2b 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -121,10 +121,11 @@ text-align: center; border: 2px; border-style: dashed; + border-color: $border-color; min-height: 200px; } .upload-link { font-weight: normal; - color: #0000EE; + color: $md-link-color; } diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 5b61846fe6d..131818d2a83 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -17,6 +17,6 @@ tree_join(@commit.sha, @path)), class: 'btn btn-sm' - if allowed_tree_edit? - .btn-group{:role => "group"} - %button.btn.btn-default{class: 'btn-primary', href: '#modal-replace-blob', 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal'} Replace - %button.btn.btn-default{class: 'btn-remove', href: '#modal-remove-blob', 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal'} Remove + .btn-group{ role: "group" } + %button.btn.btn-default{ 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal' } Replace + %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove diff --git a/app/views/projects/blob/_replace.html.haml b/app/views/projects/blob/_replace.html.haml index 84abf0303d0..1d11fb0ae72 100644 --- a/app/views/projects/blob/_replace.html.haml +++ b/app/views/projects/blob/_replace.html.haml @@ -10,10 +10,10 @@ .modal-body = form_tag namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'blob-file-upload-form-js form-horizontal' do .dropzone - .dropzone-previews{class: "blob-upload-dropzone-previews"} - %p.dz-message{class: "hint"}< - Attach files by dragging & dropping or  - %a{href: '#', class: "markdown-selector"}>click to upload + .dropzone-previews.blob-upload-dropzone-previews + %p.dz-message.light + Attach files by dragging & dropping or + = link_to 'click to upload', '#', class: "markdown-selector" %br .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} = render 'shared/commit_message_container', params: params, diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 5a6a3358a17..4666faca013 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -10,10 +10,11 @@ .modal-body = form_tag namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'blob-file-upload-form-js form-horizontal' do .dropzone - .dropzone-previews{class: "blob-upload-dropzone-previews"} - %p.dz-message{class: "hint"}< - Attach files by dragging & dropping or  - %a{href: '#', class: "markdown-selector"}>click to upload + .dropzone-previews.blob-upload-dropzone-previews + %p.dz-message.light + Attach files by dragging & dropping or + = link_to 'click to upload', '#', class: "markdown-selector" + %br .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} = render 'shared/commit_message_container', params: params, diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 6fb46ea2040..9a50a52c453 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,10 +1,9 @@ -%h3.page-title< - Create new file or  - %a.upload-link{href: '#modal-upload-blob', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'}>upload existing one +.gray-content-block.top-block + Create new file or + = link_to 'upload existing one', '#modal-upload-blob', + { class: 'upload-link', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} -.file-title - = render 'projects/blob/upload' - %br += render 'projects/blob/upload' .file-editor = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do -- cgit v1.2.1 From 0a8ef29b3d2504ba66cd0a98992c1f5f70b11daa Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 15 Sep 2015 15:31:33 +0200 Subject: Improve wording for header and placeholders --- app/views/projects/blob/_replace.html.haml | 4 ++-- app/views/projects/blob/_upload.html.haml | 5 ++--- app/views/projects/blob/new.html.haml | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/projects/blob/_replace.html.haml b/app/views/projects/blob/_replace.html.haml index 1d11fb0ae72..ed584d19ed7 100644 --- a/app/views/projects/blob/_replace.html.haml +++ b/app/views/projects/blob/_replace.html.haml @@ -12,12 +12,12 @@ .dropzone .dropzone-previews.blob-upload-dropzone-previews %p.dz-message.light - Attach files by dragging & dropping or + Attach a file by drag & drop or = link_to 'click to upload', '#', class: "markdown-selector" %br .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} = render 'shared/commit_message_container', params: params, - placeholder: 'Replace this file because...' + placeholder: 'Replace file' .form-group .col-sm-offset-2.col-sm-10 = button_tag 'Replace file', class: 'btn btn-small btn-primary btn-replace-file', id: 'submit-all' diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 4666faca013..4ed36cb8aa0 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -12,13 +12,12 @@ .dropzone .dropzone-previews.blob-upload-dropzone-previews %p.dz-message.light - Attach files by dragging & dropping or + Attach a file by drag & drop or = link_to 'click to upload', '#', class: "markdown-selector" - %br .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} = render 'shared/commit_message_container', params: params, - placeholder: 'Upload this file because...' + placeholder: 'Upload new file' .form-group .col-sm-offset-2.col-sm-10 = button_tag 'Upload file', class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 9a50a52c453..68c9ec7f802 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,7 +1,8 @@ .gray-content-block.top-block - Create new file or - = link_to 'upload existing one', '#modal-upload-blob', + Create a new file or + = link_to 'upload', '#modal-upload-blob', { class: 'upload-link', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} + an existing one = render 'projects/blob/upload' -- cgit v1.2.1 From d9af6f79ed439308d13f44a0cfade6a355607f14 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 15 Sep 2015 06:58:49 -0700 Subject: Change the replace placeholder to use the filename Dynamically adjust placedholder for uploads and fix Dropzone event handlers Override error handler to prevent error messages from being inserted underneath image preview Fix tests Use regexp instead of startsWith for better browser compatibility Remove duplicate code in _replace.html.haml and use one template Remove files upon error and retain alert messages until user adds a new file --- .../javascripts/blob/blob_file_dropzone.js.coffee | 47 ++++++++++++++-------- app/controllers/projects/blob_controller.rb | 11 +++++ app/views/projects/blob/_actions.html.haml | 2 +- app/views/projects/blob/_replace.html.haml | 28 ------------- app/views/projects/blob/_upload.html.haml | 12 +++--- app/views/projects/blob/show.html.haml | 2 +- features/project/source/browse_files.feature | 6 +-- features/steps/project/source/browse_files.rb | 10 ++--- 8 files changed, 58 insertions(+), 60 deletions(-) delete mode 100644 app/views/projects/blob/_replace.html.haml diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee index 090af9bb376..3ab3ba66754 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee +++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee @@ -20,26 +20,41 @@ class @BlobFileDropzone headers: "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") - success: (header, response) -> - window.location.href = response.filePath - return + init: -> + this.on 'addedfile', (file) -> + $('.dropzone-alerts').html('').hide() + commit_message = form.find('#commit_message')[0] - error: (temp, errorMessage) -> - stripped = $("
    ").html(errorMessage).text(); - $('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show() - return + if /^Upload/.test(commit_message.placeholder) + commit_message.placeholder = 'Upload ' + file.name - maxfilesexceeded: (file) -> - @removeFile file - return + return + + this.on 'removedfile', (file) -> + commit_message = form.find('#commit_message')[0] - removedfile: (file) -> - $('.dropzone-previews')[0].removeChild(file.previewTemplate) - $('.dropzone-alerts').html('').hide() - return true + if /^Upload/.test(commit_message.placeholder) + commit_message.placeholder = 'Upload new file' - sending: (file, xhr, formData) -> - formData.append('commit_message', form.find('#commit_message').val()) + return + + this.on 'success', (header, response) -> + window.location.href = response.filePath + return + + this.on 'maxfilesexceeded', (file) -> + @removeFile file + return + + this.on 'sending', (file, xhr, formData) -> + formData.append('commit_message', form.find('#commit_message').val()) + return + + # Override behavior of adding error underneath preview + error: (file, errorMessage) -> + stripped = $("
    ").html(errorMessage).text(); + $('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show() + @removeFile file return ) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 8776721d243..d7be212c33a 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -18,6 +18,12 @@ class Projects::BlobController < Projects::ApplicationController before_action :after_edit_path, only: [:edit, :update] def new + @title = 'Upload' + @placeholder = 'Upload new file' + @button_title = 'Upload file' + @form_path = namespace_project_create_blob_path(@project.namespace, @project, @id) + @method = :post + commit unless @repository.empty? end @@ -40,6 +46,11 @@ class Projects::BlobController < Projects::ApplicationController end def show + @title = "Replace #{@blob.name}" + @placeholder = @title + @button_title = 'Replace file' + @form_path = namespace_project_update_blob_path(@project.namespace, @project, @id) + @method = :put end def edit diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 131818d2a83..373b3a0c5b0 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -18,5 +18,5 @@ - if allowed_tree_edit? .btn-group{ role: "group" } - %button.btn.btn-default{ 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal' } Replace + %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove diff --git a/app/views/projects/blob/_replace.html.haml b/app/views/projects/blob/_replace.html.haml deleted file mode 100644 index ed584d19ed7..00000000000 --- a/app/views/projects/blob/_replace.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -#modal-replace-blob.modal - .modal-dialog - .modal-content - .modal-header - %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title Replace #{@blob.name} - %p.light - From branch - %strong= @ref - .modal-body - = form_tag namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'blob-file-upload-form-js form-horizontal' do - .dropzone - .dropzone-previews.blob-upload-dropzone-previews - %p.dz-message.light - Attach a file by drag & drop or - = link_to 'click to upload', '#', class: "markdown-selector" - %br - .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} - = render 'shared/commit_message_container', params: params, - placeholder: 'Replace file' - .form-group - .col-sm-offset-2.col-sm-10 - = button_tag 'Replace file', class: 'btn btn-small btn-primary btn-replace-file', id: 'submit-all' - = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" - -:coffeescript - disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-replace-file' - new BlobFileDropzone($('.blob-file-upload-form-js'), 'put') diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 4ed36cb8aa0..2cfb79486dc 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -3,26 +3,26 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title Upload + %h3.page-title #{@title} %p.light From branch %strong= @ref .modal-body - = form_tag namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'blob-file-upload-form-js form-horizontal' do + = form_tag @form_path, method: @method, class: 'blob-file-upload-form-js form-horizontal' do .dropzone .dropzone-previews.blob-upload-dropzone-previews %p.dz-message.light Attach a file by drag & drop or = link_to 'click to upload', '#', class: "markdown-selector" %br - .dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"} + .dropzone-alerts{class: "alert alert-danger data", style: "display:none"} = render 'shared/commit_message_container', params: params, - placeholder: 'Upload new file' + placeholder: @placeholder .form-group .col-sm-offset-2.col-sm-10 - = button_tag 'Upload file', class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' + = button_tag @button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :coffeescript disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file' - new BlobFileDropzone($('.blob-file-upload-form-js'), 'post') + new BlobFileDropzone($('.blob-file-upload-form-js'), '#{@method}') diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 19e876ec34c..4e66a43bbd5 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -10,4 +10,4 @@ - if allowed_tree_edit? = render 'projects/blob/remove' - = render 'projects/blob/replace' + = render 'projects/blob/upload' diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index b5b6abe6aff..58574166ef3 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -33,13 +33,13 @@ Feature: Project Source Browse Files And I click on "Commit Changes" Then I am redirected to the new file And I should see its new content - + @javascript Scenario: I can upload file and commit Given I click on "new file" link in repo Then I can see new file page - And I can see "upload existing one" - And I click on "upload existing one" + And I can see "upload an existing one" + And I click on "upload" And I upload a new text file And I fill the upload file commit message And I click on "Upload file" diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 7a0ee4df45e..a1a49dd58a6 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -119,12 +119,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).to have_content "Commit message" end - step 'I can see "upload existing one"' do - expect(page).to have_content "upload existing one" + step 'I can see "upload an existing one"' do + expect(page).to have_content "upload an existing one" end - step 'I click on "upload existing one"' do - click_link 'upload existing one' + step 'I click on "upload"' do + click_link 'upload' end step 'I click on "Upload file"' do @@ -150,7 +150,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I fill the replace file commit message' do - page.within('#modal-replace-blob') do + page.within('#modal-upload-blob') do fill_in :commit_message, with: 'Replacement file commit message' end end -- cgit v1.2.1 From c95b8476c7d7464071afed0e3f4279ab116d8a2f Mon Sep 17 00:00:00 2001 From: Jared Szechy Date: Tue, 15 Sep 2015 16:09:32 -0400 Subject: bump fogbugz gem --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 5510d7ee639..6d371f27a34 100644 --- a/Gemfile +++ b/Gemfile @@ -158,7 +158,7 @@ gem "slack-notifier", "~> 1.0.0" gem 'asana', '~> 0.0.6' # FogBugz integration -gem 'ruby-fogbugz', '~> 0.2.0' +gem 'ruby-fogbugz', '~> 0.2.1' # d3 gem 'd3_rails', '~> 3.5.5' diff --git a/Gemfile.lock b/Gemfile.lock index 4f16e14c897..ab743c7d590 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -575,7 +575,7 @@ GEM powerpack (~> 0.0.6) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) - ruby-fogbugz (0.2.0) + ruby-fogbugz (0.2.1) crack (~> 0.4) ruby-progressbar (1.7.1) ruby-saml (1.0.0) @@ -849,7 +849,7 @@ DEPENDENCIES rqrcode-rails3 rspec-rails (~> 3.3.0) rubocop (= 0.28.0) - ruby-fogbugz (~> 0.2.0) + ruby-fogbugz (~> 0.2.1) sanitize (~> 2.0) sass-rails (~> 4.0.5) sdoc -- cgit v1.2.1 From fe42de3a48a43ccea2bb60c8dcdb4d6665a72eef Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:13:01 +0200 Subject: Fix: features/ci/admin/runners_spec.rb --- app/models/ci/runner.rb | 2 +- app/views/ci/admin/runners/index.html.haml | 2 +- app/views/ci/admin/runners/show.html.haml | 2 +- spec/features/ci/admin/runners_spec.rb | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 79c81df5eb2..1e9f78a3748 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -37,7 +37,7 @@ module Ci acts_as_taggable def self.search(query) - where('LOWER(runners.token) LIKE :query OR LOWER(runners.description) like :query', + where('LOWER(ci_runners.token) LIKE :query OR LOWER(ci_runners.description) like :query', query: "%#{query.try(:downcase)}%") end diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index d578ff922ad..b9d6703ff41 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -25,7 +25,7 @@ .append-bottom-20.clearfix .pull-left - = form_tag ci_admin_runners_path, class: 'form-inline', method: :get do + = form_tag ci_admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do .form-group = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token' = submit_tag 'Search', class: 'btn' diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 0270da53349..24e0ad3b070 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -74,7 +74,7 @@ %tr %td - = form_tag ci_admin_runner_path(@runner), class: 'form-inline', method: :get do + = form_tag ci_admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do .form-group = search_field_tag :search, params[:search], class: 'form-control' = submit_tag 'Search', class: 'btn' diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index 644d48ac298..b25121f0806 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -23,8 +23,9 @@ describe "Admin Runners" do FactoryGirl.create :ci_runner, description: 'foo' FactoryGirl.create :ci_runner, description: 'bar' - fill_in 'search', with: 'foo' - click_button 'Search' + search_form = find('#runners-search') + search_form.fill_in 'search', with: 'foo' + search_form.click_button 'Search' end it { expect(page).to have_content("foo") } @@ -52,8 +53,9 @@ describe "Admin Runners" do describe 'search' do before do - fill_in 'search', with: 'foo' - click_button 'Search' + search_form = find('#runner-projects-search') + search_form.fill_in 'search', with: 'foo' + search_form.click_button 'Search' end it { expect(page).to have_content("foo") } -- cgit v1.2.1 From d3b62e683730df2d93a6c39f7c0da6bd24640ee3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:19:31 +0200 Subject: Add missing builds/ folder to fix backup tests --- builds/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 builds/.gitkeep diff --git a/builds/.gitkeep b/builds/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 81d98112d409e674dbdc47c0920a6d865c9eb49e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:21:51 +0200 Subject: Fix: tasks/gitlab/backup_rake_spec.rb --- spec/tasks/gitlab/backup_rake_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 32adcbec71f..36e425bb490 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -112,19 +112,19 @@ describe 'gitlab:app namespace rake task' do it 'should set correct permissions on the tar contents' do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads repositories} + %W{tar -tvf #{@backup_tar} db uploads repositories builds} ) expect(exit_status).to eq(0) expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads/') expect(tar_contents).to match('repositories/') expect(tar_contents).to match('builds/') - expect(tar_contents).not_to match(/^.{4,9}[rwx].* (db|uploads|repositories)\/$/) + expect(tar_contents).not_to match(/^.{4,9}[rwx].* (db|uploads|repositories|builds)\/$/) end it 'should delete temp directories' do temp_dirs = Dir.glob( - File.join(Gitlab.config.backup.path, '{db,repositories,uploads}') + File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds}') ) expect(temp_dirs).to be_empty -- cgit v1.2.1 From 0d23676f9d8dff5cad6ae2a11409d6026f423f21 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:23:43 +0200 Subject: Fix: rubocop --- spec/services/ci/create_project_service_spec.rb | 4 ++-- spec/services/ci/image_for_build_service_spec.rb | 2 +- spec/services/ci/register_build_service_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index 9f464d20fe7..64041b8d5a2 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -20,9 +20,9 @@ describe Ci::CreateProjectService do end context "forking" do - let (:ci_origin_project) { + let(:ci_origin_project) do FactoryGirl.create(:ci_project, shared_runners_enabled: true, public: true, allow_git_fetch: true) - } + end subject { service.execute(current_user, project, 'http://localhost/projects/:project_id', ci_origin_project) } diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index fdeb754d689..7565eb8f032 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -45,4 +45,4 @@ module Ci end end end -end \ No newline at end of file +end diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 7d665d9a112..7b5af6c3dd0 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -1,4 +1,4 @@ - require 'spec_helper' +require 'spec_helper' module Ci describe RegisterBuildService do -- cgit v1.2.1 From 7e2dbcbe0915cfd75e91d78e943c153f284df37d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:36:25 +0200 Subject: Fix: models/ci/project_spec.rb once again --- spec/models/ci/project_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 7c0fbbd60bb..f24a77d08ff 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -131,8 +131,8 @@ describe Ci::Project do it { is_expected.to be_valid } it { is_expected.to be_kind_of(Ci::Project) } it { expect(subject.name).to eq(project.name_with_namespace) } - it { expect(subject.gitlab_id).to eq(4) } - it { expect(subject.gitlab_url).to eq("http://localhost/namespace5/gitlabhq") } + it { expect(subject.gitlab_id).to eq(project.id) } + it { expect(subject.gitlab_url).to eq(project.path_with_namespace) } end describe :repo_url_with_auth do -- cgit v1.2.1 From ddde3e3ae8c38b81b28525d12ed6787a78160614 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:42:30 +0200 Subject: Remove gitlab_ci_meta from gems --- Gemfile | 2 -- Gemfile.lock | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 1a7af589f6d..0cc69125c62 100644 --- a/Gemfile +++ b/Gemfile @@ -301,8 +301,6 @@ gem 'whenever', '~> 0.8.4', require: false # OAuth gem 'oauth2', '~> 1.0.0' -gem 'gitlab_ci_meta', '~> 4.0' - # Soft deletion gem "paranoia", "~> 2.0" diff --git a/Gemfile.lock b/Gemfile.lock index 65050a71c34..2fd4d0dd65b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -825,7 +825,6 @@ DEPENDENCIES github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-linguist (~> 3.0.1) - gitlab_ci_meta (~> 4.0) gitlab_emoji (~> 0.1) gitlab_git (~> 7.2.15) gitlab_meta (= 7.0) @@ -887,7 +886,7 @@ DEPENDENCIES rerun (~> 0.10.0) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) - rubocop (= 0.28.0) + rubocop (~> 0.28.0) ruby-fogbugz (~> 0.2.0) sanitize (~> 2.0) sass-rails (~> 4.0.5) -- cgit v1.2.1 From c9d914a392be0b3aaf2f287cdaf728008eb00b9f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 22:48:28 +0200 Subject: Fix builds directory store --- .gitignore | 2 +- lib/backup/builds.rb | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 39caad149b3..2a97eacad48 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,4 @@ rails_best_practices_output.html /tags tmp/ vendor/bundle/* -/ci/builds/* +builds/* diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index 4280438e86c..6f56f680bb9 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -3,14 +3,18 @@ module Backup attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir def initialize - @app_builds_dir = File.realpath(Rails.root.join('ci/builds')) - @backup_dir = GitlabCi.config.backup.path - @backup_builds_dir = File.join(GitlabCi.config.backup.path, 'ci/builds') + @app_builds_dir = Settings.gitlab_ci.builds_path + @backup_dir = Gitlab.config.backup.path + @backup_builds_dir = File.join(Gitlab.config.backup.path, 'builds') end # Copy builds from builds directory to backup/builds def dump - FileUtils.mkdir_p(backup_builds_dir) + FileUtils.rm_rf(backup_builds_dir) + # Ensure the parent dir of backup_builds_dir exists + FileUtils.mkdir_p(Gitlab.config.backup.path) + # Fail if somebody raced to create backup_builds_dir before us + FileUtils.mkdir(backup_builds_dir, mode: 0700) FileUtils.cp_r(app_builds_dir, backup_dir) end -- cgit v1.2.1 From 757fdd34dce9a6783d4d3fa9e91f8e302590f242 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 23:08:40 +0200 Subject: Revert "Fix: models/ci/mail_service_spec.rb" This reverts commit 345ff6cbf93eca5d61218f28d5f4d9eb2d4abf67. This requires sidekiq >= 3.4 and fixed all other CE tests --- spec/models/ci/mail_service_spec.rb | 51 ++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index 316c374fd5f..564c2941bb5 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -29,11 +29,6 @@ describe Ci::MailService do describe 'Sends email for' do let(:mail) { Ci::MailService.new } - let(:deliveries) { ActionMailer::Base.deliveries} - - before(:each) do - deliveries.clear - end describe 'failed build' do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } @@ -47,10 +42,13 @@ describe Ci::MailService do end it do + should_email("git@example.com") mail.execute(build) - expect(deliveries.count).to eq(1) - expect(deliveries[0].subject).to include('Build failed for') - expect(deliveries[0].to).to eq(["git@example.com"]) + end + + def should_email(email) + expect(Notify).to receive(:build_fail_email).with(build.id, email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) end end @@ -66,10 +64,13 @@ describe Ci::MailService do end it do + should_email("git@example.com") mail.execute(build) - expect(deliveries.count).to eq(1) - expect(deliveries[0].subject).to include('Build success for') - expect(deliveries[0].to).to eq(["git@example.com"]) + end + + def should_email(email) + expect(Notify).to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -90,12 +91,14 @@ describe Ci::MailService do end it do + should_email("git@example.com") + should_email("jeroen@example.com") mail.execute(build) - expect(deliveries.count).to eq(2) - expect(deliveries[0].subject).to include('Build success for') - expect(deliveries[0].to).to eq(["jeroen@example.com"]) - expect(deliveries[1].subject).to include('Build success for') - expect(deliveries[1].to).to eq(["git@example.com"]) + end + + def should_email(email) + expect(Notify).to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -116,8 +119,14 @@ describe Ci::MailService do end it do + should_email(commit.git_author_email) + should_email("jeroen@example.com") mail.execute(build) if mail.can_execute?(build) - expect(deliveries.count).to eq(0) + end + + def should_email(email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -161,8 +170,14 @@ describe Ci::MailService do it do Ci::Build.retry(build) + should_email(commit.git_author_email) + should_email("jeroen@example.com") mail.execute(build) if mail.can_execute?(build) - expect(deliveries.count).to eq(0) + end + + def should_email(email) + expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Notify).not_to receive(:build_fail_email).with(build.id, email) end end end -- cgit v1.2.1 From 2d8c4273ef831bb3eb0dd0680d1d99f11feb6c5d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 23:18:52 +0200 Subject: Fix models/ci/mail_service_spec.rb - Change Notify to Ci::Notify - Downgrade sidekiq to fix CE errors: otherwise we need to check deliverables --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- spec/models/ci/mail_service_spec.rb | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Gemfile b/Gemfile index 0cc69125c62..82adfb556f6 100644 --- a/Gemfile +++ b/Gemfile @@ -128,7 +128,7 @@ gem 'acts-as-taggable-on', '~> 3.4' # Background jobs gem 'slim', '~> 2.0.2' gem 'sinatra', '~> 1.4.4', require: nil -gem 'sidekiq', '~> 3.3' +gem 'sidekiq', '3.3.0' gem 'sidetiq', '~> 0.6.3' # HTTP requests diff --git a/Gemfile.lock b/Gemfile.lock index 2fd4d0dd65b..1f300b0c779 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -338,7 +338,7 @@ GEM hipchat (1.5.2) httparty mimemagic - hitimes (1.2.2) + hitimes (1.2.3) html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) @@ -637,12 +637,12 @@ GEM shellany (0.0.1) shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (3.4.2) - celluloid (~> 0.16.0) - connection_pool (~> 2.2, >= 2.2.0) - json (~> 1.0) - redis (~> 3.2, >= 3.2.1) - redis-namespace (~> 1.5, >= 1.5.2) + sidekiq (3.3.0) + celluloid (>= 0.16.0) + connection_pool (>= 2.0.0) + json + redis (>= 3.0.6) + redis-namespace (>= 1.3.1) sidetiq (0.6.3) celluloid (>= 0.14.1) ice_cube (= 0.11.1) @@ -710,7 +710,7 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.0.1) + timers (4.0.4) hitimes timfel-krb5-auth (0.8.3) tinder (1.9.4) @@ -896,7 +896,7 @@ DEPENDENCIES settingslogic (~> 2.0.9) sham_rack shoulda-matchers (~> 2.8.0) - sidekiq (~> 3.3) + sidekiq (= 3.3.0) sidetiq (~> 0.6.3) simplecov (~> 0.10.0) sinatra (~> 1.4.4) diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index 564c2941bb5..b5f37b349db 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -47,8 +47,8 @@ describe Ci::MailService do end def should_email(email) - expect(Notify).to receive(:build_fail_email).with(build.id, email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) end end @@ -69,8 +69,8 @@ describe Ci::MailService do end def should_email(email) - expect(Notify).to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -97,8 +97,8 @@ describe Ci::MailService do end def should_email(email) - expect(Notify).to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -125,8 +125,8 @@ describe Ci::MailService do end def should_email(email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) end end @@ -176,8 +176,8 @@ describe Ci::MailService do end def should_email(email) - expect(Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Notify).not_to receive(:build_fail_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) + expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) end end end -- cgit v1.2.1 From 416d98b497db6e8c8ad46072e17e835da27f23c7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 23:42:57 +0200 Subject: Fix: models/ci/project_spec.rb once again --- spec/models/ci/project_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index f24a77d08ff..1025868da6e 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -132,7 +132,7 @@ describe Ci::Project do it { is_expected.to be_kind_of(Ci::Project) } it { expect(subject.name).to eq(project.name_with_namespace) } it { expect(subject.gitlab_id).to eq(project.id) } - it { expect(subject.gitlab_url).to eq(project.path_with_namespace) } + it { expect(subject.gitlab_url).to eq(project.web_url) } end describe :repo_url_with_auth do -- cgit v1.2.1 From c9389d0e7450850423c8c87062f6c26422b78422 Mon Sep 17 00:00:00 2001 From: Allister Antosik Date: Tue, 15 Sep 2015 22:48:24 +0100 Subject: Sorted autocomplete users list by name --- CHANGELOG | 1 + app/controllers/autocomplete_controller.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 007e47f7446..eb4c59d6205 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -48,6 +48,7 @@ v 8.0.0 (unreleased) - Fix highlighting of deleted lines in diffs. - Added service API endpoint to retrieve service parameters (Petheő Bence) - Add FogBugz project import (Jared Szechy) + - Sort users autocomplete lists by user (Allister Antosik) v 7.14.3 - No changes diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 904d26a39f4..202e9da9eee 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -32,6 +32,7 @@ class AutocompleteController < ApplicationController @users ||= User.none @users = @users.search(params[:search]) if params[:search].present? @users = @users.active + @users = @users.reorder(:name) @users = @users.page(params[:page]).per(PER_PAGE) unless params[:search].present? -- cgit v1.2.1 From 2f2b9f67c21a504826595079e103f1ea9ac813f2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 00:00:08 +0200 Subject: Fix backup tests --- lib/backup/manager.rb | 2 +- spec/support/setup_builds_storage.rb | 3 ++- spec/tasks/gitlab/backup_rake_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 13c68d9354f..ac63f89c6ec 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -153,7 +153,7 @@ module Backup end def folders_to_backup - folders = %w{repositories db uploads} + folders = %w{repositories db uploads builds} if ENV["SKIP"] return folders.reject{ |folder| ENV["SKIP"].include?(folder) } diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb index 1f3b12bb8d2..a3e59646187 100644 --- a/spec/support/setup_builds_storage.rb +++ b/spec/support/setup_builds_storage.rb @@ -1,10 +1,11 @@ RSpec.configure do |config| def builds_path - Rails.root.join('tmp/builds_test') + Rails.root.join('tmp/builds') end config.before(:each) do FileUtils.mkdir_p(builds_path) + FileUtils.touch(File.join(builds_path, ".gitkeep")) Settings.gitlab_ci['builds_path'] = builds_path end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 36e425bb490..2e63e5f36af 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do end def reenable_backup_sub_tasks - %w{db repo uploads}.each do |subtask| + %w{db repo uploads builds}.each do |subtask| Rake::Task["gitlab:backup:#{subtask}:create"].reenable end end @@ -160,7 +160,7 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads repositories} + %W{tar -tvf #{@backup_tar} db uploads repositories builds} ) expect(tar_contents).to match('db/') -- cgit v1.2.1 From 9c5833d5acd6efecf54e4c910dd7c4e89d4ddca6 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 16:35:27 +0200 Subject: Add rake task for easy migration of SQL dumps --- lib/ci/migrate/database.rb | 67 ++++++++++++++++++++++++++++++++++++++++++++ lib/ci/migrate/tags.rb | 49 ++++++++++++++++++++++++++++++++ lib/tasks/ci/migrate.rake | 70 ++++++++++++++++++++++++++++------------------ 3 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 lib/ci/migrate/database.rb create mode 100644 lib/ci/migrate/tags.rb diff --git a/lib/ci/migrate/database.rb b/lib/ci/migrate/database.rb new file mode 100644 index 00000000000..74f592dcaea --- /dev/null +++ b/lib/ci/migrate/database.rb @@ -0,0 +1,67 @@ +require 'yaml' + +module Ci + module Migrate + class Database + attr_reader :config + + def initialize + @config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env] + end + + def restore(ci_dump) + puts 'Deleting all CI related data ... ' + truncate_ci_tables + + puts 'Restoring CI data ... ' + case config["adapter"] + when /^mysql/ then + print "Restoring MySQL database #{config['database']} ... " + # Workaround warnings from MySQL 5.6 about passwords on cmd line + ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] + system('mysql', *mysql_args, config['database'], in: ci_dump) + when "postgresql" then + puts "Restoring PostgreSQL database #{config['database']} ... " + pg_env + system('psql', config['database'], '-f', ci_dump) + end + end + + protected + + def truncate_ci_tables + c = ActiveRecord::Base.connection + c.tables.select { |t| t.start_with?('ci_') }.each do |table| + puts "Deleting data from #{table}..." + c.execute("DELETE FROM #{table}") + end + end + + def mysql_args + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set' + } + args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + end + + def pg_env + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] + end + + def report_success(success) + if success + puts '[DONE]'.green + else + puts '[FAILED]'.red + end + end + end + end +end diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb new file mode 100644 index 00000000000..f4114c698d2 --- /dev/null +++ b/lib/ci/migrate/tags.rb @@ -0,0 +1,49 @@ +require 'yaml' + +module Ci + module Migrate + class Tags + def restore + puts 'Migrating tags for Runners... ' + list_objects('Runner').each do |id| + putc '.' + runner = Ci::Runner.find_by_id(id) + if runner + tags = list_tags('Runner', id) + runner.update_attributes(tag_list: tags) + end + end + puts '' + + puts 'Migrating tags for Builds... ' + list_objects('Build').each do |id| + putc '.' + build = Ci::Build.find_by_id(id) + if build + tags = list_tags('Build', id) + build.update_attributes(tag_list: tags) + end + end + puts '' + end + + protected + + def list_objects(type) + ids = ActiveRecord::Base.connection.select_all( + "select distinct taggable_id from ci_taggings where taggable_type = #{ActiveRecord::Base::sanitize(type)}" + ) + ids.map { |id| id['taggable_id'] } + end + + def list_tags(type, id) + tags = ActiveRecord::Base.connection.select_all( + 'select ci_tags.name from ci_tags ' + + 'join ci_taggings on ci_tags.id = ci_taggings.tag_id ' + + "where taggable_type = #{ActiveRecord::Base::sanitize(type)} and taggable_id = #{ActiveRecord::Base::sanitize(id)} and context = \"tags\"" + ) + tags.map { |tag| tag['name'] } + end + end + end +end diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake index 7d99664dcf3..2760c503e22 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -1,38 +1,54 @@ namespace :ci do - namespace :migrate do - def list_objects(type) - ids = ActiveRecord::Base.connection.select_all( - 'select distinct taggable_id from ci_taggings where taggable_type = $1', - nil, [[nil, type]] - ) - ids.map { |id| id['taggable_id'] } + desc 'GitLab | Import and migrate CI database' + task migrate: :environment do + unless ENV['force'] == 'yes' + puts "This will truncate all CI tables and restore it from provided backup." + puts "You will lose any previous CI data stored in the database." + ask_to_continue + puts "" end - def list_tags(type, id) - tags = ActiveRecord::Base.connection.select_all( - 'select ci_tags.name from ci_tags ' + - 'join ci_taggings on ci_tags.id = ci_taggings.tag_id ' + - 'where taggable_type = $1 and taggable_id = $2 and context = $3', - nil, [[nil, type], [nil, id], [nil, 'tags']] - ) - tags.map { |tag| tag['name'] } + Rake::Task["ci:migrate:db"].invoke + Rake::Task["ci:migrate:autoincrements"].invoke + Rake::Task["ci:migrate:tags"].invoke + end + + namespace :migrate do + desc 'GitLab | Import CI database' + task db: :environment do + if ENV["CI_DUMP"].nil? + puts "No CI SQL dump specified:" + puts "rake gitlab:backup:restore CI_DUMP=ci_dump.sql" + exit 1 + end + + ci_dump = ENV["CI_DUMP"] + unless File.exists?(ci_dump) + puts "The specified sql dump doesn't exist!" + exit 1 + end + + ::Ci::Migrate::Database.new.restore(ci_dump) end desc 'GitLab | Migrate CI tags' task tags: :environment do - list_objects('Runner').each do |id| - runner = Ci::Runner.find_by_id(id) - if runner - tags = list_tags('Runner', id) - runner.update_attributes(tag_list: tags) - end - end + ::Ci::Migrate::Tags.new.restore + end - list_objects('Build').each do |id| - build = Ci::Build.find_by_id(id) - if build - tags = list_tags('Build', id) - build.update_attributes(tag_list: tags) + desc 'GitLab | Migrate CI auto-increments' + task autoincrements: :environment do + c = ActiveRecord::Base.connection + c.tables.select { |t| t.start_with?('ci_') }.each do |table| + result = c.select_one("SELECT id FROM #{table} ORDER BY id DESC LIMIT 1") + if result + ai_val = result['id'].to_i + 1 + puts "Resetting auto increment ID for #{table} to #{ai_val}" + if c.adapter_name == 'PostgreSQL' + c.execute("ALTER SEQUENCE #{table}_id_seq RESTART WITH #{ai_val}") + else + c.execute("ALTER TABLE #{table} AUTO_INCREMENT = #{ai_val}") + end end end end -- cgit v1.2.1 From f2d8902341b298a842d02d2a21a938085d4840b7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Sep 2015 20:13:34 +0200 Subject: Start working on migration docs --- doc/migrate/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/migrate/README.md diff --git a/doc/migrate/README.md b/doc/migrate/README.md new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 1b464bfe03be2f05f509a96740a9f9d024e8217d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 00:28:59 +0200 Subject: Update documentation and config files --- config/gitlab.yml.example | 22 +- doc/README.md | 22 ++ doc/ci/README.md | 4 - doc/ci/api/README.md | 4 +- doc/ci/api/builds.md | 4 +- doc/ci/api/commits.md | 4 +- doc/ci/api/forks.md | 2 +- doc/ci/api/projects.md | 16 +- doc/ci/api/runners.md | 6 +- doc/ci/docker/using_docker_build.md | 4 +- doc/ci/docker/using_docker_images.md | 2 +- ...test-and-deploy-python-application-to-heroku.md | 4 +- .../test-and-deploy-ruby-application-to-heroku.md | 4 +- doc/ci/examples/test-clojure-application.md | 2 +- doc/ci/install/README.md | 276 --------------------- doc/ci/install/requirements.md | 61 ----- doc/ci/migration_to_omnibus/README.md | 29 --- doc/ci/quick_start/README.md | 6 +- doc/ci/raketasks/README.md | 3 - doc/ci/raketasks/backup_restore.md | 237 ------------------ doc/ci/update/3.0-to-3.1.md | 30 --- doc/ci/update/3.1-to-3.2.md | 30 --- doc/ci/update/3.2-to-4.0.md | 35 --- doc/ci/update/4.0-to-4.1.md | 49 ---- doc/ci/update/4.1-to-4.2.md | 47 ---- doc/ci/update/4.2-to-4.3.md | 61 ----- doc/ci/update/4.3-to-5.0.md | 42 ---- doc/ci/update/5.0-to-5.1.md | 42 ---- doc/ci/update/5.1-to-5.2.md | 42 ---- doc/ci/update/5.2-to-5.3.md | 42 ---- doc/ci/update/5.3-to-5.4.md | 60 ----- doc/ci/update/5.4-to-7.8.md | 65 ----- doc/ci/update/7.10-to-7.11.md | 45 ---- doc/ci/update/7.11-to-7.12.md | 67 ----- doc/ci/update/7.12-to-7.13.md | 63 ----- doc/ci/update/7.8-to-7.9.md | 66 ----- doc/ci/update/7.9-to-7.10.md | 49 ---- doc/ci/update/README.md | 2 - doc/ci/update/patch_versions.md | 59 ----- doc/install/installation.md | 23 ++ doc/release/monthly.md | 2 +- doc/update/7.14-to-8.0.md | 26 +- 42 files changed, 116 insertions(+), 1543 deletions(-) delete mode 100644 doc/ci/install/README.md delete mode 100644 doc/ci/install/requirements.md delete mode 100644 doc/ci/migration_to_omnibus/README.md delete mode 100644 doc/ci/raketasks/README.md delete mode 100644 doc/ci/raketasks/backup_restore.md delete mode 100644 doc/ci/update/3.0-to-3.1.md delete mode 100644 doc/ci/update/3.1-to-3.2.md delete mode 100644 doc/ci/update/3.2-to-4.0.md delete mode 100644 doc/ci/update/4.0-to-4.1.md delete mode 100644 doc/ci/update/4.1-to-4.2.md delete mode 100644 doc/ci/update/4.2-to-4.3.md delete mode 100644 doc/ci/update/4.3-to-5.0.md delete mode 100644 doc/ci/update/5.0-to-5.1.md delete mode 100644 doc/ci/update/5.1-to-5.2.md delete mode 100644 doc/ci/update/5.2-to-5.3.md delete mode 100644 doc/ci/update/5.3-to-5.4.md delete mode 100644 doc/ci/update/5.4-to-7.8.md delete mode 100644 doc/ci/update/7.10-to-7.11.md delete mode 100644 doc/ci/update/7.11-to-7.12.md delete mode 100644 doc/ci/update/7.12-to-7.13.md delete mode 100644 doc/ci/update/7.8-to-7.9.md delete mode 100644 doc/ci/update/7.9-to-7.10.md delete mode 100644 doc/ci/update/README.md delete mode 100644 doc/ci/update/patch_versions.md diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 9eb99dae456..b2bd8796004 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -110,7 +110,23 @@ production: &base # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon # - # 2. Auth settings + # 2. GitLab CI settings + # ========================== + + gitlab_ci: + # Default project notifications settings: + # + # Send emails only on broken builds (default: true) + # all_broken_builds: true + # + # Add pusher to recipients list (default: false) + # add_pusher: true + + # The location where build traces are stored (default: builds/). Relative paths are relative to Rails.root + # builds_path: builds/ + + # + # 3. Auth settings # ========================== ## LDAP settings @@ -256,7 +272,7 @@ production: &base # - # 3. Advanced settings + # 4. Advanced settings # ========================== # GitLab Satellites @@ -315,7 +331,7 @@ production: &base timeout: 10 # - # 4. Extra customization + # 5. Extra customization # ========================== extra: diff --git a/doc/README.md b/doc/README.md index 337c4e6a62d..632ddefeb2c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -15,6 +15,23 @@ - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. +## CI Documentation + ++ [Quick Start](ci/quick_start/README.md) ++ [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) ++ [Configuring runner](ci/runners/README.md) ++ [Configuring deployment](ci/deployment/README.md) ++ [Using Docker Images](ci/docker/using_docker_images.md) ++ [Using Docker Build](ci/docker/using_docker_build.md) ++ [Using Variables](ci/variables/README.md) + +### CI Examples + ++ [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) ++ [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md) ++ [Test Clojure applications](ci/examples/test-clojure-application.md) ++ Help your favorite programming language and GitLab by sending a merge request with a guide for that language. + ## Administrator documentation - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough. @@ -31,6 +48,11 @@ - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Reply by email](reply_by_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. +### Administrator documentation + ++ [User permissions](permissions/README.md) ++ [API](api/README.md) + ## Contributor documentation - [Development](development/README.md) Explains the architecture and the guidelines for shell commands. diff --git a/doc/ci/README.md b/doc/ci/README.md index e3534c6991f..97325069ceb 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -19,9 +19,5 @@ ### Administrator documentation -+ [Install](install/README.md) -+ [Update](update/README.md) + [User permissions](permissions/README.md) -+ [Backup/Restore](raketasks/backup_restore.md) -+ [Migrating to packaged CI](migration_to_omnibus/README.md) + [API](api/README.md) diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index 95fe2f837a5..e47e5c46732 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -29,12 +29,12 @@ sending the `private-token` of a valid user and the `url` of an authorized Gitlab instance via a query string along with the API request: - GET http://ci.example.com/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/ + GET http://gitlab.example.com/ci/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/ If preferred, you may instead send the `private-token` as a header in your request: - curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://ci.example.com/api/v1/projects?url=http://demo.gitlab.com/" + curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://gitlab.example.com/ci/api/v1/projects?url=http://demo.gitlab.com/" ### Authentication #2: GitLab CI project token diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md index 54749bc6fa1..3b5008ccdb4 100644 --- a/doc/ci/api/builds.md +++ b/doc/ci/api/builds.md @@ -8,7 +8,7 @@ __Authentication is done by runner token__ ### Runs oldest pending build by runner - POST /builds/register + POST /ci/builds/register Parameters: @@ -32,7 +32,7 @@ Returns: ### Update details of an existing build - PUT /builds/:id + PUT /ci/builds/:id Parameters: diff --git a/doc/ci/api/commits.md b/doc/ci/api/commits.md index 0015a62a38f..4df7afc6c52 100644 --- a/doc/ci/api/commits.md +++ b/doc/ci/api/commits.md @@ -8,7 +8,7 @@ __Authentication is done by GitLab CI project token__ Get list of commits per project - GET /commits + GET /ci/commits Parameters: @@ -58,7 +58,7 @@ Inform GitLab CI about new commit you want it to build. __If commit already exists in GitLab CI it will not be created__ - POST /commits + POST /ci/commits Parameters: diff --git a/doc/ci/api/forks.md b/doc/ci/api/forks.md index 1a5ea8041d8..8f32e2d3b40 100644 --- a/doc/ci/api/forks.md +++ b/doc/ci/api/forks.md @@ -12,7 +12,7 @@ __Authentication is done by GitLab user token & GitLab project token__ ``` -POST /forks +POST /ci/forks ``` Parameters: diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md index c24d48f829f..54584db0938 100644 --- a/doc/ci/api/projects.md +++ b/doc/ci/api/projects.md @@ -12,7 +12,7 @@ __Authentication is done by GitLab user token & GitLab url__ Lists all projects that the authenticated user has access to. ``` -GET /projects +GET /ci/projects ``` Returns: @@ -55,7 +55,7 @@ Returns: Lists all projects that the authenticated user owns. ``` -GET /projects/owned +GET /ci/projects/owned ``` Returns: @@ -84,7 +84,7 @@ Returns: Returns information about a single project for which the user is authorized. - GET /projects/:id + GET /ci/projects/:id Parameters: @@ -94,7 +94,7 @@ Parameters: Creates a Gitlab CI project using Gitlab project details. - POST /projects + POST /ci/projects Parameters: @@ -109,7 +109,7 @@ Parameters: Updates a Gitlab CI project using Gitlab project details that the authenticated user has access to. - PUT /projects/:id + PUT /ci/projects/:id Parameters: @@ -123,7 +123,7 @@ Parameters: Removes a Gitlab CI project that the authenticated user has access to. - DELETE /projects/:id + DELETE /ci/projects/:id Parameters: @@ -134,7 +134,7 @@ Parameters: Links a runner to a project so that it can make builds (only via authorized user). - POST /projects/:id/runners/:runner_id + POST /ci/projects/:id/runners/:runner_id Parameters: @@ -146,7 +146,7 @@ Parameters: Removes a runner from a project so that it can not make builds (only via authorized user). - DELETE /projects/:id/runners/:runner_id + DELETE /ci/projects/:id/runners/:runner_id Parameters: diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md index 68b5851617a..e9f88ee066e 100644 --- a/doc/ci/api/runners.md +++ b/doc/ci/api/runners.md @@ -9,7 +9,7 @@ __Authentication is done by GitLab user token & GitLab url__ Used to get information about all runners registered on the Gitlab CI instance. - GET /runners + GET /ci/runners Returns: @@ -33,7 +33,7 @@ __Authentication is done with a Shared runner registration token or a project Sp Used to make Gitlab CI aware of available runners. - POST /runners/register + POST /ci/runners/register Parameters: @@ -58,7 +58,7 @@ __Authentication is done by runner token__ Used to removing runners. - DELETE /runners/delete + DELETE /ci/runners/delete Parameters: diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 702a6c6b587..a698fbc8184 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -35,7 +35,7 @@ GitLab Runner then executes build scripts as `gitlab-runner` user. ```bash $ sudo gitlab-runner register -n \ - --url http://ci.gitlab.com \ + --url http://gitlab.com/ci \ --token RUNNER_TOKEN \ --executor shell --description "My Runner" @@ -84,7 +84,7 @@ In order to do that follow the steps: ```bash $ sudo gitlab-runner register -n \ - --url http://ci.gitlab.com \ + --url http://gitlab.com/ci \ --token RUNNER_TOKEN \ --executor docker \ --description "My Docker Runner" \ diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index ef449cd45bc..191e3a8144d 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -14,7 +14,7 @@ To use GitLab Runner with Docker you need to register new runner to use `docker` ```bash gitlab-ci-multi-runner register \ - --url "https://ci.gitlab.com/" \ + --url "https://gitlab.com/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "docker-ruby-2.1" \ --executor "docker" \ diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md index 859adf5f465..036b03dd6b9 100644 --- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -55,11 +55,11 @@ You can do this through the [Dashboard](https://dashboard.heroku.com/). ### Create runner First install [Docker Engine](https://docs.docker.com/installation/). To build this project you also need to have [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner). -You can use public runners available on `ci.gitlab.com`, but you can register your own: +You can use public runners available on `gitlab.com/ci`, but you can register your own: ``` gitlab-ci-multi-runner register \ --non-interactive \ - --url "https://ci.gitlab.com/" \ + --url "https://gitlab.com/ci/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "python-3.2" \ --executor "docker" \ diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md index a1265ae8833..d2a872f1934 100644 --- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md @@ -50,11 +50,11 @@ You can do this through the [Dashboard](https://dashboard.heroku.com/). ### Create runner First install [Docker Engine](https://docs.docker.com/installation/). To build this project you also need to have [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner). -You can use public runners available on `ci.gitlab.com`, but you can register your own: +You can use public runners available on `gitlab.com/ci`, but you can register your own: ``` gitlab-ci-multi-runner register \ --non-interactive \ - --url "https://ci.gitlab.com/" \ + --url "https://gitlab.com/ci/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "ruby-2.1" \ --executor "docker" \ diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md index 6c6faf8f928..eaee94a10f1 100644 --- a/doc/ci/examples/test-clojure-application.md +++ b/doc/ci/examples/test-clojure-application.md @@ -32,4 +32,4 @@ In before script we install JRE and [Leiningen](http://leiningen.org/). Sample project uses [migratus](https://github.com/yogthos/migratus) library to manage database migrations. So we added database migration as last step of `before_script` section -You can use public runners available on `ci.gitlab.com` for testing your application with such configuration. +You can use public runners available on `gitlab.com` for testing your application with such configuration. diff --git a/doc/ci/install/README.md b/doc/ci/install/README.md deleted file mode 100644 index 8cbc858458c..00000000000 --- a/doc/ci/install/README.md +++ /dev/null @@ -1,276 +0,0 @@ -# Select Version to Install -Make sure you view this installation guide from the branch (version) of GitLab CI you would like to install. In most cases -this should be the highest numbered stable branch (example shown below). - -![capture](http://i.imgur.com/fmdlXxa.png) - -If this is unclear check the [GitLab Blog](http://blog.gitlab.org/) for installation guide links by version. - -## GitLab CI 7.12 requires GitLab 7.12 or newer - -other [requirements](requirements.md) - -# Setup: - -## 1. Packages / Dependencies - -`sudo` is not installed on Debian by default. Make sure your system is -up-to-date and install it. - - sudo apt-get update - sudo apt-get upgrade - -**Note:** -During this installation some files will need to be edited manually. If -you are familiar with vim set it as default editor with the commands -below. If you are not familiar with vim please skip this and keep using -the default editor. - - # Install vim - sudo apt-get install vim - sudo update-alternatives --set editor /usr/bin/vim.basic - -Install the required packages: - - sudo apt-get install wget curl gcc checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libreadline6-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev openssh-server git-core libyaml-dev postfix libpq-dev libicu-dev openssl nodejs - sudo apt-get install redis-server - -# 2. Ruby - -Download Ruby and compile it: - - mkdir /tmp/ruby && cd /tmp/ruby - curl --progress http://cache.ruby-lang.org/pub/ruby/ruby-2.1.6.tar.bz2 | tar xj - cd ruby-2.1.6/ - ./configure --disable-install-rdoc - make - sudo make install - -Install the Bundler Gem: - - sudo gem install bundler --no-ri --no-rdoc - - -## 3. GitLab CI user: - - sudo adduser --disabled-login --gecos 'GitLab CI' gitlab_ci - - -## 4. Prepare the database - -We recommend PostgreSQL but you can also use MySQL - -### MySQL - - # Install the database packages - sudo apt-get install mysql-server mysql-client libmysqlclient-dev - - # Login to MySQL - $ mysql -u root -p - - # Create the GitLab CI database - mysql> CREATE DATABASE IF NOT EXISTS `gitlab_ci_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; - - # Create the MySQL User change $password to a real password - mysql> CREATE USER 'gitlab_ci'@'localhost' IDENTIFIED BY '$password'; - - # Grant proper permissions to the MySQL User - mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlab_ci_production`.* TO 'gitlab_ci'@'localhost'; - - # Logout MYSQL - mysql> exit; - -### PostgreSQL - - # Install the database packages - sudo apt-get install -y postgresql-9.1 libpq-dev - - # Login to PostgreSQL - sudo -u postgres psql -d template1 - - # Create a user for GitLab CI. We do not specify a password because we are using peer authentication. - template1=# CREATE USER gitlab_ci; - - # Create the GitLab CI production database & grant all privileges on database - template1=# CREATE DATABASE gitlab_ci_production OWNER gitlab_ci; - - # Quit the database session - template1=# \q - - # Try connecting to the new database with the new user - sudo -u gitlab_ci -H psql -d gitlab_ci_production - -## 5. Get code - - cd /home/gitlab_ci/ - - sudo -u gitlab_ci -H git clone https://gitlab.com/gitlab-org/gitlab-ci.git - - cd gitlab-ci - - sudo -u gitlab_ci -H git checkout 7-12-stable - -## 6. Setup application - - # Edit application settings - # Production - sudo -u gitlab_ci -H cp config/application.yml.example config/application.yml - sudo -u gitlab_ci -H editor config/application.yml - # Development - #sudo -u gitlab_ci -H cp config/application.yml.example.development config/application.yml - - # Copy the example secrets file - sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml - sudo -u gitlab_ci -H chmod 0600 config/secrets.yml - - # Edit web server settings - sudo -u gitlab_ci -H cp config/unicorn.rb.example config/unicorn.rb - sudo -u gitlab_ci -H editor config/unicorn.rb - - # Create socket and pid directories - sudo -u gitlab_ci -H mkdir -p tmp/sockets/ - sudo chmod -R u+rwX tmp/sockets/ - sudo -u gitlab_ci -H mkdir -p tmp/pids/ - sudo chmod -R u+rwX tmp/pids/ - - # Change the permissions of the directory where build traces are stored - sudo chmod -R u+rwX builds/ - -### Install gems - - # For MySQL (note, the option says "without ... postgres") - sudo -u gitlab_ci -H bundle install --without development test postgres --deployment - - # Or for PostgreSQL (note, the option says "without ... mysql") - sudo -u gitlab_ci -H bundle install --without development test mysql --deployment - -### Setup db - - # mysql - sudo -u gitlab_ci -H cp config/database.yml.mysql config/database.yml - - # postgres - sudo -u gitlab_ci -H cp config/database.yml.postgresql config/database.yml - - # Edit user/password (not necessary with default Postgres setup) - sudo -u gitlab_ci -H editor config/database.yml - - # Setup tables - sudo -u gitlab_ci -H bundle exec rake setup RAILS_ENV=production - - # Setup schedules - sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production - -### Secure secrets.yml - -The `secrets.yml` file stores encryption keys for sessions and secure variables. -Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups. -Otherwise your secrets are exposed if one of your backups is compromised. - -## 8. Install Init Script - -Copy the init script (will be /etc/init.d/gitlab_ci): - - sudo cp /home/gitlab_ci/gitlab-ci/lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci - -Make GitLab CI start on boot: - - sudo update-rc.d gitlab_ci defaults 21 - - -Start your GitLab CI instance: - - sudo service gitlab_ci start - # or - sudo /etc/init.d/gitlab_ci start - - -# 8. Nginx - - -## Installation - - sudo apt-get install nginx - -## Site Configuration - -Download an example site config: - - sudo cp /home/gitlab_ci/gitlab-ci/lib/support/nginx/gitlab_ci /etc/nginx/sites-available/gitlab_ci - sudo ln -s /etc/nginx/sites-available/gitlab_ci /etc/nginx/sites-enabled/gitlab_ci - -Make sure to edit the config file to match your setup: - - # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** - # to the IP address and fully-qualified domain name - # of your host serving GitLab CI - sudo editor /etc/nginx/sites-enabled/gitlab_ci - -## Check your configuration - - sudo nginx -t - -## Start nginx - - sudo /etc/init.d/nginx start - -# 9. GitLab OAuth2 application - - -Go to the admin area of GitLab, to the `Application` section. Create an application for the GitLab CI -For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. - -When `app_id` and `app_secret` are generated add them to the GitLab CI config: - -``` -production: - gitlab_server: - url: 'http://gitlab.example.com' - app_id: XXXXXX - app_secret: XXXXXX - -``` - - -# 10. Runners - - -Now you need Runners to process your builds. -Checkout the [Gitlab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it - -# Done! - - -Visit YOUR_SERVER for your first GitLab CI login. -You will be asked to authorize with your GitLab credentials. - -**Enjoy!** - -## Advanced settings - -### SMTP email settings - -If you want to use SMTP do next: - - # Copy config file - sudo -u gitlab_ci -H cp config/initializers/smtp_settings.rb.sample config/initializers/smtp_settings.rb - - # Edit it with your settings - sudo -u gitlab_ci -H editor config/initializers/smtp_settings.rb - -Restart application - -### Custom Redis Connection - -If you'd like Resque to connect to a Redis server on a non-standard port or on -a different host, you can configure its connection string via the -`config/resque.yml` file. - - # example - production: redis://redis.example.tld:6379 - -If you want to connect the Redis server via socket, then use the "unix:" URL scheme -and the path to the Redis socket file in the `config/resque.yml` file. - - # example - production: unix:/path/to/redis/socket diff --git a/doc/ci/install/requirements.md b/doc/ci/install/requirements.md deleted file mode 100644 index 6c12607c3f8..00000000000 --- a/doc/ci/install/requirements.md +++ /dev/null @@ -1,61 +0,0 @@ -# Requirements - -## Operating Systems - -### Supported Unix distributions - -- Ubuntu -- Debian -- CentOS -- Red Hat Enterprise Linux (please use the CentOS packages and instructions) -- Scientific Linux (please use the CentOS packages and instructions) -- Oracle Linux (please use the CentOS packages and instructions) - -For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). - -### Unsupported Unix distributions - -- OS X -- Arch Linux -- Fedora -- Gentoo -- FreeBSD - -### Non-Unix operating systems such as Windows - -GitLab CI is developed for Unix operating systems. -GitLab CI does **not** run on Windows and we have no plans of supporting it in the near future. -Please consider using a virtual machine to run GitLab CI. - -## Ruby versions - -GitLab requires Ruby (MRI) 2.0 or 2.1 -You will have to use the standard MRI implementation of Ruby. -We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab CI needs several Gems that have native extensions. - - -### Memory - -You need at least 1GB of addressable memory (RAM + swap) to install and use GitLab CI! - -## Unicorn Workers - -It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. - -For most instances we recommend using: CPU cores + 1 = unicorn workers. -So for a machine with 2 cores, 3 unicorn workers is ideal. - -For all machines that have 1GB and up we recommend a minimum of three unicorn workers. -If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. -With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). -If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. - -To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). - -## Supported web browsers - -- Chrome (Latest stable version) -- Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) -- Safari 7+ (known problem: required fields in html5 do not work) -- Opera (Latest released version) -- IE 10+ diff --git a/doc/ci/migration_to_omnibus/README.md b/doc/ci/migration_to_omnibus/README.md deleted file mode 100644 index ae46f59a7bb..00000000000 --- a/doc/ci/migration_to_omnibus/README.md +++ /dev/null @@ -1,29 +0,0 @@ -## Migrating to packaged CI - -Since version 5.1 GitLab CI is shipping as part of the GitLab omnibus package. This guide describes how to migrate GitLab CI from a source installation to an Omnibus package. - -### 1. Update GitLab - -Update GitLab CI manually to the version that you will install using the omnibus package (at least 7.11). Follow the update [manual for installation from sourse](update/README.md) - -### 2. Backup - -``` -sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production -``` - -This command will create a backup file in the tmp folder -(`/home/gitlab_ci/gitlab_ci/tmp/backups/*_gitlab_ci_backup.tar.gz`). You can read more in the [GitLab CI backup/restore documentation](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/raketasks/backup_restore.md) - -### 2. Install a packaged GitLab CI - -This process is described in the [instruction for enabling GitLab CI](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/gitlab-ci/README.md) - -### 4. Restore backup - -Put backup file to directory `/var/opt/gitlab/backups`. -Run the restore command: - -``` -sudo gitlab-ci-rake backup:restore -``` diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 3b9156f7409..a87a1f806fc 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,7 +16,7 @@ Push your application to that repository. ## 3. Add project to CI The next part is to login to GitLab CI. -Point your browser to the URL you have set GitLab CI or use [ci.gitlab.com](http://ci.gitlab.com/) that is linked to [GitLab.com](http://GitLab.com/). +Point your browser to the URL you have set GitLab or use [gitlab.com/ci](http://gitlab.com/ci/). On the first screen you will see a list of GitLab's projects that you have access to: @@ -88,7 +88,7 @@ More information about different runner types can be found in [Configuring runne To check if you have runners assigned to your project go to **Runners**. You will find there information how to setup project specific runner: 1. Install GitLab Runner software. Checkout the [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner) section to install it. -1. Specify following URL during runner setup: https://ci.gitlab.com/ +1. Specify following URL during runner setup: https://gitlab.com/ci/ 1. Use the following registration token during setup: TOKEN If you do it correctly your runner should be shown under **Runners activated for this project**: @@ -97,7 +97,7 @@ If you do it correctly your runner should be shown under **Runners activated for ### Shared runners -If you use [ci.gitlab.com](http://ci.gitlab.com/) you can use **Shared runners** provided by GitLab Inc. +If you use [gitlab.com/ci](http://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc. These are special virtual machines that are run on GitLab's infrastructure that can build any project. To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project. diff --git a/doc/ci/raketasks/README.md b/doc/ci/raketasks/README.md deleted file mode 100644 index 872be4dc966..00000000000 --- a/doc/ci/raketasks/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Rake Tasks - -+ [Backup/Restore](backup_restore.md) \ No newline at end of file diff --git a/doc/ci/raketasks/backup_restore.md b/doc/ci/raketasks/backup_restore.md deleted file mode 100644 index eed12c46247..00000000000 --- a/doc/ci/raketasks/backup_restore.md +++ /dev/null @@ -1,237 +0,0 @@ -# Backup restore - -## Create a backup of the GitLab CI - -A backup creates an archive file that contains the database and builds files. -This archive will be saved in backup_path (see `config/application.yml`). -The filename will be `[TIMESTAMP]_gitlab_ci_backup.tar.gz`. This timestamp can be used to restore an specific backup. -You can only restore a backup to exactly the same version of GitLab CI that you created it on, for example 7.10.1. - -*If you are interested in the GitLab backup please follow to the [GitLab backup documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/backup_restore.md)* - -``` -# use this command if you've installed GitLab CI with the Omnibus package -sudo gitlab-ci-rake backup:create - -# if you've installed GitLab from source -sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production -``` - - -Example output: - -``` -Dumping database ... -Dumping PostgreSQL database gitlab_ci_development ... [DONE] -done -Dumping builds ... -done -Creating backup archive: 1430930060_gitlab_ci_backup.tar.gz ... done -Uploading backup archive to remote storage ... skipped -Deleting tmp directories ... done -done -Deleting old backups ... skipping -``` - -## Upload backups to remote (cloud) storage - -You can let the backup script upload the '.tar.gz' file it creates. -It uses the [Fog library](http://fog.io/) to perform the upload. -In the example below we use Amazon S3 for storage. -But Fog also lets you use [other storage providers](http://fog.io/storage/). - -For omnibus packages: - -```ruby -gitlab_ci['backup_upload_connection'] = { - 'provider' => 'AWS', - 'region' => 'eu-west-1', - 'aws_access_key_id' => 'AKIAKIAKI', - 'aws_secret_access_key' => 'secret123' -} -gitlab_ci['backup_upload_remote_directory'] = 'my.s3.bucket' -gitlab_ci['backup_multipart_chunk_size'] = 104857600 -``` - -For installations from source: - -```yaml - backup: - # snip - upload: - # Fog storage connection settings, see http://fog.io/storage/ . - connection: - provider: AWS - region: eu-west-1 - aws_access_key_id: AKIAKIAKI - aws_secret_access_key: 'secret123' - # The remote 'directory' to store your backups. For S3, this would be the bucket name. - remote_directory: 'my.s3.bucket' - multipart_chunk_size: 104857600 -``` - -If you are uploading your backups to S3 you will probably want to create a new -IAM user with restricted access rights. To give the upload user access only for -uploading backups create the following IAM profile, replacing `my.s3.bucket` -with the name of your bucket: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Stmt1412062044000", - "Effect": "Allow", - "Action": [ - "s3:AbortMultipartUpload", - "s3:GetBucketAcl", - "s3:GetBucketLocation", - "s3:GetObject", - "s3:GetObjectAcl", - "s3:ListBucketMultipartUploads", - "s3:PutObject", - "s3:PutObjectAcl" - ], - "Resource": [ - "arn:aws:s3:::my.s3.bucket/*" - ] - }, - { - "Sid": "Stmt1412062097000", - "Effect": "Allow", - "Action": [ - "s3:GetBucketLocation", - "s3:ListAllMyBuckets" - ], - "Resource": [ - "*" - ] - }, - { - "Sid": "Stmt1412062128000", - "Effect": "Allow", - "Action": [ - "s3:ListBucket" - ], - "Resource": [ - "arn:aws:s3:::my.s3.bucket" - ] - } - ] -} -``` - -## Storing configuration files - -Please be informed that a backup does not store your configuration and secret files. -If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration). -If you have a cookbook installation there should be a copy of your configuration in Chef. -If you have an installation from source: -1. please backup `config/secrets.yml` file that contains key to encrypt variables in database, -but don't store it in the same place as your database backups. -Otherwise your secrets are exposed in case one of your backups is compromised. -1. please consider backing up your `application.yml` file, -1. any SSL keys and certificates, -1. and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). - -## Restore a previously created backup - -You can only restore a backup to exactly the same version of GitLab CI that you created it on, for example 7.10.1. - -### Installation from source - -``` -sudo -u gitlab_ci -H bundle exec rake backup:restore RAILS_ENV=production -``` - -Options - -``` -BACKUP=timestamp_of_backup (required if more than one backup exists) -``` - -### Omnibus package installation - -We will assume that you have installed GitLab CI from an omnibus package and run -`sudo gitlab-ctl reconfigure` at least once. - -First make sure your backup tar file is in `/var/opt/gitlab/backups`. - -```shell -sudo cp 1393513186_gitlab_ci_backup.tar.gz /var/opt/gitlab/backups/ -``` - -Next, restore the backup by running the restore command. You need to specify the -timestamp of the backup you are restoring. - -```shell -# Stop processes that are connected to the database -sudo gitlab-ctl stop ci-unicorn -sudo gitlab-ctl stop ci-sidekiq - -# This command will overwrite the contents of your GitLab CI database! -sudo gitlab-ci-rake backup:restore BACKUP=1393513186 - -# Start GitLab -sudo gitlab-ctl start -``` - -If there is a GitLab version mismatch between your backup tar file and the installed -version of GitLab, the restore command will abort with an error. Install a package for -the [required version](https://www.gitlab.com/downloads/archives/) and try again. - - - -## Configure cron to make daily backups - -### For installation from source: -``` -cd /home/git/gitlab -sudo -u gitlab_ci -H editor config/application.yml # Enable keep_time in the backup section to automatically delete old backups -sudo -u gitlab_ci crontab -e # Edit the crontab for the git user -``` - -Add the following lines at the bottom: - -``` -# Create a backup of the GitLab CI every day at 4am -0 4 * * * cd /home/gitlab_ci/gitlab_ci && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake backup:create RAILS_ENV=production CRON=1 -``` - -The `CRON=1` environment setting tells the backup script to suppress all progress output if there are no errors. -This is recommended to reduce cron spam. - -### Omnibus package installation - -To schedule a cron job that backs up your GitLab CI, use the root user: - -``` -sudo su - -crontab -e -``` - -There, add the following line to schedule the backup for everyday at 2 AM: - -``` -0 2 * * * /opt/gitlab/bin/gitlab-ci-rake backup:create CRON=1 -``` - -You may also want to set a limited lifetime for backups to prevent regular -backups using all your disk space. To do this add the following lines to -`/etc/gitlab/gitlab.rb` and reconfigure: - -``` -# limit backup lifetime to 7 days - 604800 seconds -gitlab_ci['backup_keep_time'] = 604800 -``` - -NOTE: This cron job does not [backup your omnibus-gitlab configuration](#backup-and-restore-omnibus-gitlab-configuration). - -## Known issues - -If you’ve been using GitLab CI since 7.11 or before using MySQL and the official installation guide, you will probably get the following error while making a backup: `Dumping MySQL database gitlab_ci_production ... mysqldump: Got error: 1044: Access denied for user 'gitlab_ci'@'localhost' to database 'gitlab_ci_production' when using LOCK TABLES` .This can be resolved by adding a LOCK TABLES permission to the gitlab_ci MySQL user. Add this permission with: -``` -$ mysql -u root -p -mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlab_ci_production`.* TO 'gitlab_ci'@'localhost'; -``` - diff --git a/doc/ci/update/3.0-to-3.1.md b/doc/ci/update/3.0-to-3.1.md deleted file mode 100644 index 039e781a6c8..00000000000 --- a/doc/ci/update/3.0-to-3.1.md +++ /dev/null @@ -1,30 +0,0 @@ -# Update from 3.0 to 3.1 - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 3-1-stable -``` - -### 4. Install libs, migrations etc - -``` -bundle install --without development test --deployment -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/3.1-to-3.2.md b/doc/ci/update/3.1-to-3.2.md deleted file mode 100644 index dc6ab6514c6..00000000000 --- a/doc/ci/update/3.1-to-3.2.md +++ /dev/null @@ -1,30 +0,0 @@ -# Update from 3.1 to 3.2 - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 3-2-stable -``` - -### 4. Install libs, migrations etc - -``` -bundle install --without development test --deployment -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/3.2-to-4.0.md b/doc/ci/update/3.2-to-4.0.md deleted file mode 100644 index 50c26e49224..00000000000 --- a/doc/ci/update/3.2-to-4.0.md +++ /dev/null @@ -1,35 +0,0 @@ -# Update from 3.2 to 4.0 - -## GitLab CI 4.0 requires GitLab 6.3 or higher - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 4-0-stable -``` - -### 4. Install libs, migrations etc - -``` -bundle install --without development test --deployment -bundle exec rake db:migrate RAILS_ENV=production -bundle exec whenever -w -``` - -### 5. Start web application - - sudo service gitlab_ci start - -### 6. Update your runners to version 4.0 diff --git a/doc/ci/update/4.0-to-4.1.md b/doc/ci/update/4.0-to-4.1.md deleted file mode 100644 index e749b324ee7..00000000000 --- a/doc/ci/update/4.0-to-4.1.md +++ /dev/null @@ -1,49 +0,0 @@ -# Update from 4.0 to 4.1 - -## GitLab CI 4.x requires GitLab 6.3 or higher - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 4-1-stable -``` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production - -# Update cron -bundle exec whenever -w -``` - -### 5. Start web application - - sudo service gitlab_ci start - -### 6. Make sure your runners are version 4.0 - - - cd path_to_runner - cat VERSION - -To update runners follow this instructions https://github.com/gitlabhq/gitlab-ci-runner#update diff --git a/doc/ci/update/4.1-to-4.2.md b/doc/ci/update/4.1-to-4.2.md deleted file mode 100644 index 81394aa216e..00000000000 --- a/doc/ci/update/4.1-to-4.2.md +++ /dev/null @@ -1,47 +0,0 @@ -# Update from 4.1 to 4.2 - -## GitLab CI 4.x requires GitLab 6.3 or higher - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 4-2-stable -``` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Install the new init script -As a user with sudo rights: - -``` -cd /home/gitlab_ci/gitlab-ci -sudo cp lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci -sudo chmod +x /etc/init.d/gitlab_ci -``` - -### 6. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/4.2-to-4.3.md b/doc/ci/update/4.2-to-4.3.md deleted file mode 100644 index f340cb61411..00000000000 --- a/doc/ci/update/4.2-to-4.3.md +++ /dev/null @@ -1,61 +0,0 @@ -# Update from 4.2 to 4.3 - -## GitLab CI 4.x requires GitLab 6.3 or higher - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 4-3-stable -``` - -### 4. Install libs, migrations etc - -Edit web server settings - -``` -cp config/unicorn.rb.example config/unicorn.rb -editor config/unicorn.rb -``` - -Then: - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Setup unicorn -``` -cp config/unicorn.rb.example config/unicorn.rb -``` - -### 6. Install the new init script -As a user with sudo rights: - -``` -cd /home/gitlab_ci/gitlab-ci -sudo cp lib/support/init.d/gitlab_ci /etc/init.d/gitlab_ci -sudo chmod +x /etc/init.d/gitlab_ci -``` - -### 7. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/4.3-to-5.0.md b/doc/ci/update/4.3-to-5.0.md deleted file mode 100644 index 23327eac608..00000000000 --- a/doc/ci/update/4.3-to-5.0.md +++ /dev/null @@ -1,42 +0,0 @@ -# Update from 4.3 to 5.0 - -__GitLab CI 5.0 requires GitLab 6.3 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. get latest code - -``` -git fetch -git checkout 5-0-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.0-to-5.1.md b/doc/ci/update/5.0-to-5.1.md deleted file mode 100644 index 9a37a87a882..00000000000 --- a/doc/ci/update/5.0-to-5.1.md +++ /dev/null @@ -1,42 +0,0 @@ -# Update from 5.0 to 5.1 - -__GitLab CI 5.1 requires GitLab 6.3 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 5-1-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.1-to-5.2.md b/doc/ci/update/5.1-to-5.2.md deleted file mode 100644 index 6dcf00276f6..00000000000 --- a/doc/ci/update/5.1-to-5.2.md +++ /dev/null @@ -1,42 +0,0 @@ -# Update from 5.1 to 5.2 - -__GitLab CI 5.2 requires GitLab 7.5 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 5-2-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.2-to-5.3.md b/doc/ci/update/5.2-to-5.3.md deleted file mode 100644 index 2b70f2146ac..00000000000 --- a/doc/ci/update/5.2-to-5.3.md +++ /dev/null @@ -1,42 +0,0 @@ -# Update from 5.2 to 5.3 - -__GitLab CI 5.3 requires GitLab 7.5 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 5-3-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start \ No newline at end of file diff --git a/doc/ci/update/5.3-to-5.4.md b/doc/ci/update/5.3-to-5.4.md deleted file mode 100644 index 6d1cd61389a..00000000000 --- a/doc/ci/update/5.3-to-5.4.md +++ /dev/null @@ -1,60 +0,0 @@ -# Update from 5.3 to 5.4 - -__GitLab CI 5.4 requires GitLab 7.5 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 5-4-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Update config -GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), -you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. - -For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. - -You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: - -``` -production: - gitlab_server: - url: 'http://gitlab.example.com' - app_id: XXXXXX - app_secret: XXXXXX - -``` - - -### 6. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/5.4-to-7.8.md b/doc/ci/update/5.4-to-7.8.md deleted file mode 100644 index 71561d047e6..00000000000 --- a/doc/ci/update/5.4-to-7.8.md +++ /dev/null @@ -1,65 +0,0 @@ -# Update from 5.3 to 7.8 - -## Notice - -With this release we are bumping the GitLab CI version to 7.8 in order to be on par with the current GitLab version and -to avoid naming confusion. - -__GitLab CI 7.8 requires GitLab 7.8 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 7-8-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - -``` -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Update config -GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), -you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. - -For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. - -You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: - -``` -production: - gitlab_server: - url: 'http://gitlab.example.com' - app_id: XXXXXX - app_secret: XXXXXX - -``` - - -### 6. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/7.10-to-7.11.md b/doc/ci/update/7.10-to-7.11.md deleted file mode 100644 index 4e04509e84d..00000000000 --- a/doc/ci/update/7.10-to-7.11.md +++ /dev/null @@ -1,45 +0,0 @@ -# Update from 7.10 to 7.11 - -## Notice - -__GitLab CI 7.11 requires GitLab 7.11 or higher and GitLab Multi Runner 0.3.0 and higher - -### 1. Stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 7-11-stable -``` - -### 4. Install libs, migrations etc - - -``` -# Install nodejs dependency: -sudo apt-get install nodejs - -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - - -### 5. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/7.11-to-7.12.md b/doc/ci/update/7.11-to-7.12.md deleted file mode 100644 index ad45ea3a660..00000000000 --- a/doc/ci/update/7.11-to-7.12.md +++ /dev/null @@ -1,67 +0,0 @@ -# Update from 7.11 to 7.12 - -## Notice - -__GitLab CI 7.12 requires GitLab 7.12 or higher and GitLab Multi Runner 0.4.0 or higher - -### 1. Stop CI server - - sudo service gitlab_ci stop - -### 2. Update ruby if needed - -If your ruby version is older than 2.0.0 please update it. - -Update packages: - - sudo apt-get update - sudo apt-get upgrade - -Download Ruby and compile it: - - mkdir /tmp/ruby && cd /tmp/ruby - curl --progress http://cache.ruby-lang.org/pub/ruby/ruby-2.1.6.tar.bz2 | tar xj - cd ruby-2.1.6/ - ./configure --disable-install-rdoc - make - sudo make install - -Install the Bundler Gem: - - sudo gem install bundler --no-ri --no-rdoc - -### 3. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 4. Get latest code - -``` -git fetch -git checkout 7-12-stable -``` - -### 5. Install libs, migrations etc - - -``` -# Install nodejs dependency: -sudo apt-get install nodejs - -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - - -### 6. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/7.12-to-7.13.md b/doc/ci/update/7.12-to-7.13.md deleted file mode 100644 index 2877c297d6f..00000000000 --- a/doc/ci/update/7.12-to-7.13.md +++ /dev/null @@ -1,63 +0,0 @@ -# Update from 7.12 to 7.13 - -## Notice - -__GitLab CI 7.13 requires GitLab 7.12 or higher and GitLab Multi Runner 0.5.0 or higher - -### 1. Stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 7-13-stable -``` - -### 4. Make sure GitLab CI can write to the builds/ directory - -``` -sudo chmod -R u+rwX builds -``` - -### 4. Copy secrets - -The `secrets.yml` file is used to store keys to encrypt sessions and encrypt secure variables. -When you run migrations make sure to store it someplace safe. -Don't store it in the same place as your database backups, -otherwise your secrets are exposed if one of your backups is compromised. - -``` -sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml -sudo -u gitlab_ci -H chmod 0600 config/secrets.yml -``` - -### 5. Install libs, migrations etc - - -``` -# Install nodejs dependency: -sudo apt-get install nodejs - -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - - -### 5. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/7.8-to-7.9.md b/doc/ci/update/7.8-to-7.9.md deleted file mode 100644 index fc1cefe9ba5..00000000000 --- a/doc/ci/update/7.8-to-7.9.md +++ /dev/null @@ -1,66 +0,0 @@ -# Update from 7.8 to 7.9 - -## Notice - -__GitLab CI 7.9 requires GitLab 7.9 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 7-9-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - - -``` -# Install nodejs dependency: -sudo apt-get install nodejs - -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Update config -GitLab CI 5.4 and above make use of the OAuth2 protocol for authentication with GitLab. This means that after updating GitLab (CI), -you need to create an OAuth2 application in GitLab admin area, which gives you the APP_ID and APP_SECRET. - -For callback URL use: `http://ci.example.com/user_sessions/callback` if you use http, or `https://ci.example.com/user_sessions/callback` if you use https. - -You will have to add APP_ID and APP_SECRET to the GitLab CI config, as such: - -``` -production: - gitlab_server: - url: 'http://gitlab.example.com' - app_id: XXXXXX - app_secret: XXXXXX - -``` - - -### 6. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/7.9-to-7.10.md b/doc/ci/update/7.9-to-7.10.md deleted file mode 100644 index 2b54874daf4..00000000000 --- a/doc/ci/update/7.9-to-7.10.md +++ /dev/null @@ -1,49 +0,0 @@ -# Update from 7.9 to 7.10 - -## Notice - -__GitLab CI 7.10 requires GitLab 7.10 or higher and GitLab CI Runner v5__ - -### 1. stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git fetch -git checkout 7-10-stable -``` - -#### Redis config - -If you have `config/resque.yml` file - please update it with recent `config/resque.yml.example` - -### 4. Install libs, migrations etc - - -``` -# Install nodejs dependency: -sudo apt-get install nodejs - -# For MySQL users -bundle install --without postgres development test --deployment - -# For Postgres users -bundle install --without mysql development test --deployment - -# Run migrations -bundle exec rake db:migrate RAILS_ENV=production -``` - - -### 5. Start web application - - sudo service gitlab_ci start diff --git a/doc/ci/update/README.md b/doc/ci/update/README.md deleted file mode 100644 index 7a615ef7978..00000000000 --- a/doc/ci/update/README.md +++ /dev/null @@ -1,2 +0,0 @@ -+ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update) -+ [Patch versions](patch_versions.md) diff --git a/doc/ci/update/patch_versions.md b/doc/ci/update/patch_versions.md deleted file mode 100644 index c13f69c03c9..00000000000 --- a/doc/ci/update/patch_versions.md +++ /dev/null @@ -1,59 +0,0 @@ -# Universal update guide for patch versions. For example from 4.0.0 to 4.0.1, also see the [semantic versioning specification](http://semver.org/). - -### 1. Stop CI server - - sudo service gitlab_ci stop - -### 2. Switch to your gitlab_ci user - -``` -sudo su gitlab_ci -cd /home/gitlab_ci/gitlab-ci -``` - -### 3. Get latest code - -``` -git pull origin STABLE_BRANCH -``` - -### 4. Install libs, migrations etc - -``` -bundle install --without development test --deployment -bundle exec rake db:migrate RAILS_ENV=production -``` - -### 5. Start web application - - sudo service gitlab_ci start - - -# One line upgrade command - -You have read through the entire guide and probably already did all the steps one by one. - -Here is a one line command with all above steps for the next time you upgrade: - -``` - sudo service gitlab_ci stop && \ - cd /home/gitlab_ci/gitlab-ci && \ - sudo -u gitlab_ci -H git pull origin `git rev-parse --abbrev-ref HEAD` && \ - sudo -u gitlab_ci -H bundle install --without development test --deployment && \ - sudo -u gitlab_ci -H bundle exec rake db:migrate RAILS_ENV=production && \ - cd && \ - sudo service gitlab_ci start -``` - -Since when we start this `gitlab_ci` service, the document `db/schema.rb` is shown always as modified for git, you could even do like this, **if and only if**, you are sure you only have that modification: - -``` - sudo service gitlab_ci stop && \ - cd /home/gitlab_ci/gitlab-ci && \ - sudo -u gitlab_ci -H git checkout -f `git rev-parse --abbrev-ref HEAD` && \ - sudo -u gitlab_ci -H git pull origin `git rev-parse --abbrev-ref HEAD` && \ - sudo -u gitlab_ci -H bundle install --without development test --deployment && \ - sudo -u gitlab_ci -H bundle exec rake db:migrate RAILS_ENV=production && \ - cd && \ - sudo service gitlab_ci start -``` diff --git a/doc/install/installation.md b/doc/install/installation.md index ee13b0f2537..8936697b40e 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -221,6 +221,10 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Update GitLab config file, follow the directions at top of file sudo -u git -H editor config/gitlab.yml + + # Copy the example secrets file + sudo -u git -H cp config/secrets.yml.example config/secrets.yml + sudo -u git -H chmod 0600 config/secrets.yml # Make sure GitLab can write to the log/ and tmp/ directories sudo chown -R git log/ @@ -234,6 +238,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Make sure GitLab can write to the public/uploads/ directory sudo chmod -R u+rwX public/uploads + + # Change the permissions of the directory where CI build traces are stored + sudo chmod -R u+rwX builds/ # Copy the example Unicorn config sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb @@ -328,6 +335,17 @@ GitLab Shell is an SSH access and repository management software developed speci sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword +### Secure secrets.yml + +The `secrets.yml` file stores encryption keys for sessions and secure variables. +Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups. +Otherwise your secrets are exposed if one of your backups is compromised. + +### Install schedules + + # Setup schedules + sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production + ### Install Init Script Download the init script (will be `/etc/init.d/gitlab`): @@ -491,3 +509,8 @@ You can configure LDAP authentication in `config/gitlab.yml`. Please restart Git ### Using Custom Omniauth Providers See the [omniauth integration document](../integration/omniauth.md) + +### Build your projects + +GitLab can build your projects. To enable that feature you need GitLab Runners to do that for you. +Checkout the [Gitlab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it diff --git a/doc/release/monthly.md b/doc/release/monthly.md index c1ed9e3b80e..c56e99a7005 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -195,7 +195,7 @@ This can happen before tagging because Omnibus uses tags in its own repo and SHA ## Update GitLab.com with the stable version - Deploy the package (should not need downtime because of the small difference with RC1) -- Deploy the package for ci.gitlab.com +- Deploy the package for gitlab.com/ci ## Release CE, EE and CI diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 3ae0f9616ac..b81b6a59980 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -91,7 +91,18 @@ If your Git repositories are in a directory other than `/home/git/repositories`, you need to tell `gitlab-git-http-server` about it via `/etc/gitlab/default`. See `lib/support/init.d/gitlab.default.example` for the options. -### 6. Install libs, migrations, etc. +### 6. Copy secrets + +The `secrets.yml` file is used to store keys to encrypt sessions and encrypt secure variables. +When you run migrations make sure to store it someplace safe. +Don't store it in the same place as your database backups, +otherwise your secrets are exposed if one of your backups is compromised. + +``` +sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml +sudo -u gitlab_ci -H chmod 0600 config/secrets.yml + +### 7. Install libs, migrations, etc. ```bash cd /home/git/gitlab @@ -112,7 +123,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ``` -### 7. Update config files +### 8. Update config files #### New configuration options for `gitlab.yml` @@ -122,6 +133,8 @@ There are new configuration options available for [`gitlab.yml`](config/gitlab.y git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example ``` +The new options include configuration of GitLab CI that are now being part of GitLab CE and EE. + #### New Nginx configuration Because of the new `gitlab-git-http-server` you need to update your Nginx @@ -139,12 +152,17 @@ git diff origin/7-14-stable:lib/support/nginx/gitlab-ssl origin/8-0-stable:lib/s git diff origin/7-14-stable:lib/support/nginx/gitlab origin/8-0-stable:lib/support/nginx/gitlab ``` -### 8. Start application +### 9. Migrate GitLab CI to GitLab CE/EE + +Now, GitLab CE and EE has CI integrated. However, migrations don't happen automatically and you need to do it manually. +Please follow the following guide [to migrate](../migrate/README.md) your GitLab CI instance to GitLab CE/EE. + +### 10. Start application sudo service gitlab start sudo service nginx restart -### 9. Check application status +### 11. Check application status Check if GitLab and its environment are configured correctly: -- cgit v1.2.1 From df7d807d5a4e26efc78516459c04be286d6624c9 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 00:43:04 +0200 Subject: Migrate CI services --- lib/tasks/ci/migrate.rake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake index 2760c503e22..2bbcd0f578c 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -11,6 +11,7 @@ namespace :ci do Rake::Task["ci:migrate:db"].invoke Rake::Task["ci:migrate:autoincrements"].invoke Rake::Task["ci:migrate:tags"].invoke + Rake::Task["ci:migrate:services"].invoke end namespace :migrate do @@ -52,5 +53,11 @@ namespace :ci do end end end + + desc 'GitLab | Migrate CI services' + task services: :environment do + c = ActiveRecord::Base.connection + c.execute("UPDATE ci_services SET type=CONCAT('Ci::'', type) WHERE type NOT LIKE 'Ci::%'") + end end end -- cgit v1.2.1 From 1c289ac0c3b7993cfca961a9b446267b700a97c7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 15 Sep 2015 22:38:48 -0700 Subject: Fix broken sort in merge request API Closes #2266 --- CHANGELOG | 1 + lib/api/merge_requests.rb | 2 +- spec/requests/api/merge_requests_spec.rb | 25 +++++++++++++------------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eb4c59d6205..71238630d31 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.0.0 (unreleased) + - Fix broken sort in merge request API (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu) - Add "replace" and "upload" functionalities to allow user replace existing file and upload new file into current repository diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 7412274b045..63ea2f05438 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -55,7 +55,7 @@ module API else merge_requests end - merge_requests.reorder(issuable_order_by => issuable_sort) + merge_requests = merge_requests.reorder(issuable_order_by => issuable_sort) present paginate(merge_requests), with: Entities::MergeRequest end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 942768fa254..35b3d3e296a 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -2,11 +2,12 @@ require "spec_helper" describe API::API, api: true do include ApiHelpers + let(:base_time) { Time.now } let(:user) { create(:user) } let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } - let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") } - let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test") } - let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test") } + let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) } + let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.seconds) } + let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) } let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") } @@ -74,8 +75,8 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) - expect(json_response.last['id']).to eq(@mr_earlier.id) - expect(json_response.first['id']).to eq(@mr_later.id) + response_dates = json_response.map{ |merge_request| merge_request['created_at'] } + expect(response_dates).to eq(response_dates.sort) end it "should return an array of merge_requests in descending order" do @@ -83,8 +84,8 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) - expect(json_response.first['id']).to eq(@mr_later.id) - expect(json_response.last['id']).to eq(@mr_earlier.id) + response_dates = json_response.map{ |merge_request| merge_request['created_at'] } + expect(response_dates).to eq(response_dates.sort.reverse) end it "should return an array of merge_requests ordered by updated_at" do @@ -92,17 +93,17 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) - expect(json_response.last['id']).to eq(@mr_earlier.id) - expect(json_response.first['id']).to eq(@mr_later.id) + response_dates = json_response.map{ |merge_request| merge_request['updated_at'] } + expect(response_dates).to eq(response_dates.sort.reverse) end it "should return an array of merge_requests ordered by created_at" do - get api("/projects/#{project.id}/merge_requests?sort=created_at", user) + get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user) expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) - expect(json_response.last['id']).to eq(@mr_earlier.id) - expect(json_response.first['id']).to eq(@mr_later.id) + response_dates = json_response.map{ |merge_request| merge_request['created_at'] } + expect(response_dates).to eq(response_dates.sort) end end end -- cgit v1.2.1 From b92c03730bfd95e8ad6d984e240aaf15b22e4b5a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 16 Sep 2015 09:09:09 +0200 Subject: Tweak text --- doc/reply_by_email/postfix.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md index 0b16e5e8e80..b8ab07d9fe1 100644 --- a/doc/reply_by_email/postfix.md +++ b/doc/reply_by_email/postfix.md @@ -58,7 +58,7 @@ The instructions make the assumption that you will be using the email address `r 220 gitlab.example.com ESMTP Postfix (Ubuntu) ``` - If you get a `Connection refused` error instead, check if `postfix` is running: + If you get a `Connection refused` error instead, verify that `postfix` is running: ```sh sudo postfix status @@ -114,7 +114,7 @@ The instructions make the assumption that you will be using the email address `r ## Configure Postfix to use Maildir-style mailboxes -Courier, which we will install later to add IMAP authentication, requiers mailboxes to have the Maildir format, rather than mbox. +Courier, which we will install later to add IMAP authentication, requires mailboxes to have the Maildir format, rather than mbox. 1. Configure Postfix to use Maildir-style mailboxes: @@ -130,7 +130,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo 1. Test the new setup: - 1. Follow steps 1 and 2 of "Test the out-of-the-box setup". + 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_. 2. Check if the `replies` user received the email: ```sh @@ -287,7 +287,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo a login replies PASSWORD ``` - Replace PASSWORD by the password you set on the `replies` user earlier. + Replace PASSWORD with the password you set on the `replies` user earlier. You should see output like this: @@ -303,7 +303,7 @@ Courier, which we will install later to add IMAP authentication, requiers mailbo ## Done! -If all the tests went all right, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) doc to configure GitLab. +If all the tests were successfull, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) guide to configure GitLab. --------- -- cgit v1.2.1 From 84d57bc70391f0419bc60c8fcffb3694078d8fb9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 16 Sep 2015 09:14:04 +0200 Subject: Make code clearer --- lib/gitlab/ldap/auth_hash.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb index dc8a8fd41dd..55deeeacd90 100644 --- a/lib/gitlab/ldap/auth_hash.rb +++ b/lib/gitlab/ldap/auth_hash.rb @@ -6,18 +6,16 @@ module Gitlab private def get_info(key) - raw_key = ldap_config.attributes[key] - return super unless raw_key + attributes = ldap_config.attributes[key] + return super unless attributes - value = - case raw_key - when String - get_raw(raw_key) - when Array - raw_key.inject(nil) { |value, key| value || get_raw(key).presence } - else - nil - end + attributes = Array(attributes) + + value = nil + attributes.each do |attribute| + value = get_raw(attribute) + break if value.present? + end return super unless value -- cgit v1.2.1 From d3886f9d41a90f68578e70985caac6afbfe12747 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 10:58:33 +0200 Subject: Added migration docs and updated installation documentation --- doc/README.md | 1 + doc/migrate/README.md | 0 doc/migrate_ci_to_ce/README.md | 244 +++++++++++++++++++++++++++++++++++++++++ doc/update/7.14-to-8.0.md | 2 +- lib/support/nginx/gitlab_ci | 29 +++++ 5 files changed, 275 insertions(+), 1 deletion(-) delete mode 100644 doc/migrate/README.md create mode 100644 doc/migrate_ci_to_ce/README.md create mode 100644 lib/support/nginx/gitlab_ci diff --git a/doc/README.md b/doc/README.md index 632ddefeb2c..f5f1f56b1e2 100644 --- a/doc/README.md +++ b/doc/README.md @@ -47,6 +47,7 @@ - [Update](update/README.md) Update guides to upgrade your installation. - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Reply by email](reply_by_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. +- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. ### Administrator documentation diff --git a/doc/migrate/README.md b/doc/migrate/README.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md new file mode 100644 index 00000000000..262346e8ae4 --- /dev/null +++ b/doc/migrate_ci_to_ce/README.md @@ -0,0 +1,244 @@ +## Migrate GitLab CI to GitLab CE/EE + +## Notice + +**You need to have working GitLab CI 7.14 to perform migration. +The older versions are not supported and will most likely break migration procedure.** + +This migration can't be done online and takes significant amount of time. +Make sure to plan it ahead. + +If you are running older version please follow the upgrade guide first: +https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/update/7.13-to-7.14.md + +The migration is done in two parts: +1. **[CI]** You will be making a changes to GitLab CI instance. +1. **[CE]** You will be making a changes to GitLab CE/EE instance. + +### 1. Stop CI server [CI] + + sudo service gitlab_ci stop + +### 2. Backup [CI] + +**The migration procedure is database breaking. +You need to create backup if you still want to access CI data in case of failure.** + +```bash +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production +``` + +### 3. Prepare GitLab CI database to migration [CI] + +Copy and paste the command in terminal to rename all tables. +This also breaks your database structure disallowing you to use it anymore. + + cat < gitlab_ci.sql + +#### b. Dump PostgreSQL + + pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql + +#### c. Dump MySQL and migrate to PostgreSQL + + # Dump existing MySQL database first + mysqldump --default-character-set=utf8 --compatible=postgresql --complete-insert \ + --host=DB_USERNAME --port=DB_PORT --user=DB_HOSTNAME -p + GITLAB_CI_DATABASE \ + ci_application_settings ci_builds ci_commits ci_events ci_jobs ci_projects \ + ci_runner_projects ci_runners ci_services ci_tags ci_taggings ci_trigger_requests \ + ci_triggers ci_variables ci_web_hooks > gitlab_ci.sql.tmp + + # Convert database to be compatible with PostgreSQL + git clone https://github.com/gitlabhq/mysql-postgresql-converter.git -b gitlab + python mysql-postgresql-converter/db_converter.py gitlab_ci.sql.tmp gitlab_ci.sql.tmp2 + ed -s gitlab_ci.sql.tmp2 < mysql-postgresql-converter/move_drop_indexes.ed + + # Filter to only include INSERT statements + grep "^\(START\|SET\|INSERT\|COMMIT\)" gitlab_ci.sql.tmp2 > gitlab_ci.sql + +### 5. Make sure that your GitLab CE/EE is 8.0 [CE] + +Please verify that you use GitLab CE/EE 8.0. +If not, please follow update guide: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md + +### 6. Stop GitLab CE/EE [CE] + +Before you can migrate actual data you need to stop GitLab CE/EE first. + + sudo service gitlab stop + +### 7. Backup GitLab CE/EE [CE] + +This migration poses a **significant risk** of breaking your GitLab CE/EE. +You should create a backup before doing it. + + cd /home/git/gitlab + sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production + +### 8. Copy secret tokens [CE] + +The `secrets.yml` file stores encryption keys for secure variables. + +You need to copy the content of `config/secrets.yml` to the same file in GitLab CE. + + sudo cp /home/gitlab_ci/gitlab-ci/config/secrets.yml /home/git/gitlab/config/secrets.yml + sudo chown git:git /home/git/gitlab/config/secrets.yml + sudo chown 0600 /home/git/gitlab/config/secrets.yml + +### 9. Copy build logs [CE] + +You need to copy the contents of `builds/` to the same directory in GitLab CE/EE. + + sudo rsync -av /home/gitlab_ci/gitlab-ci/builds /home/git/gitlab/builds + sudo chown -R git:git /home/git/gitlab/builds + +The build traces are usually quite big so it will take a significant amount of time. + +### 10. Import GitLab CI database [CE] + +The one of the last steps is to import existing GitLab CI database. + + sudo mv /home/gitlab_ci/gitlab-ci/gitlab_ci.sql /home/git/gitlab/gitlab_ci.sql + sudo chown git:git /home/git/gitlab/gitlab_ci.sql + sudo -u git -H bundle exec rake ci:migrate CI_DUMP=/home/git/gitlab/gitlab_ci.sql RAILS_ENV=production + +This will take a significant amount of time. The GitLab CE/EE task does: +1. Deletes data from all existing CI tables +1. Import database data +1. Fixes database auto increments +1. Fixes tags assigned to Builds and Runners +1. Fixes services used by CI + +### 11. Start GitLab [CE] + + sudo service gitlab start + +### 12. Update nginx [CI] + +Now get back to GitLab CI and update **Nginx** configuration in order to: +1. Have all existing runners able to communicate with GitLab. +1. Have GitLab able send build triggers to CI address specified in Project's settings -> Services -> GitLab CI. + +You need to edit `/etc/nginx/sites-available/gitlab_ci` and paste: + + # GITLAB CI + server { + listen 80 default_server; # e.g., listen 192.168.1.1:80; + server_name YOUR_CI_SERVER_FQDN; # e.g., server_name source.example.com; + + access_log /var/log/nginx/gitlab_ci_access.log; + error_log /var/log/nginx/gitlab_ci_error.log; + + # expose API to fix runners + location /api { + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + + # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN + resolver 8.8.8.8 8.8.4.4; + proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } + + # redirect all other CI requests + location / { + return 301 $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } + + # adjust this to match the largest build log your runners might submit, + # set to 0 to disable limit + client_max_body_size 10m; + } + +Make sure to fill the blanks to match your setup: +1. **YOUR_CI_SERVER_FQDN**: The existing public facing address of GitLab CI, eg. ci.gitlab.com. +1. **YOUR_GITLAB_SERVER_FQDN**: The public facing address of GitLab CE/EE, eg. gitlab.com. + +**Make sure to not remove the `/ci$request_uri`. This is required to properly forward the requests.** + +## Check your configuration + + sudo nginx -t + +## Restart nginx + + sudo /etc/init.d/nginx restart + +### Done! + +If everything went OK you should be able to access all your GitLab CI data by pointing your browser to: +https://gitlab.example.com/ci/. + +The GitLab CI should also work when using the previous address, redirecting you to the GitLab CE/EE. + +**Enjoy!** diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index b81b6a59980..59415e98782 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -155,7 +155,7 @@ git diff origin/7-14-stable:lib/support/nginx/gitlab origin/8-0-stable:lib/suppo ### 9. Migrate GitLab CI to GitLab CE/EE Now, GitLab CE and EE has CI integrated. However, migrations don't happen automatically and you need to do it manually. -Please follow the following guide [to migrate](../migrate/README.md) your GitLab CI instance to GitLab CE/EE. +Please follow the following guide [to migrate](../migrate_ci_to_ce/README.md) your GitLab CI instance to GitLab CE/EE. ### 10. Start application diff --git a/lib/support/nginx/gitlab_ci b/lib/support/nginx/gitlab_ci new file mode 100644 index 00000000000..bf05edfd780 --- /dev/null +++ b/lib/support/nginx/gitlab_ci @@ -0,0 +1,29 @@ +# GITLAB CI +server { + listen 80 default_server; # e.g., listen 192.168.1.1:80; + server_name YOUR_CI_SERVER_FQDN; # e.g., server_name source.example.com; + + access_log /var/log/nginx/gitlab_ci_access.log; + error_log /var/log/nginx/gitlab_ci_error.log; + + # expose API to fix runners + location /api { + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + + # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN + resolver 8.8.8.8 8.8.4.4; + proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } + + # redirect all other CI requests + location / { + return 301 $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } + + # adjust this to match the largest build log your runners might submit, + # set to 0 to disable limit + client_max_body_size 10m; +} \ No newline at end of file -- cgit v1.2.1 From fb0bd4eb2f73707ddf293c1f5cc2edd0358cd927 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 11:08:48 +0200 Subject: Added info about gitlab.yml config --- doc/migrate_ci_to_ce/README.md | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 262346e8ae4..e12ea9a9ad7 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -11,7 +11,7 @@ Make sure to plan it ahead. If you are running older version please follow the upgrade guide first: https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/update/7.13-to-7.14.md -The migration is done in two parts: +The migration is divided into a two parts: 1. **[CI]** You will be making a changes to GitLab CI instance. 1. **[CE]** You will be making a changes to GitLab CE/EE instance. @@ -22,7 +22,7 @@ The migration is done in two parts: ### 2. Backup [CI] **The migration procedure is database breaking. -You need to create backup if you still want to access CI data in case of failure.** +You need to create backup if you still want to access GitLab CI in case of failure.** ```bash cd /home/gitlab_ci/gitlab-ci @@ -65,7 +65,6 @@ First check used database and credentials on GitLab CI and GitLab CE/EE: cat /home/git/gitlab/config/database.yml Please first check the database engine used for GitLab CI and GitLab CE/EE. -There's great chance that you will also need to convert MySQL to PostgreSQL: 1. If your GitLab CI uses **mysql2** and GitLab CE/EE uses it too. Please follow **Dump MySQL** guide. @@ -76,7 +75,8 @@ Please follow **Dump PostgreSQL** guide. 1. If your GitLab CI uses **mysql2** and GitLab CE/EE uses **postgres**. Please follow **Dump MySQL and migrate to PostgreSQL** guide. -**Remember credentials stored for GitLab CI. You will need to put the credentials into commands executed below.** +**Remember credentials stored for accessing GitLab CI. +You will need to put these credentials into commands executed below.** $ cat config/database.yml [10:06:55] # @@ -128,18 +128,18 @@ Please follow **Dump MySQL and migrate to PostgreSQL** guide. ### 5. Make sure that your GitLab CE/EE is 8.0 [CE] Please verify that you use GitLab CE/EE 8.0. -If not, please follow update guide: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md +If not, please follow the update guide: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md ### 6. Stop GitLab CE/EE [CE] -Before you can migrate actual data you need to stop GitLab CE/EE first. +Before you can migrate data you need to stop GitLab CE/EE first. sudo service gitlab stop ### 7. Backup GitLab CE/EE [CE] This migration poses a **significant risk** of breaking your GitLab CE/EE. -You should create a backup before doing it. +**You should create the GitLab CI/EE backup before doing it.** cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production @@ -153,8 +153,19 @@ You need to copy the content of `config/secrets.yml` to the same file in GitLab sudo cp /home/gitlab_ci/gitlab-ci/config/secrets.yml /home/git/gitlab/config/secrets.yml sudo chown git:git /home/git/gitlab/config/secrets.yml sudo chown 0600 /home/git/gitlab/config/secrets.yml + +### 9. New configuration options for `gitlab.yml` [CE] + +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). +View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example +``` -### 9. Copy build logs [CE] +The new options include configuration of GitLab CI that are now being part of GitLab CE and EE. + +### 10. Copy build logs [CE] You need to copy the contents of `builds/` to the same directory in GitLab CE/EE. @@ -163,7 +174,7 @@ You need to copy the contents of `builds/` to the same directory in GitLab CE/EE The build traces are usually quite big so it will take a significant amount of time. -### 10. Import GitLab CI database [CE] +### 11. Import GitLab CI database [CE] The one of the last steps is to import existing GitLab CI database. @@ -171,21 +182,23 @@ The one of the last steps is to import existing GitLab CI database. sudo chown git:git /home/git/gitlab/gitlab_ci.sql sudo -u git -H bundle exec rake ci:migrate CI_DUMP=/home/git/gitlab/gitlab_ci.sql RAILS_ENV=production -This will take a significant amount of time. The GitLab CE/EE task does: -1. Deletes data from all existing CI tables +The task does: +1. Delete data from all existing CI tables 1. Import database data -1. Fixes database auto increments -1. Fixes tags assigned to Builds and Runners -1. Fixes services used by CI +1. Fix database auto increments +1. Fix tags assigned to Builds and Runners +1. Fix services used by CI + +### 12. Start GitLab [CE] -### 11. Start GitLab [CE] +You can start GitLab CI/EE now and see if everything is working. sudo service gitlab start -### 12. Update nginx [CI] +### 13. Update nginx [CI] Now get back to GitLab CI and update **Nginx** configuration in order to: -1. Have all existing runners able to communicate with GitLab. +1. Have all existing runners able to communicate with a migrated GitLab CI. 1. Have GitLab able send build triggers to CI address specified in Project's settings -> Services -> GitLab CI. You need to edit `/etc/nginx/sites-available/gitlab_ci` and paste: @@ -226,6 +239,10 @@ Make sure to fill the blanks to match your setup: **Make sure to not remove the `/ci$request_uri`. This is required to properly forward the requests.** +You should also make sure that you can do: +1. `curl https://YOUR_GITLAB_SERVER_FQDN/` from your previous GitLab CI server. +1. `curl https://YOUR_CI_SERVER_FQDN/` from your GitLab CE/EE server. + ## Check your configuration sudo nginx -t @@ -234,7 +251,7 @@ Make sure to fill the blanks to match your setup: sudo /etc/init.d/nginx restart -### Done! +### 14. Done! If everything went OK you should be able to access all your GitLab CI data by pointing your browser to: https://gitlab.example.com/ci/. -- cgit v1.2.1 From ee5ce705d40cf7a2c27ac0f0c2dc330ef320edfc Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 16 Sep 2015 13:20:38 +0300 Subject: fix rubocop --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index f372e2f6ba8..05b8ecc3b00 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1000,6 +1000,7 @@ AllCops: - 'lib/backup/**/*' - 'lib/ci/backup/**/*' - 'lib/tasks/**/*' + - 'lib/ci/migrate/**/*' - 'lib/email_validator.rb' - 'lib/gitlab/upgrader.rb' - 'lib/gitlab/seeder.rb' -- cgit v1.2.1 From 0859ba75a873ce78f77587369b9e761a2cc782db Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 13:30:03 +0200 Subject: Fix migrate task --- lib/tasks/ci/migrate.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake index 2bbcd0f578c..e7d41874a11 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -57,7 +57,7 @@ namespace :ci do desc 'GitLab | Migrate CI services' task services: :environment do c = ActiveRecord::Base.connection - c.execute("UPDATE ci_services SET type=CONCAT('Ci::'', type) WHERE type NOT LIKE 'Ci::%'") + c.execute("UPDATE ci_services SET type=CONCAT('Ci::', type) WHERE type NOT LIKE 'Ci::%'") end end end -- cgit v1.2.1 From 0696aeb3dcdb8b083b63f81fc6fcac51766afffe Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 16 Sep 2015 15:19:47 +0200 Subject: Fix gemfile.lock --- Gemfile.lock | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e913d7ae9f6..a4a0762bdd8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -886,7 +886,7 @@ DEPENDENCIES rerun (~> 0.10.0) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) - rubocop (= 0.28.0) + rubocop (~> 0.28.0) ruby-fogbugz (~> 0.2.1) sanitize (~> 2.0) sass-rails (~> 4.0.5) @@ -928,3 +928,6 @@ DEPENDENCIES webmock (~> 1.21.0) whenever (~> 0.8.4) wikicloth (= 0.8.1) + +BUNDLED WITH + 1.10.6 -- cgit v1.2.1 From 5a035238cb7566a4d89d1a01cf145b4f3730a4cb Mon Sep 17 00:00:00 2001 From: Nicolas Bigaouette Date: Wed, 16 Sep 2015 10:00:27 -0400 Subject: Fix in Markdown help page: properly create the link to a wiki page. The help file on Markdown that is included with Gitlab CE (7.14.3 d321305) and also gitlab.com (see https://gitlab.com/help/markdown/markdown#links) contains a typo in the description of a link to a wiki page. --- doc/markdown/markdown.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index 322111ae9e1..6fdb2fe1491 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -413,7 +413,7 @@ Some text to show that the reference links can follow later. Relative links do not allow referencing project files in a wiki page or wiki page in a project file. The reason for this is that, in GitLab, wiki is always a separate git repository. For example: -`[I'm a reference-style link][style]` +`[I'm a reference-style link](style)` will point the link to `wikis/style` when the link is inside of a wiki markdown file. -- cgit v1.2.1 From 5772faf19bcd859c01da167d3c756a1a2e2b5f7f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 16:46:10 +0200 Subject: Fix projects edit --- app/views/ci/projects/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/ci/projects/edit.html.haml b/app/views/ci/projects/edit.html.haml index 298007a6565..79e8fd3a295 100644 --- a/app/views/ci/projects/edit.html.haml +++ b/app/views/ci/projects/edit.html.haml @@ -1,6 +1,6 @@ - if @project.generated_yaml_config %p.alert.alert-danger - CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_project_path(@project)} + CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project(@project)} or %a.preview-yml{:href => "#yaml-content", "data-toggle" => "modal"} preview yaml file which is based on your old jobs. -- cgit v1.2.1 From 7348d34cfdb50c1cc3efa2debb093e8019752ac2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 16:46:19 +0200 Subject: Fix migrate tags statement --- lib/ci/migrate/tags.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb index f4114c698d2..125a535e9a9 100644 --- a/lib/ci/migrate/tags.rb +++ b/lib/ci/migrate/tags.rb @@ -40,7 +40,7 @@ module Ci tags = ActiveRecord::Base.connection.select_all( 'select ci_tags.name from ci_tags ' + 'join ci_taggings on ci_tags.id = ci_taggings.tag_id ' + - "where taggable_type = #{ActiveRecord::Base::sanitize(type)} and taggable_id = #{ActiveRecord::Base::sanitize(id)} and context = \"tags\"" + "where taggable_type = #{ActiveRecord::Base::sanitize(type)} and taggable_id = #{ActiveRecord::Base::sanitize(id)} and context = 'tags'" ) tags.map { |tag| tag['name'] } end -- cgit v1.2.1 From e01300ddb0f8a910ac34329a6dabe83b3cbdc69d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 16:46:26 +0200 Subject: Fix update_runner_info helper --- lib/ci/api/helpers.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 9197f917d73..e602cda81d6 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -1,6 +1,8 @@ module Ci module API module Helpers + UPDATE_RUNNER_EVERY = 60 + def authenticate_runners! forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN end -- cgit v1.2.1 From 1b2a1d0ddd39ecc530c64563d0083e1e255f7c1a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 16:48:54 +0200 Subject: Fix skipped svg --- public/ci/build-skipped.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/ci/build-skipped.svg diff --git a/public/ci/build-skipped.svg b/public/ci/build-skipped.svg new file mode 100644 index 00000000000..f15507188e0 --- /dev/null +++ b/public/ci/build-skipped.svg @@ -0,0 +1 @@ +buildbuildskippedskipped \ No newline at end of file -- cgit v1.2.1 From 912f470497129b0d8759b18700299e38535e7bec Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 16:59:54 +0200 Subject: Fix ordering issue --- app/models/ci/project.rb | 2 +- ...20150916145038_add_index_for_committed_at_and_id.rb | 5 +++++ db/schema.rb | 3 ++- spec/models/ci/project_spec.rb | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150916145038_add_index_for_committed_at_and_id.rb diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 2cf1783616f..cd2692246b7 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -33,7 +33,7 @@ module Ci belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id - has_many :commits, ->() { order(:committed_at) }, dependent: :destroy, class_name: 'Ci::Commit' + has_many :commits, ->() { order('CASE WHEN commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit' has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' diff --git a/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb b/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb new file mode 100644 index 00000000000..78d9e5f61a1 --- /dev/null +++ b/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb @@ -0,0 +1,5 @@ +class AddIndexForCommittedAtAndId < ActiveRecord::Migration + def change + add_index :ci_commits, [:project_id, :committed_at, :id] + end +end diff --git a/db/schema.rb b/db/schema.rb index 5fd764bf698..48314b8db6a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150914215247) do +ActiveRecord::Schema.define(version: 20150916145038) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -118,6 +118,7 @@ ActiveRecord::Schema.define(version: 20150914215247) do t.datetime "committed_at" end + add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree add_index "ci_commits", ["project_id", "committed_at"], name: "index_ci_commits_on_project_id_and_committed_at", using: :btree add_index "ci_commits", ["project_id", "sha"], name: "index_ci_commits_on_project_id_and_sha", using: :btree add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 1025868da6e..a6fd6f27942 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -61,6 +61,24 @@ describe Ci::Project do end end + describe 'ordered commits' do + let (:project) { FactoryGirl.create :ci_project } + + it 'returns ordered list of commits' do + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + project.commits.should == [commit2, commit1] + end + + it 'returns commits ordered by committed_at and id, with nulls last' do + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project + project.commits.should == [commit2, commit4, commit3, commit1] + end + end + context :valid_project do let(:project) { FactoryGirl.create :ci_project } -- cgit v1.2.1 From 7ea48ec54689cc262cc036fa65234a231a39c7e8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 17:19:05 +0200 Subject: Fix CI tests --- app/models/ci/project.rb | 2 +- spec/models/ci/project_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index cd2692246b7..ae901d4ccd0 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -33,7 +33,7 @@ module Ci belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id - has_many :commits, ->() { order('CASE WHEN commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit' + has_many :commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit' has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index a6fd6f27942..89dc906e845 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -67,7 +67,7 @@ describe Ci::Project do it 'returns ordered list of commits' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project - project.commits.should == [commit2, commit1] + expect(project.commits).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do @@ -75,7 +75,7 @@ describe Ci::Project do commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project - project.commits.should == [commit2, commit4, commit3, commit1] + expect(project.commits).to eq([commit2, commit4, commit3, commit1]) end end -- cgit v1.2.1 From 2b20603f5a7e419defc4bd58d9fce63924c843d8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 17:30:15 +0200 Subject: Make rubocop happy --- spec/models/ci/project_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 89dc906e845..261ea69f5b4 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -62,7 +62,7 @@ describe Ci::Project do end describe 'ordered commits' do - let (:project) { FactoryGirl.create :ci_project } + let(:project) { FactoryGirl.create :ci_project } it 'returns ordered list of commits' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project -- cgit v1.2.1 From f9dc3a2fb59da4ca6192faa48a9b25850f99e1a7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 16 Sep 2015 17:54:38 +0200 Subject: Add links from GitLab to CI Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 1 + app/views/layouts/nav/_dashboard.html.haml | 5 +++++ app/views/projects/_home_panel.html.haml | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index 81951467d41..6e2f9645661 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -119,6 +119,7 @@ class Project < ActiveRecord::Base has_many :starrers, through: :users_star_projects, source: :user has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" + has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index c3b07200621..56283cba6bd 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -46,3 +46,8 @@ = icon('user fw') %span Profile Settings + = nav_link(controller: :ci) do + = link_to ci_root_path, title: 'Continuous Integration', data: {placement: 'right'} do + = icon('building fw') + %span + CI diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index dbecd1e7192..b347846c932 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -28,4 +28,8 @@ = render 'projects/buttons/dropdown' + - if @project.gitlab_ci? + = link_to ci_project_path(@project.gitlab_ci_project), class: 'btn btn-default' do + CI + = render "shared/clone_panel" -- cgit v1.2.1 From 71cdb24990beb0d6f0d3d845d01e75b14180801b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Sep 2015 10:52:23 -0700 Subject: Fix default avatars to ensure that helpers don't have /assets dir appended --- app/helpers/application_helper.rb | 2 +- app/helpers/groups_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c3da54fd554..b049bd9fcc2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -82,7 +82,7 @@ module ApplicationHelper end def default_avatar - image_path('no_avatar.png') + 'no_avatar.png' end def last_commit(project) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 82eebf4245b..5e70de23f29 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -27,7 +27,7 @@ module GroupsHelper if group && group.avatar.present? group.avatar.url else - image_path('no_group_avatar.png') + 'no_group_avatar.png' end end -- cgit v1.2.1 From 07d20657ceb0ac74f7f03149e1e4369a73c99c26 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 16 Sep 2015 19:59:21 +0200 Subject: Style CI projects page and builds list --- app/assets/stylesheets/ci/projects.scss | 51 +++++++++++++++++++++++++++----- app/views/ci/projects/_project.html.haml | 2 +- app/views/ci/projects/gitlab.html.haml | 22 +++++++------- app/views/ci/projects/index.html.haml | 2 +- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index b246fb9e07d..e5d69360c2c 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -6,19 +6,54 @@ line-height: 1.5; } - .builds { - @extend .table; - - .build { - &.alert{ - margin-bottom: 6px; - } - } + .wide-table-holder { + margin-left: -$gl-padding; + margin-right: -$gl-padding; } + .builds, .projects-table { + .alert-success { + background-color: #6fc995; + border-color: #5bba83; + } + + .alert-danger { + background-color: #eb897f; + border-color: #d4776e; + } + + .alert-info { + background-color: #3498db; + border-color: #2e8ece; + } + + .alert-warning { + background-color: #EB974E; + border-color: #E87E04; + } + + .alert-disabled { + background: $background-color; + border-color: $border-color; + } + + .light { + border-color: $border-color; + } + + th, td { + padding: 10px $gl-padding; + } + td { vertical-align: middle !important; + border-color: inherit !important; + + a { + font-weight: normal; + text-decoration: none; + } } } diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index b3ad47ce432..e4a811119e1 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -2,7 +2,7 @@ %tr.alert{class: commit_status_alert_class(last_commit) } %td = link_to [:ci, project] do - %strong= project.name + = project.name %td - if last_commit #{last_commit.status} (#{commit_link(last_commit)}) diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml index f57dfcb0790..2101aa932a4 100644 --- a/app/views/ci/projects/gitlab.html.haml +++ b/app/views/ci/projects/gitlab.html.haml @@ -1,22 +1,22 @@ - if @offset == 0 - .clearfix.light + .gray-content-block.clearfix.light.second-block .pull-left.fetch-status - if params[:search].present? by keyword: "#{params[:search]}", #{@total_count} projects, #{@projects.size} of them added to CI - %br - %table.table.projects-table.content-list - %thead - %tr - %th Project Name - %th Last commit - %th Access - %th Commits + .wide-table-holder + %table.table.projects-table.content-list + %thead + %tr + %th Project Name + %th Last commit + %th Access + %th Commits - = render @projects + = render @projects - = render "gl_projects" + = render "gl_projects" %p.text-center.hide.loading %i.fa.fa-refresh.fa-spin diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 085a70811ae..60ab29a66cf 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -1,7 +1,7 @@ - if current_user .gray-content-block.top-block = render "search" - .projects.prepend-top-default + .projects %p.fetch-status.light %i.fa.fa-refresh.fa-spin :coffeescript -- cgit v1.2.1 From 1dc04bf6b7b3d3984e1ba69c7fce5f7fa1677fe9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Sep 2015 11:04:09 -0700 Subject: Eliminate combined image_tag and image_path in providers list --- app/helpers/auth_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index d9502181c4f..ce7e9b1db87 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -40,7 +40,7 @@ module AuthHelper if provider_has_icon?(provider) file_name = "#{provider.to_s.split('_').first}_#{size}.png" - image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}") + image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}") else label end -- cgit v1.2.1 From 9437f0618a72d6201bbf0382f4237c678138ff43 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 16 Sep 2015 20:16:44 +0200 Subject: Fix confusing behaviour of back link in CI sidebar Back link should always be responsible for sidebar navigation below. It should change sidebar navigation to one level up --- app/views/layouts/ci/_nav_build.html.haml | 3 --- app/views/layouts/ci/_nav_commit.haml | 3 --- app/views/layouts/ci/_nav_project.html.haml | 4 ++-- app/views/layouts/ci/build.html.haml | 2 +- app/views/layouts/ci/commit.html.haml | 2 +- 5 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 app/views/layouts/ci/_nav_build.html.haml delete mode 100644 app/views/layouts/ci/_nav_commit.haml diff --git a/app/views/layouts/ci/_nav_build.html.haml b/app/views/layouts/ci/_nav_build.html.haml deleted file mode 100644 index 732882726e7..00000000000 --- a/app/views/layouts/ci/_nav_build.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -= render 'layouts/ci/nav_project', - back_title: 'Back to project commit', - back_url: ci_project_ref_commits_path(@project, @commit.ref, @commit.sha) diff --git a/app/views/layouts/ci/_nav_commit.haml b/app/views/layouts/ci/_nav_commit.haml deleted file mode 100644 index 19c526678d0..00000000000 --- a/app/views/layouts/ci/_nav_commit.haml +++ /dev/null @@ -1,3 +0,0 @@ -= render 'layouts/ci/nav_project', - back_title: 'Back to project commits', - back_url: ci_project_path(@project) diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 10b87e3a2b1..d747679c8cf 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,8 +1,8 @@ %ul.nav.nav-sidebar = nav_link do - = link_to defined?(back_url) ? back_url : ci_root_path, title: defined?(back_title) ? back_title : 'Back to Dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to ci_root_path, title: 'Back to CI projects', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') - %span= defined?(back_title) ? back_title : 'Back to Dashboard' + %span= 'Back to CI projects' %li.separate-item = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do = link_to ci_project_path(@project) do diff --git a/app/views/layouts/ci/build.html.haml b/app/views/layouts/ci/build.html.haml index d404ecb894a..a1356f0dc2e 100644 --- a/app/views/layouts/ci/build.html.haml +++ b/app/views/layouts/ci/build.html.haml @@ -8,4 +8,4 @@ - else = render "layouts/header/public", title: header_title - = render 'layouts/ci/page', sidebar: 'nav_build' + = render 'layouts/ci/page', sidebar: 'nav_project' diff --git a/app/views/layouts/ci/commit.html.haml b/app/views/layouts/ci/commit.html.haml index 5727f1b8e3e..a1356f0dc2e 100644 --- a/app/views/layouts/ci/commit.html.haml +++ b/app/views/layouts/ci/commit.html.haml @@ -8,4 +8,4 @@ - else = render "layouts/header/public", title: header_title - = render 'layouts/ci/page', sidebar: 'nav_commit' + = render 'layouts/ci/page', sidebar: 'nav_project' -- cgit v1.2.1 From 50e5950947490b58cea1a50a5be137d45d0a334c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 20:45:58 +0200 Subject: Add missing proxy requests to migration docs --- doc/migrate_ci_to_ce/README.md | 12 ++++++++++++ lib/support/nginx/gitlab_ci | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index e12ea9a9ad7..13efc8442d2 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -222,6 +222,18 @@ You need to edit `/etc/nginx/sites-available/gitlab_ci` and paste: resolver 8.8.8.8 8.8.4.4; proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; } + + # expose build endpoint to allow trigger builds + location ~ ^/projects/\d+/build$ { + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + + # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN + resolver 8.8.8.8 8.8.4.4; + proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } # redirect all other CI requests location / { diff --git a/lib/support/nginx/gitlab_ci b/lib/support/nginx/gitlab_ci index bf05edfd780..ce179d6f599 100644 --- a/lib/support/nginx/gitlab_ci +++ b/lib/support/nginx/gitlab_ci @@ -18,6 +18,18 @@ server { proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; } + # expose build endpoint to allow trigger builds + location ~ ^/projects/\d+/build$ { + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + + # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN + resolver 8.8.8.8 8.8.4.4; + proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; + } + # redirect all other CI requests location / { return 301 $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; -- cgit v1.2.1 From 19a63b0ddff7e414598d1b23457b41cc89b662a1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 16 Sep 2015 15:24:51 -0400 Subject: Add missing code fence closure --- doc/update/7.14-to-8.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 59415e98782..6a0b355b2ec 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -101,6 +101,7 @@ otherwise your secrets are exposed if one of your backups is compromised. ``` sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml sudo -u gitlab_ci -H chmod 0600 config/secrets.yml +``` ### 7. Install libs, migrations, etc. -- cgit v1.2.1 From 1ff7833e8ce0e702ea2f5c5861f7231425b367f0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 16 Sep 2015 15:26:15 -0400 Subject: sudo user is gitlab, not gitlab_ci --- doc/update/7.14-to-8.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 6a0b355b2ec..7736afb6df6 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -99,8 +99,8 @@ Don't store it in the same place as your database backups, otherwise your secrets are exposed if one of your backups is compromised. ``` -sudo -u gitlab_ci -H cp config/secrets.yml.example config/secrets.yml -sudo -u gitlab_ci -H chmod 0600 config/secrets.yml +sudo -u gitlab -H cp config/secrets.yml.example config/secrets.yml +sudo -u gitlab -H chmod 0600 config/secrets.yml ``` ### 7. Install libs, migrations, etc. -- cgit v1.2.1 From e46f5e60b67e932eb39f409f3f3439b46c8a8b12 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 16 Sep 2015 15:32:14 -0400 Subject: Change 7-14-stable to 8-0-stable in doc/install/installation.md [ci skip] --- doc/install/installation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 8936697b40e..3b074fc8467 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -207,9 +207,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-14-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-0-stable gitlab -**Note:** You can change `7-14-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It @@ -221,7 +221,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Update GitLab config file, follow the directions at top of file sudo -u git -H editor config/gitlab.yml - + # Copy the example secrets file sudo -u git -H cp config/secrets.yml.example config/secrets.yml sudo -u git -H chmod 0600 config/secrets.yml @@ -238,7 +238,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Make sure GitLab can write to the public/uploads/ directory sudo chmod -R u+rwX public/uploads - + # Change the permissions of the directory where CI build traces are stored sudo chmod -R u+rwX builds/ -- cgit v1.2.1 From 897e3a20b38d5badea5fa2935f248b27a8c9502d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 16 Sep 2015 15:33:52 -0400 Subject: sudo user is git, not gitlab, not gitlab_ci [ci skip] --- doc/update/7.14-to-8.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 7736afb6df6..2c7003ed063 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -99,8 +99,8 @@ Don't store it in the same place as your database backups, otherwise your secrets are exposed if one of your backups is compromised. ``` -sudo -u gitlab -H cp config/secrets.yml.example config/secrets.yml -sudo -u gitlab -H chmod 0600 config/secrets.yml +sudo -u git -H cp config/secrets.yml.example config/secrets.yml +sudo -u git -H chmod 0600 config/secrets.yml ``` ### 7. Install libs, migrations, etc. -- cgit v1.2.1