From d241c6d057798dfbdb6d69e74f82e1ad1848805b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 13 Aug 2015 06:35:42 -0700 Subject: Restrict users API endpoints to use integer IDs Closes #2267 --- CHANGELOG | 1 + lib/api/users.rb | 2 +- spec/requests/api/users_spec.rb | 56 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 44ffb56d5fd..cf38606396d 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) + - Restrict users API endpoints to use integer IDs (Stan Hu) - Only show recent push event if the branch still exists or a recent merge request has not been created (Stan Hu) - Remove satellites - Better performance for web editor (switched from satellites to rugged) diff --git a/lib/api/users.rb b/lib/api/users.rb index ee29f952246..813cc379e43 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -3,7 +3,7 @@ module API class Users < Grape::API before { authenticate! } - resource :users do + resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do # Get a users list # # Example Request: diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f2aa369985e..f9bc63680ba 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -58,6 +58,11 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 Not found') end + + it "should return a 404 if invalid ID" do + get api("/users/1ASDF", user) + expect(response.status).to eq(404) + end end describe "POST /users" do @@ -257,6 +262,10 @@ describe API::API, api: true do expect(json_response['message']).to eq('404 Not found') end + it "should raise error for invalid ID" do + expect{put api("/users/ASDF", admin) }.to raise_error(ActionController::RoutingError) + end + it 'should return 400 error if user does not validate' do put api("/users/#{user.id}", admin), password: 'pass', @@ -319,6 +328,10 @@ describe API::API, api: true do post api("/users/#{user.id}/keys", admin), key_attrs end.to change{ user.keys.count }.by(1) end + + it "should raise error for invalid ID" do + expect{post api("/users/ASDF/keys", admin) }.to raise_error(ActionController::RoutingError) + end end describe 'GET /user/:uid/keys' do @@ -346,6 +359,11 @@ describe API::API, api: true do expect(json_response).to be_an Array expect(json_response.first['title']).to eq(key.title) end + + it "should return 404 for invalid ID" do + get api("/users/ASDF/keys", admin) + expect(response.status).to eq(404) + end end end @@ -400,6 +418,10 @@ describe API::API, api: true do post api("/users/#{user.id}/emails", admin), email_attrs end.to change{ user.emails.count }.by(1) end + + it "should raise error for invalid ID" do + expect{post api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError) + end end describe 'GET /user/:uid/emails' do @@ -427,6 +449,10 @@ describe API::API, api: true do expect(json_response).to be_an Array expect(json_response.first['email']).to eq(email.email) end + + it "should raise error for invalid ID" do + expect{put api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError) + end end end @@ -463,6 +489,10 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 Email Not Found') end + + it "should raise error for invalid ID" do + expect{delete api("/users/ASDF/emails/bar", admin) }.to raise_error(ActionController::RoutingError) + end end end @@ -491,6 +521,10 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 User Not Found') end + + it "should raise error for invalid ID" do + expect{delete api("/users/ASDF", admin) }.to raise_error(ActionController::RoutingError) + end end describe "GET /user" do @@ -553,6 +587,11 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 Not found') end + + it "should return 404 for invalid ID" do + get api("/users/keys/ASDF", admin) + expect(response.status).to eq(404) + end end describe "POST /user/keys" do @@ -608,6 +647,10 @@ describe API::API, api: true do delete api("/user/keys/#{key.id}") expect(response.status).to eq(401) end + + it "should raise error for invalid ID" do + expect{delete api("/users/keys/ASDF", admin) }.to raise_error(ActionController::RoutingError) + end end describe "GET /user/emails" do @@ -653,6 +696,11 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 Not found') end + + it "should return 404 for invalid ID" do + get api("/users/emails/ASDF", admin) + expect(response.status).to eq(404) + end end describe "POST /user/emails" do @@ -697,6 +745,10 @@ describe API::API, api: true do delete api("/user/emails/#{email.id}") expect(response.status).to eq(401) end + + it "should raise error for invalid ID" do + expect{delete api("/users/emails/ASDF", admin) }.to raise_error(ActionController::RoutingError) + end end describe 'PUT /user/:id/block' do @@ -748,5 +800,9 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('404 User Not Found') end + + it "should raise error for invalid ID" do + expect{put api("/users/ASDF/block", admin) }.to raise_error(ActionController::RoutingError) + end end end -- cgit v1.2.1 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 3655e433dd697998578b03dc57191a412f505f5a Mon Sep 17 00:00:00 2001 From: Rowan Wookey Date: Mon, 24 Aug 2015 23:06:48 +0100 Subject: Fixed #1952 docker ssh connection times can be slow when UseDNS is enabled --- CHANGELOG | 1 + docker/Dockerfile | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5940d586d88..7816430a208 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.0.0 (unreleased) - Search for comments should be case insensetive - Create cross-reference for closing references on commits pushed to non-default branches (Maël Valais) - Ability to search milestones + - Disabled DNS lookups for SSH in docker image (Rowan Wookey) v 7.14.1 (unreleased) - Only include base URL in OmniAuth full_host parameter (Stan Hu) diff --git a/docker/Dockerfile b/docker/Dockerfile index 05521af6963..304bb97409e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -25,6 +25,9 @@ RUN mkdir -p /opt/gitlab/sv/sshd/supervise \ && ln -s /opt/gitlab/sv/sshd /opt/gitlab/service \ && mkdir -p /var/run/sshd +# Disabling use DNS in ssh since it tends to slow connecting +RUN echo "UseDNS no" >> /etc/ssh/sshd_config + # Prepare default configuration RUN ( \ echo "" && \ -- 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 57d001309013c4c27e593778ec68b523e7d73b81 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Sep 2015 15:50:23 +0300 Subject: CRUD for admin labels --- app/controllers/admin/labels_controller.rb | 59 ++++++++ app/models/label.rb | 8 +- app/views/admin/labels/_form.html.haml | 35 +++++ app/views/admin/labels/_label.html.haml | 5 + app/views/admin/labels/destroy.js.haml | 2 + app/views/admin/labels/edit.html.haml | 9 ++ app/views/admin/labels/index.html.haml | 16 +++ app/views/admin/labels/new.html.haml | 7 + app/views/layouts/nav/_admin.html.haml | 6 + config/routes.rb | 2 + db/migrate/20150902001023_add_template_to_label.rb | 5 + db/schema.rb | 160 ++++++++++++++++----- 12 files changed, 275 insertions(+), 39 deletions(-) create mode 100644 app/controllers/admin/labels_controller.rb create mode 100644 app/views/admin/labels/_form.html.haml create mode 100644 app/views/admin/labels/_label.html.haml create mode 100644 app/views/admin/labels/destroy.js.haml create mode 100644 app/views/admin/labels/edit.html.haml create mode 100644 app/views/admin/labels/index.html.haml create mode 100644 app/views/admin/labels/new.html.haml create mode 100644 db/migrate/20150902001023_add_template_to_label.rb diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb new file mode 100644 index 00000000000..bcdfe6c80a0 --- /dev/null +++ b/app/controllers/admin/labels_controller.rb @@ -0,0 +1,59 @@ + +class Admin::LabelsController < Admin::ApplicationController + before_action :set_label, only: [:show, :edit, :update, :destroy] + + def index + @labels = Label.templates.page(params[:page]).per(PER_PAGE) + end + + def show + end + + def new + @label = Label.new + end + + def edit + end + + def create + @label = Label.new(label_params) + @label.template = true + + if @label.save + redirect_to admin_labels_url, notice: "Label was created" + else + render :new + end + end + + def update + if @label.update(label_params) + redirect_to admin_labels_path, notice: 'label was successfully updated.' + else + render :edit + end + end + + def destroy + @label.destroy + @labels = Label.templates + + respond_to do |format| + format.html do + redirect_to(admin_labels_path, notice: 'Label was removed') + end + format.js + end + end + + private + + def set_label + @label = Label.find(params[:id]) + end + + def label_params + params[:label].permit(:title, :color) + end +end diff --git a/app/models/label.rb b/app/models/label.rb index 230631b5180..4a22bd53400 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -24,7 +24,7 @@ class Label < ActiveRecord::Base validates :color, format: { with: /\A#[0-9A-Fa-f]{6}\Z/ }, allow_blank: false - validates :project, presence: true + validates :project, presence: true, unless: Proc.new { |service| service.template? } # Don't allow '?', '&', and ',' for label titles validates :title, @@ -34,6 +34,8 @@ class Label < ActiveRecord::Base default_scope { order(title: :asc) } + scope :templates, -> { where(template: true) } + alias_attribute :name, :title def self.reference_prefix @@ -78,4 +80,8 @@ class Label < ActiveRecord::Base def open_issues_count issues.opened.count end + + def template? + template + end end diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml new file mode 100644 index 00000000000..ad58a3837f6 --- /dev/null +++ b/app/views/admin/labels/_form.html.haml @@ -0,0 +1,35 @@ += form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f| + -if @label.errors.any? + .row + .col-sm-offset-2.col-sm-10 + .alert.alert-danger + - @label.errors.full_messages.each do |msg| + %span= msg + %br + + .form-group + = f.label :title, class: 'control-label' + .col-sm-10 + = f.text_field :title, class: "form-control", required: true + .form-group + = f.label :color, "Background Color", class: 'control-label' + .col-sm-10 + .input-group + .input-group-addon.label-color-preview   + = f.color_field :color, class: "form-control" + .help-block + Choose any color. + %br + Or you can choose one of suggested colors below + + .suggest-colors + - suggested_colors.each do |color| + = link_to '#', style: "background-color: #{color}", data: { color: color } do +   + + .form-actions + = f.submit 'Save', class: 'btn btn-save js-save-button' + = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel' + +:coffeescript + new Labels diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml new file mode 100644 index 00000000000..596e06243dd --- /dev/null +++ b/app/views/admin/labels/_label.html.haml @@ -0,0 +1,5 @@ +%li{id: dom_id(label)} + = render_colored_label(label) + .pull-right + = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm' + = link_to 'Remove', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} diff --git a/app/views/admin/labels/destroy.js.haml b/app/views/admin/labels/destroy.js.haml new file mode 100644 index 00000000000..9d51762890f --- /dev/null +++ b/app/views/admin/labels/destroy.js.haml @@ -0,0 +1,2 @@ +- if @labels.size == 0 + $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000) diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml new file mode 100644 index 00000000000..45c62a76259 --- /dev/null +++ b/app/views/admin/labels/edit.html.haml @@ -0,0 +1,9 @@ +- page_title "Edit", @label.name, "Labels" +%h3 + Edit label + %span.light #{@label.name} +.back-link + = link_to admin_labels_path do + ← To labels list +%hr += render 'form' diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml new file mode 100644 index 00000000000..8b11c28c56e --- /dev/null +++ b/app/views/admin/labels/index.html.haml @@ -0,0 +1,16 @@ +- page_title "Labels" += link_to new_admin_label_path, class: "pull-right btn btn-new" do + New label +%h3.page-title + Labels +%hr + +.labels + - if @labels.present? + %ul.bordered-list.manage-labels-list + = render @labels + = paginate @labels, theme: 'gitlab' + - else + .light-well + .nothing-here-block There are no any labels yet + \ No newline at end of file diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml new file mode 100644 index 00000000000..8d298ad20f7 --- /dev/null +++ b/app/views/admin/labels/new.html.haml @@ -0,0 +1,7 @@ +- page_title "New Label" +%h3 New label +.back-link + = link_to admin_labels_path do + ← To labels list +%hr += render 'form' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 2065be3828a..3fe0127041e 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -57,6 +57,12 @@ %span Service Templates + = nav_link(controller: :labels) do + = link_to admin_labels_path, title: 'Labels', data: {placement: 'right'} do + = icon('tags fw') + %span + Labels + = nav_link(controller: :abuse_reports) do = link_to admin_abuse_reports_path, title: "Abuse reports" do = icon('exclamation-circle fw') diff --git a/config/routes.rb b/config/routes.rb index 920ece518ea..d93b5f936ab 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -201,6 +201,8 @@ Gitlab::Application.routes.draw do resources :services end + resources :labels + root to: 'dashboard#index' end diff --git a/db/migrate/20150902001023_add_template_to_label.rb b/db/migrate/20150902001023_add_template_to_label.rb new file mode 100644 index 00000000000..bd381a97b69 --- /dev/null +++ b/db/migrate/20150902001023_add_template_to_label.rb @@ -0,0 +1,5 @@ +class AddTemplateToLabel < ActiveRecord::Migration + def change + add_column :labels, :template, :boolean, default: false + end +end \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 36568dd4edd..83757a27729 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: 20150824002011) do +ActiveRecord::Schema.define(version: 20150902001023) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,17 @@ ActiveRecord::Schema.define(version: 20150824002011) 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" @@ -36,17 +47,36 @@ ActiveRecord::Schema.define(version: 20150824002011) do t.integer "default_branch_protection", default: 2 t.boolean "twitter_sharing_enabled", default: true 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.integer "default_snippet_visibility" t.text "restricted_signup_domains" + t.boolean "version_check_enabled", default: true t.boolean "user_oauth_applications", default: true t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false + t.text "help_text" t.text "import_sources" end + create_table "approvals", force: true do |t| + t.integer "merge_request_id", null: false + t.integer "user_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "approvers", force: true do |t| + t.integer "target_id", null: false + t.string "target_type" + t.integer "user_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree + add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree + create_table "audit_events", force: true do |t| t.integer "author_id", null: false t.string "type", null: false @@ -119,6 +149,28 @@ ActiveRecord::Schema.define(version: 20150824002011) 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" @@ -186,10 +238,26 @@ ActiveRecord::Schema.define(version: 20150824002011) do t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" + t.boolean "template", default: false end 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 @@ -271,14 +339,15 @@ ActiveRecord::Schema.define(version: 20150824002011) 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 @@ -356,6 +425,14 @@ ActiveRecord::Schema.define(version: 20150824002011) 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" @@ -368,25 +445,30 @@ ActiveRecord::Schema.define(version: 20150824002011) 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.integer "commit_count", default: 0 + t.text "merge_requests_template" + t.boolean "merge_requests_rebase_enabled", default: false + t.boolean "merge_requests_rebase_default", default: true + t.integer "approvals_before_merge", default: 0, null: false + t.boolean "reset_approvals_on_push", default: true + t.integer "commit_count", default: 0 end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree @@ -487,12 +569,12 @@ ActiveRecord::Schema.define(version: 20150824002011) do add_index "tags", ["name"], name: "index_tags_on_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" @@ -500,22 +582,22 @@ ActiveRecord::Schema.define(version: 20150824002011) 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" @@ -524,20 +606,21 @@ ActiveRecord::Schema.define(version: 20150824002011) 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.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.string "public_email", default: "", null: false - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.integer "dashboard", default: 0 + t.datetime "admin_email_unsubscribed_at" + t.integer "project_view", default: 0 end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree @@ -573,6 +656,7 @@ ActiveRecord::Schema.define(version: 20150824002011) do t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false t.boolean "note_events", default: false, null: false + t.integer "group_id" t.boolean "enable_ssl_verification", default: false end -- cgit v1.2.1 From f5ffeac058260de26efdcdae4205804154bc027e Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Sep 2015 17:12:15 +0300 Subject: Create labels in new project --- CHANGELOG | 1 + app/models/project.rb | 9 +++++++++ app/services/projects/create_service.rb | 2 ++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e46fb5d6c1e..276a221971d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ v 8.0.0 (unreleased) - Added Drone CI integration (Kirill Zaitsev) - Refactored service API and added automatically service docs generator (Kirill Zaitsev) - Added web_url key project hook_attrs (Kirill Zaitsev) + - Global Labels that are available to all projects v 7.14.1 - Improve abuse reports management from admin area diff --git a/app/models/project.rb b/app/models/project.rb index 8e33a4b2f0f..bdedd7aa9e0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -401,6 +401,15 @@ class Project < ActiveRecord::Base end end + def create_labels + Label.templates.each do |label| + label = label.dup + label.template = nil + label.project_id = self.id + label.save + end + end + def find_service(list, name) list.find { |service| service.to_param == name } end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index b35aed005da..1bb2462565a 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -87,6 +87,8 @@ module Projects @project.build_missing_services + @project.create_labels + event_service.create_project(@project, current_user) system_hook_service.execute_hooks_for(@project, :create) -- cgit v1.2.1 From 6bd3d72bbdf288ecf2eee718f2943821c1401a22 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Sep 2015 18:44:58 +0300 Subject: added spinach for glabal labels --- app/controllers/admin/labels_controller.rb | 1 - db/schema.rb | 157 ++++++-------------------- features/admin/labels.feature | 38 +++++++ features/steps/admin/labels.rb | 117 +++++++++++++++++++ spec/services/projects/create_service_spec.rb | 8 ++ 5 files changed, 200 insertions(+), 121 deletions(-) create mode 100644 features/admin/labels.feature create mode 100644 features/steps/admin/labels.rb diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb index bcdfe6c80a0..3b070e65d0d 100644 --- a/app/controllers/admin/labels_controller.rb +++ b/app/controllers/admin/labels_controller.rb @@ -1,4 +1,3 @@ - class Admin::LabelsController < Admin::ApplicationController before_action :set_label, only: [:show, :edit, :update, :destroy] diff --git a/db/schema.rb b/db/schema.rb index 83757a27729..55cbd8c293e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -24,17 +24,6 @@ ActiveRecord::Schema.define(version: 20150902001023) 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" @@ -47,36 +36,17 @@ ActiveRecord::Schema.define(version: 20150902001023) do t.integer "default_branch_protection", default: 2 t.boolean "twitter_sharing_enabled", default: true 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.integer "default_snippet_visibility" t.text "restricted_signup_domains" - t.boolean "version_check_enabled", default: true t.boolean "user_oauth_applications", default: true t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false - t.text "help_text" t.text "import_sources" end - create_table "approvals", force: true do |t| - t.integer "merge_request_id", null: false - t.integer "user_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "approvers", force: true do |t| - t.integer "target_id", null: false - t.string "target_type" - t.integer "user_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree - add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree - create_table "audit_events", force: true do |t| t.integer "author_id", null: false t.string "type", null: false @@ -149,28 +119,6 @@ ActiveRecord::Schema.define(version: 20150902001023) 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" @@ -243,21 +191,6 @@ ActiveRecord::Schema.define(version: 20150902001023) 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 @@ -339,15 +272,14 @@ ActiveRecord::Schema.define(version: 20150902001023) 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 @@ -425,14 +357,6 @@ ActiveRecord::Schema.define(version: 20150902001023) 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" @@ -445,30 +369,25 @@ ActiveRecord::Schema.define(version: 20150902001023) 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 "approvals_before_merge", default: 0, null: false - t.boolean "reset_approvals_on_push", 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 @@ -569,12 +488,12 @@ ActiveRecord::Schema.define(version: 20150902001023) do add_index "tags", ["name"], name: "index_tags_on_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" @@ -582,22 +501,22 @@ ActiveRecord::Schema.define(version: 20150902001023) 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" @@ -606,21 +525,20 @@ ActiveRecord::Schema.define(version: 20150902001023) 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.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.datetime "admin_email_unsubscribed_at" - 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 @@ -656,7 +574,6 @@ ActiveRecord::Schema.define(version: 20150902001023) do t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false t.boolean "note_events", default: false, null: false - t.integer "group_id" t.boolean "enable_ssl_verification", default: false end diff --git a/features/admin/labels.feature b/features/admin/labels.feature new file mode 100644 index 00000000000..1af0e700bd4 --- /dev/null +++ b/features/admin/labels.feature @@ -0,0 +1,38 @@ +Feature: Admin Issues Labels + Background: + Given I sign in as an admin + And I have labels: "bug", "feature", "enhancement" + Given I visit admin labels page + + Scenario: I should see labels list + Then I should see label 'bug' + And I should see label 'feature' + + Scenario: I create new label + Given I submit new label 'support' + Then I should see label 'support' + + Scenario: I edit label + Given I visit 'bug' label edit page + When I change label 'bug' to 'fix' + Then I should not see label 'bug' + Then I should see label 'fix' + + Scenario: I remove label + When I remove label 'bug' + Then I should not see label 'bug' + + @javascript + Scenario: I delete all labels + When I delete all labels + Then I should see labels help message + + Scenario: I create a label with invalid color + Given I visit admin new label page + When I submit new label with invalid color + Then I should see label color error message + + Scenario: I create a label that already exists + Given I visit admin new label page + When I submit new label 'bug' + Then I should see label exist error message diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb new file mode 100644 index 00000000000..d64380abf73 --- /dev/null +++ b/features/steps/admin/labels.rb @@ -0,0 +1,117 @@ +class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps + include SharedAuthentication + include SharedProject + include SharedPaths + + step 'I visit \'bug\' label edit page' do + visit edit_admin_label_path(bug_label) + end + + step 'I visit admin new label page' do + visit new_admin_label_path + end + + step 'I visit admin labels page' do + visit admin_labels_path + end + + step 'I remove label \'bug\'' do + page.within "#label_#{bug_label.id}" do + click_link 'Remove' + end + end + + step 'I have labels: "bug", "feature", "enhancement"' do + ["bug", "feature", "enhancement"].each do |title| + Label.create(title: title, template: true) + end + end + + step 'I delete all labels' do + page.within '.labels' do + page.all('.btn-remove').each do |remove| + remove.click + sleep 0.05 + end + end + end + + step 'I should see labels help message' do + page.within '.labels' do + expect(page).to have_content 'There are no any labels yet' + end + end + + step 'I submit new label \'support\'' do + visit new_admin_label_path + fill_in 'Title', with: 'support' + fill_in 'Background Color', with: '#F95610' + click_button 'Save' + end + + step 'I submit new label \'bug\'' do + visit new_admin_label_path + fill_in 'Title', with: 'bug' + fill_in 'Background Color', with: '#F95610' + click_button 'Save' + end + + step 'I submit new label with invalid color' do + visit new_admin_label_path + fill_in 'Title', with: 'support' + fill_in 'Background Color', with: '#12' + click_button 'Save' + end + + step 'I should see label exist error message' do + page.within '.label-form' do + expect(page).to have_content 'Title has already been taken' + end + end + + step 'I should see label color error message' do + page.within '.label-form' do + expect(page).to have_content 'Color is invalid' + end + end + + step 'I should see label \'feature\'' do + page.within '.manage-labels-list' do + expect(page).to have_content 'feature' + end + end + + step 'I should see label \'bug\'' do + page.within '.manage-labels-list' do + expect(page).to have_content 'bug' + end + end + + step 'I should not see label \'bug\'' do + page.within '.manage-labels-list' do + expect(page).not_to have_content 'bug' + end + end + + step 'I should see label \'support\'' do + page.within '.manage-labels-list' do + expect(page).to have_content 'support' + end + end + + step 'I change label \'bug\' to \'fix\'' do + fill_in 'Title', with: 'fix' + fill_in 'Background Color', with: '#F15610' + click_button 'Save' + end + + step 'I should see label \'fix\'' do + page.within '.manage-labels-list' do + expect(page).to have_content 'fix' + end + end + + def bug_label + Label.templates.find_or_create_by(title: 'bug') + end +end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 66cdfd5d758..ff4ed2dd484 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -17,6 +17,14 @@ describe Projects::CreateService do expect(project.services).not_to be_empty end + it 'creates labels on Project creation if there are templates' do + Label.create(title: "bug", template: true) + project = create_project(@user, @opts) + project.reload + + expect(project.labels).not_to be_empty + end + context 'user namespace' do before do @project = create_project(@user, @opts) -- cgit v1.2.1 From 8820785c8fe267789a5c6edf7f4fcb196c48b4a8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 6 Sep 2015 09:42:39 -0700 Subject: Fix emoji URLs in Markdown when relative_url_root is used Also adds the ability to run rspecs with relative_url_defined on the enviornment. For example: RELATIVE_URL_ROOT=/gitlab rspec Closes #1728 --- CHANGELOG | 1 + lib/gitlab/markdown.rb | 2 +- spec/spec_helper.rb | 1 + spec/support/matchers/markdown_matchers.rb | 3 +++ spec/support/relative_url.rb | 8 ++++++++ 5 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 spec/support/relative_url.rb diff --git a/CHANGELOG b/CHANGELOG index 8154d4333d9..329ce74a5d5 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 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) - Prevent anchors from being hidden by header (Stan Hu) - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 097caf67a65..ae5f2544691 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -77,7 +77,7 @@ module Gitlab pipeline: options[:pipeline], # EmojiFilter - asset_root: Gitlab.config.gitlab.url, + asset_root: Gitlab.config.gitlab.base_url, asset_host: Gitlab::Application.config.asset_host, # TableOfContentsFilter diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d0f1873ee2d..0780c4f3203 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,6 +28,7 @@ RSpec.configure do |config| config.include LoginHelpers, type: :feature config.include LoginHelpers, type: :request config.include StubConfiguration + config.include RelativeUrl, type: feature config.include TestEnv config.infer_spec_type_from_file_location! diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 9df226c3af8..7500d0fdf80 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -27,6 +27,9 @@ module MarkdownMatchers match do |actual| expect(actual).to have_selector('img.emoji', count: 10) + + image = actual.at_css('img.emoji') + expect(image['src'].to_s).to start_with(Gitlab.config.gitlab.url + '/assets') end end diff --git a/spec/support/relative_url.rb b/spec/support/relative_url.rb new file mode 100644 index 00000000000..72e3ccce75b --- /dev/null +++ b/spec/support/relative_url.rb @@ -0,0 +1,8 @@ +# Fix route helpers in tests (e.g. root_path, ...) +module RelativeUrl + extend ActiveSupport::Concern + + included do + default_url_options[:script_name] = Rails.application.config.relative_url_root + end +end -- cgit v1.2.1 From 8d59b1ac456575496e0bceb6812c59545b1e9b50 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Sep 2015 13:05:29 +0200 Subject: Do not let NGINX buffer Git HTTP requests Before this change NGINX would convert a chunked HTTP POST (e.g. git push) into a HTTP 1.0 single large POST. This creates an unnecessary delay, and it creates unnecessary memory pressure on gitlab-git-http-server. For the response ('proxy_buffering') I am less sure that NGINX 's buffering behavior is harmful, but it still makes more sense to me not to interfere with gitlab-git-http-server (and the Golang net/http server). --- lib/support/nginx/gitlab | 7 +++++++ lib/support/nginx/gitlab-ssl | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 17f89c8beb6..1cd81ea769f 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -124,6 +124,13 @@ server { proxy_connect_timeout 300; proxy_redirect off; + # Do not buffer Git HTTP responses + proxy_buffering off; + + # Pass chunked request bodies to gitlab-git-http-server as-is + proxy_request_buffering off; + proxy_http_version 1.1; + proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 5ba39fc41a4..83aad321aab 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -171,6 +171,13 @@ server { proxy_connect_timeout 300; proxy_redirect off; + # Do not buffer Git HTTP responses + proxy_buffering off; + + # Pass chunked request bodies to gitlab-git-http-server as-is + proxy_request_buffering off; + proxy_http_version 1.1; + proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Ssl on; -- cgit v1.2.1 From 783791fd08e8144a2ab97307a12df9d2a40e0421 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Sep 2015 14:59:32 +0200 Subject: The good stuff needs NGINX 1.7.11 --- lib/support/nginx/gitlab | 8 +++++--- lib/support/nginx/gitlab-ssl | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 1cd81ea769f..7218a4d2f20 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -127,9 +127,11 @@ server { # Do not buffer Git HTTP responses proxy_buffering off; - # Pass chunked request bodies to gitlab-git-http-server as-is - proxy_request_buffering off; - proxy_http_version 1.1; + # The following settings only work with NGINX 1.7.11 or newer + # + # # Pass chunked request bodies to gitlab-git-http-server as-is + # proxy_request_buffering off; + # proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 83aad321aab..7dabfba87e2 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -174,9 +174,11 @@ server { # Do not buffer Git HTTP responses proxy_buffering off; - # Pass chunked request bodies to gitlab-git-http-server as-is - proxy_request_buffering off; - proxy_http_version 1.1; + # The following settings only work with NGINX 1.7.11 or newer + # + # # Pass chunked request bodies to gitlab-git-http-server as-is + # proxy_request_buffering off; + # proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; -- cgit v1.2.1 From c258e97747f13cdc60ca2765789213292099c87a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 14:42:30 +0100 Subject: Make Help accessible for guests. --- app/controllers/help_controller.rb | 2 ++ app/views/help/show.html.haml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 71831c5380d..ad00948da51 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -1,4 +1,6 @@ class HelpController < ApplicationController + skip_before_action :authenticate_user!, :reject_blocked + layout 'help' def index diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml index 8551496b98a..0398afb4c1d 100644 --- a/app/views/help/show.html.haml +++ b/app/views/help/show.html.haml @@ -1,3 +1,3 @@ - page_title @file.humanize, *@category.split("/").reverse.map(&:humanize) .documentation.wiki - = markdown @markdown.gsub('$your_email', current_user.email) + = markdown @markdown.gsub('$your_email', current_user.try(:email) || "email@example.com") -- cgit v1.2.1 From d92696d39d10d1862e6235742897c94bc85ab8a4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 14:42:39 +0100 Subject: Link to help from signin page bottom. --- app/views/layouts/devise.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 1987bf1592a..95e077c339f 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -31,5 +31,5 @@ .container .footer-links = link_to "Explore", explore_root_path - = link_to "Documentation", "http://doc.gitlab.com/" + = link_to "Help", help_path = link_to "About GitLab", "https://about.gitlab.com/" -- cgit v1.2.1 From 260fcd45209b59ace6b3fd6dcca6c32c2c75a8f1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 14:43:41 +0100 Subject: Consistently case `Back to X` links. --- app/views/layouts/nav/_group.html.haml | 2 +- app/views/layouts/nav/_profile.html.haml | 2 +- app/views/layouts/nav/_project.html.haml | 4 ++-- features/steps/groups.rb | 2 +- features/steps/project/project.rb | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 695ce68a201..f57ec7e13f2 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -3,7 +3,7 @@ = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to Dashboard + Back to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 33fd5fcef6c..729a584457b 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -3,7 +3,7 @@ = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to Dashboard + Back to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 5e7b902622b..1d22a7442e3 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -4,13 +4,13 @@ = link_to group_path(@project.group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to Group + Back to group - else = 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 Dashboard + Back to dashboard %li.separate-item diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 18a1c4d32ce..a5c2eed4ddd 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -6,7 +6,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include Select2Helper step 'I should see back to dashboard button' do - expect(page).to have_content 'Back to Dashboard' + expect(page).to have_content 'Back to dashboard' end step 'gitlab user "Mike"' do diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 0404fd5e594..079a190e356 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -124,10 +124,10 @@ class Spinach::Features::Project < Spinach::FeatureSteps end step 'I should see back to dashboard button' do - expect(page).to have_content 'Back to Dashboard' + expect(page).to have_content 'Back to dashboard' end step 'I should see back to group button' do - expect(page).to have_content 'Back to Group' + expect(page).to have_content 'Back to group' end end -- cgit v1.2.1 From 5d785457db3017a17722314a52433543dd925164 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 14:49:20 +0100 Subject: Clean up overlap between dashboard and explore. - Split up SnippetsController into separate dashboard and explore sections. - Use consistent page titles, header titles and sidebars between dashboard and explore sections when signed in or not. --- app/controllers/dashboard/projects_controller.rb | 15 ++++++++ app/controllers/dashboard/snippets_controller.rb | 10 ++++++ app/controllers/dashboard_controller.rb | 19 ---------- app/controllers/explore/application_controller.rb | 2 ++ app/controllers/explore/groups_controller.rb | 3 -- app/controllers/explore/projects_controller.rb | 3 -- app/controllers/explore/snippets_controller.rb | 6 ++++ app/controllers/groups_controller.rb | 4 +++ app/controllers/projects_controller.rb | 4 +++ app/controllers/root_controller.rb | 5 +-- app/controllers/snippets_controller.rb | 8 ++--- app/views/dashboard/_groups_head.html.haml | 4 +-- app/views/dashboard/_projects.html.haml | 10 ------ app/views/dashboard/_projects_head.html.haml | 4 +-- app/views/dashboard/_snippets_head.html.haml | 7 ++++ app/views/dashboard/activity.html.haml | 4 ++- app/views/dashboard/projects/_projects.html.haml | 10 ++++++ app/views/dashboard/projects/index.atom.builder | 12 +++++++ app/views/dashboard/projects/index.html.haml | 16 +++++++++ app/views/dashboard/projects/starred.html.haml | 5 +-- app/views/dashboard/show.atom.builder | 12 ------- app/views/dashboard/show.html.haml | 14 -------- app/views/dashboard/snippets/index.html.haml | 38 ++++++++++++++++++++ app/views/explore/_head.html.haml | 6 ++++ app/views/explore/groups/index.html.haml | 8 +++-- app/views/explore/projects/index.html.haml | 7 +++- app/views/explore/projects/starred.html.haml | 6 +++- app/views/explore/projects/trending.html.haml | 12 +++---- app/views/explore/snippets/index.html.haml | 18 ++++++++++ app/views/layouts/_page.html.haml | 4 +++ app/views/layouts/explore.html.haml | 6 +--- app/views/layouts/nav/_dashboard.html.haml | 44 +++++++++++------------ app/views/layouts/nav/_explore.html.haml | 21 +++++++++++ app/views/layouts/snippets.html.haml | 7 +--- app/views/projects/activity.html.haml | 1 + app/views/snippets/_head.html.haml | 7 ---- app/views/snippets/current_user_index.html.haml | 36 ------------------- app/views/snippets/index.html.haml | 24 ++++++------- app/views/snippets/show.html.haml | 4 +-- app/views/snippets/user_index.html.haml | 13 ------- config/routes.rb | 20 ++++++----- features/steps/snippets/user.rb | 2 +- 42 files changed, 259 insertions(+), 202 deletions(-) create mode 100644 app/controllers/dashboard/snippets_controller.rb create mode 100644 app/controllers/explore/snippets_controller.rb delete mode 100644 app/views/dashboard/_projects.html.haml create mode 100644 app/views/dashboard/_snippets_head.html.haml create mode 100644 app/views/dashboard/projects/_projects.html.haml create mode 100644 app/views/dashboard/projects/index.atom.builder create mode 100644 app/views/dashboard/projects/index.html.haml delete mode 100644 app/views/dashboard/show.atom.builder delete mode 100644 app/views/dashboard/show.html.haml create mode 100644 app/views/dashboard/snippets/index.html.haml create mode 100644 app/views/explore/_head.html.haml create mode 100644 app/views/explore/snippets/index.html.haml create mode 100644 app/views/layouts/nav/_explore.html.haml delete mode 100644 app/views/snippets/_head.html.haml delete mode 100644 app/views/snippets/current_user_index.html.haml delete mode 100644 app/views/snippets/user_index.html.haml diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index da96171e885..467d0f81aca 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,6 +1,21 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController before_action :event_filter + def index + @projects = current_user.authorized_projects.sorted_by_activity.non_archived + @projects = @projects.includes(:namespace) + @last_push = current_user.recent_push + + respond_to do |format| + format.html + format.atom do + event_filter + load_events + render layout: false + end + end + end + def starred @projects = current_user.starred_projects @projects = @projects.includes(:namespace, :forked_from_project, :tags) diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb new file mode 100644 index 00000000000..f4354c6d8ca --- /dev/null +++ b/app/controllers/dashboard/snippets_controller.rb @@ -0,0 +1,10 @@ +class Dashboard::SnippetsController < Dashboard::ApplicationController + def index + @snippets = SnippetsFinder.new.execute(current_user, + filter: :by_user, + user: current_user, + scope: params[:scope] + ) + @snippets = @snippets.page(params[:page]).per(PER_PAGE) + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 2bc2e5e58f5..4ebb3d7276e 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,23 +1,8 @@ class DashboardController < Dashboard::ApplicationController - before_action :load_projects, except: :activity before_action :event_filter, only: :activity respond_to :html - def show - @projects = @projects.includes(:namespace) - @last_push = current_user.recent_push - - respond_to do |format| - format.html - format.atom do - event_filter - load_events - render layout: false - end - end - end - def merge_requests @merge_requests = get_merge_requests_collection @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @@ -50,10 +35,6 @@ class DashboardController < Dashboard::ApplicationController protected - def load_projects - @projects = current_user.authorized_projects.sorted_by_activity.non_archived - end - def load_events project_ids = if params[:filter] == "starred" diff --git a/app/controllers/explore/application_controller.rb b/app/controllers/explore/application_controller.rb index 4b275033d26..461fc059a3c 100644 --- a/app/controllers/explore/application_controller.rb +++ b/app/controllers/explore/application_controller.rb @@ -1,3 +1,5 @@ class Explore::ApplicationController < ApplicationController + skip_before_action :authenticate_user!, :reject_blocked + layout 'explore' end diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 55cda0cff17..9575a87ee41 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,7 +1,4 @@ class Explore::GroupsController < Explore::ApplicationController - skip_before_action :authenticate_user!, - :reject_blocked, :set_current_user_for_observers - def index @groups = GroupsFinder.new.execute(current_user) @groups = @groups.search(params[:search]) if params[:search].present? diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 6c733c1ae4d..a5aeaed66c5 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,7 +1,4 @@ class Explore::ProjectsController < Explore::ApplicationController - skip_before_action :authenticate_user!, - :reject_blocked - def index @projects = ProjectsFinder.new.execute(current_user) @tags = @projects.tags_on(:tags) diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb new file mode 100644 index 00000000000..b70ac51d06e --- /dev/null +++ b/app/controllers/explore/snippets_controller.rb @@ -0,0 +1,6 @@ +class Explore::SnippetsController < Explore::ApplicationController + def index + @snippets = SnippetsFinder.new.execute(current_user, filter: :all) + @snippets = @snippets.page(params[:page]).per(PER_PAGE) + end +end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 279c6ef0f4d..85bf44ee9a5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -14,6 +14,10 @@ class GroupsController < Groups::ApplicationController layout :determine_layout + def index + redirect_to (current_user ? dashboard_groups_path : explore_groups_path) + end + def new @group = Group.new end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index dafc11d0707..540bfa9ac07 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -10,6 +10,10 @@ class ProjectsController < ApplicationController layout :determine_layout + def index + redirect_to (current_user ? root_path : explore_root_path) + end + def new @project = Project.new end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index fdfe00dc135..a3aa86de5b4 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -6,10 +6,10 @@ # # For users who haven't customized the setting, we simply delegate to # `DashboardController#show`, which is the default. -class RootController < DashboardController +class RootController < Dashboard::ProjectsController before_action :redirect_to_custom_dashboard, only: [:show] - def show + def index super end @@ -20,6 +20,7 @@ class RootController < DashboardController case current_user.dashboard when 'stars' + flash.keep redirect_to starred_dashboard_projects_path else return diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 8e7e45c781f..f5938da936f 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -24,13 +24,9 @@ class SnippetsController < ApplicationController scope: params[:scope] }). page(params[:page]).per(PER_PAGE) - if @user == current_user - render 'current_user_index' - else - render 'user_index' - end + render 'index' else - @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(PER_PAGE) + redirect_to (current_user ? dashboard_snippets_path : explore_snippets_path) end end diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index dcd6c97d44d..64bd356f546 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -1,7 +1,7 @@ %ul.center-top-menu - = nav_link(page: [dashboard_groups_path]) do + = nav_link(page: dashboard_groups_path) do = link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do Your Groups - = nav_link(page: [explore_groups_path]) do + = nav_link(page: explore_groups_path) do = link_to explore_groups_path, title: 'Explore groups', data: {placement: 'bottom'} do Explore Groups diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml deleted file mode 100644 index ef9b9ce756a..00000000000 --- a/app/views/dashboard/_projects.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -.projects-list-holder - .projects-search-form - .input-group - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' - - if current_user.can_create_project? - %span.input-group-btn - = link_to new_project_path, class: 'btn btn-success' do - New project - - = render 'shared/projects/list', projects: @projects diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 13a5eae3cdc..ed480b8caf8 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,6 +1,6 @@ %ul.center-top-menu - = nav_link(path: ['dashboard#show', 'root#show']) do - = link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = nav_link(path: ['projects#index', 'root#index']) do + = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do Your Projects = nav_link(page: starred_dashboard_projects_path) do = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml new file mode 100644 index 00000000000..0ae62d6f1b6 --- /dev/null +++ b/app/views/dashboard/_snippets_head.html.haml @@ -0,0 +1,7 @@ +%ul.center-top-menu + = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do + = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do + Your Snippets + = nav_link(page: explore_snippets_path) do + = link_to explore_snippets_path, title: 'Explore snippets', data: {placement: 'right'} do + Explore Snippets diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml index 3e24338af64..aa57df14c23 100644 --- a/app/views/dashboard/activity.html.haml +++ b/app/views/dashboard/activity.html.haml @@ -1,8 +1,10 @@ = content_for :meta_tags do - if current_user - = auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity") + = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity") +- page_title "Activity" - header_title "Activity", activity_dashboard_path + = render 'dashboard/activity_head' %section.activities diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml new file mode 100644 index 00000000000..ef9b9ce756a --- /dev/null +++ b/app/views/dashboard/projects/_projects.html.haml @@ -0,0 +1,10 @@ +.projects-list-holder + .projects-search-form + .input-group + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' + - if current_user.can_create_project? + %span.input-group-btn + = link_to new_project_path, class: 'btn btn-success' do + New project + + = render 'shared/projects/list', projects: @projects diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder new file mode 100644 index 00000000000..d2c51486841 --- /dev/null +++ b/app/views/dashboard/projects/index.atom.builder @@ -0,0 +1,12 @@ +xml.instruct! +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do + xml.title "Activity" + xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" + xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" + xml.id dashboard_projects_url + xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + + @events.each do |event| + event_to_atom(xml, event) + end +end diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml new file mode 100644 index 00000000000..7a16b811f6b --- /dev/null +++ b/app/views/dashboard/projects/index.html.haml @@ -0,0 +1,16 @@ += content_for :meta_tags do + - if current_user + = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity") + +- page_title "Projects" +- header_title "Projects", root_path + += render 'dashboard/projects_head' + +- if @last_push + = render "events/event_last_push", event: @last_push + +- if @projects.any? + = render 'projects' +- else + = render "zero_authorized_projects" diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 2fd7a1cf16c..339362701d4 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -1,9 +1,10 @@ - page_title "Starred Projects" -- header_title "Projects", (current_user ? root_path : explore_root_path) +- header_title "Projects", projects_path + = render 'dashboard/projects_head' - if @projects.any? - = render 'dashboard/projects' + = render 'projects' - else %h3 You don't have starred projects yet %p.slead Visit project page and press on star icon and it will appear on this page. diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder deleted file mode 100644 index e9a612231d5..00000000000 --- a/app/views/dashboard/show.atom.builder +++ /dev/null @@ -1,12 +0,0 @@ -xml.instruct! -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do - xml.title "Activity" - xml.link href: dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" - xml.link href: dashboard_url, rel: "alternate", type: "text/html" - xml.id dashboard_url - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? - - @events.each do |event| - event_to_atom(xml, event) - end -end diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml deleted file mode 100644 index 1d5324e0d72..00000000000 --- a/app/views/dashboard/show.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -= content_for :meta_tags do - - if current_user - = auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity") - -- header_title "Projects", (current_user ? root_path : explore_root_path) -= render 'dashboard/projects_head' - -- if @last_push - = render "events/event_last_push", event: @last_push - -- if @projects.any? - = render 'projects' -- else - = render "zero_authorized_projects" diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml new file mode 100644 index 00000000000..d3908062f43 --- /dev/null +++ b/app/views/dashboard/snippets/index.html.haml @@ -0,0 +1,38 @@ +- page_title "Snippets" +- header_title "Snippets", dashboard_snippets_path + += render 'dashboard/snippets_head' + +.gray-content-block + .pull-right + = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do + Add new snippet + + .oneline + Share code pastes with others out of git repository + +%ul.nav.nav-tabs.prepend-top-20 + = nav_tab :scope, nil do + = link_to dashboard_snippets_path do + All + %span.badge + = current_user.snippets.count + = nav_tab :scope, 'are_private' do + = link_to dashboard_snippets_path(scope: 'are_private') do + Private + %span.badge + = current_user.snippets.are_private.count + = nav_tab :scope, 'are_internal' do + = link_to dashboard_snippets_path(scope: 'are_internal') do + Internal + %span.badge + = current_user.snippets.are_internal.count + = nav_tab :scope, 'are_public' do + = link_to dashboard_snippets_path(scope: 'are_public') do + Public + %span.badge + = current_user.snippets.are_public.count + +.my-snippets + = render 'snippets/snippets' + diff --git a/app/views/explore/_head.html.haml b/app/views/explore/_head.html.haml new file mode 100644 index 00000000000..d8a57560788 --- /dev/null +++ b/app/views/explore/_head.html.haml @@ -0,0 +1,6 @@ +.explore-title + %h3 + Explore GitLab + %p.lead + Discover projects, groups and snippets. Share your projects with others + %br diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index e8a6752de8c..83d4d321c83 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -1,7 +1,11 @@ -- page_title "Groups" -- header_title "Groups", (current_user ? dashboard_groups_path : explore_groups_path) +- page_title "Groups" +- header_title "Groups", dashboard_groups_path + - if current_user = render 'dashboard/groups_head' +- else + = render 'explore/head' + .gray-content-block.clearfix .pull-left = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 9df5b3830a8..67e38ca3127 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,6 +1,11 @@ -- page_title "Projects" +- page_title "Projects" +- header_title "Projects", root_path + - if current_user = render 'dashboard/projects_head' +- else + = render 'explore/head' + .gray-content-block.clearfix = render 'filter' = render 'projects', projects: @projects diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index a9df32f3d7d..596cb0a96cd 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,6 +1,10 @@ -- page_title "Starred Projects" +- page_title "Projects" +- header_title "Projects", root_path + - if current_user = render 'dashboard/projects_head' +- else + = render 'explore/head' .explore-trending-block .gray-content-block diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index c1ef06f6cdb..5ea6d81c5b9 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,13 +1,11 @@ -- page_title "Trending Projects" +- page_title "Projects" +- header_title "Projects", root_path + - if current_user = render 'dashboard/projects_head' - else - .explore-title - %h3 - Explore GitLab - %p.lead - Discover projects and groups. Share your projects with others - %br + = render 'explore/head' + .explore-trending-block .gray-content-block .pull-right diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml new file mode 100644 index 00000000000..7e4fa7d4873 --- /dev/null +++ b/app/views/explore/snippets/index.html.haml @@ -0,0 +1,18 @@ +- page_title "Snippets" +- header_title "Snippets", snippets_path + +- if current_user + = render 'dashboard/snippets_head' +- else + = render 'explore/head' + +.gray-content-block + - if current_user + .pull-right + = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do + Add new snippet + + .oneline + Public snippets created by you and other users are listed here + += render 'snippets/snippets' diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index c1746676ae2..68e5da571e6 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -6,10 +6,14 @@ = brand_header_logo .gitlab-text-container %h3 GitLab + - if defined?(sidebar) && sidebar = render "layouts/nav/#{sidebar}" - elsif current_user = render 'layouts/nav/dashboard' + - else + = render 'layouts/nav/explore' + .collapse-nav = render partial: 'layouts/collapse_button' - if current_user diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml index 9098554e6f0..df65792be73 100644 --- a/app/views/layouts/explore.html.haml +++ b/app/views/layouts/explore.html.haml @@ -1,9 +1,5 @@ - page_title "Explore" -- if current_user - - unless @header_title - - header_title "Projects", (current_user ? root_path : explore_root_path) -- else +- unless current_user - header_title "Explore GitLab", explore_root_path -- sidebar "dashboard" = render template: "layouts/application" diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 0cf1c3d5d27..68f816b86af 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,31 +1,30 @@ %ul.nav.nav-sidebar - = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do - = link_to (current_user ? root_path : explore_root_path), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do + = link_to root_path, title: 'Projects', data: {placement: 'right'} do = icon('home fw') %span Projects = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, title: 'Activity', data: {placement: 'right'} do + = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity', data: {placement: 'right'} do = icon('dashboard fw') %span Activity = nav_link(controller: :groups) do - = link_to (current_user ? dashboard_groups_path : explore_groups_path), title: 'Groups', data: {placement: 'right'} do + = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do = icon('group fw') %span Groups - - if current_user - = nav_link(controller: :milestones) do - = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do - = icon('clock-o fw') - %span - Milestones - = nav_link(path: 'dashboard#issues') do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do - = icon('exclamation-circle fw') - %span - Issues - %span.count= current_user.assigned_issues.opened.count + = nav_link(controller: :milestones) do + = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'dashboard#issues') do + = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = icon('exclamation-circle fw') + %span + Issues + %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do = icon('tasks fw') @@ -33,16 +32,15 @@ Merge Requests %span.count= current_user.assigned_merge_requests.opened.count = nav_link(controller: :snippets) do - = link_to (current_user ? user_snippets_path(current_user) : snippets_path), title: 'Your snippets', data: {placement: 'right'} do + = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do = icon('clipboard fw') %span Snippets - - if current_user - = nav_link(controller: :profile) do - = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do - = icon('user fw') - %span - Profile Settings + = nav_link(controller: :profile) do + = link_to profile_path, title: 'Profile settings', data: {placement: 'bottom'} do + = icon('user fw') + %span + Profile Settings = nav_link(controller: :help) do = link_to help_path, title: 'Help', data: {placement: 'right'} do = icon('question-circle fw') diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml new file mode 100644 index 00000000000..21e565972a7 --- /dev/null +++ b/app/views/layouts/nav/_explore.html.haml @@ -0,0 +1,21 @@ +%ul.nav.nav-sidebar + = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do + = link_to explore_root_path, title: 'Projects', data: {placement: 'right'} do + = icon('home fw') + %span + Projects + = nav_link(controller: :groups) do + = link_to explore_groups_path, title: 'Groups', data: {placement: 'right'} do + = icon('group fw') + %span + Groups + = nav_link(controller: :snippets) do + = link_to explore_snippets_path, title: 'Snippets', data: {placement: 'right'} do + = icon('clipboard fw') + %span + Snippets + = nav_link(controller: :help) do + = link_to help_path, title: 'Help', data: {placement: 'right'} do + = icon('question-circle fw') + %span + Help diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index d9c90d4fcef..02ca3ee7a28 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -1,8 +1,3 @@ -- page_title 'Snippets' -- if current_user - - header_title "Snippets", user_snippets_path(current_user) -- else - - header_title 'Snippets', snippets_path -- sidebar "dashboard" +- header_title "Snippets", snippets_path = render template: "layouts/application" diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index 65674913bb0..5f6e5f3b644 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1 +1,2 @@ +- page_title "Activity" = render 'projects/activity' diff --git a/app/views/snippets/_head.html.haml b/app/views/snippets/_head.html.haml deleted file mode 100644 index 0adf6b91f2c..00000000000 --- a/app/views/snippets/_head.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%ul.center-top-menu - = nav_link(page: user_snippets_path(current_user), html_options: {class: 'home'}) do - = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do - Your Snippets - = nav_link(page: snippets_path) do - = link_to snippets_path, title: 'Explore snippets', data: {placement: 'right'} do - Explore Snippets diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml deleted file mode 100644 index d704407c4dd..00000000000 --- a/app/views/snippets/current_user_index.html.haml +++ /dev/null @@ -1,36 +0,0 @@ -- page_title "Your Snippets" -= render 'head' - -.gray-content-block - .pull-right - = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet - - .oneline - Share code pastes with others out of git repository - -%ul.nav.nav-tabs.prepend-top-20 - = nav_tab :scope, nil do - = link_to user_snippets_path(@user) do - All - %span.badge - = @user.snippets.count - = nav_tab :scope, 'are_private' do - = link_to user_snippets_path(@user, scope: 'are_private') do - Private - %span.badge - = @user.snippets.are_private.count - = nav_tab :scope, 'are_internal' do - = link_to user_snippets_path(@user, scope: 'are_internal') do - Internal - %span.badge - = @user.snippets.are_internal.count - = nav_tab :scope, 'are_public' do - = link_to user_snippets_path(@user, scope: 'are_public') do - Public - %span.badge - = @user.snippets.are_public.count - -.my-snippets - = render 'snippets' - diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index 3b62dd2a6e1..7e4918a6085 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -1,15 +1,13 @@ -- page_title "Public Snippets" -- if current_user - = render 'head' - -.gray-content-block - - if current_user - .pull-right - = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet - - .oneline - Public snippets created by you and other users are listed here +- page_title "By #{@user.name}", "Snippets" + +%ol.breadcrumb + %li + = link_to snippets_path do + Snippets + %li + = @user.name + .pull-right.hidden-xs + = link_to user_path(@user) do + #{@user.name} profile page = render 'snippets' - diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index aed00f9caeb..97374e073dc 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -20,10 +20,10 @@ .back-link - if @snippet.author == current_user - = link_to user_snippets_path(current_user) do + = link_to dashboard_snippets_path do ← your snippets - else - = link_to snippets_path do + = link_to explore_snippets_path do ← explore snippets .file-holder diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml deleted file mode 100644 index 7af5352da34..00000000000 --- a/app/views/snippets/user_index.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- page_title "Snippets", @user.name - -%ol.breadcrumb - %li - = link_to snippets_path do - Snippets - %li - = @user.name - .pull-right.hidden-xs - = link_to user_path(@user) do - #{@user.name} profile page - -= render 'snippets' diff --git a/config/routes.rb b/config/routes.rb index 25c286b3083..720aee2d2ac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,6 +134,7 @@ Gitlab::Application.routes.draw do end resources :groups, only: [:index] + resources :snippets, only: [:index] root to: 'projects#trending' end @@ -252,24 +253,25 @@ Gitlab::Application.routes.draw do # # Dashboard Area # - resource :dashboard, controller: 'dashboard', only: [:show] do - member do - get :issues - get :merge_requests - get :activity - end + resource :dashboard, controller: 'dashboard', only: [] do + get :issues + get :merge_requests + get :activity scope module: :dashboard do resources :milestones, only: [:index, :show] resources :groups, only: [:index] + resources :snippets, only: [:index] - resources :projects, only: [] do + resources :projects, only: [:index] do collection do get :starred end end end + + root to: "dashboard/projects#index" end # @@ -293,7 +295,7 @@ Gitlab::Application.routes.draw do end end - resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create] + resources :projects, constraints: { id: /[^\/]+/ }, only: [:index, :new, :create] devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords, sessions: :sessions, confirmations: :confirmations } @@ -301,7 +303,7 @@ Gitlab::Application.routes.draw do get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error end - root to: "root#show" + root to: "root#index" # # Project Area diff --git a/features/steps/snippets/user.rb b/features/steps/snippets/user.rb index 007fcb2893f..dea3256229f 100644 --- a/features/steps/snippets/user.rb +++ b/features/steps/snippets/user.rb @@ -4,7 +4,7 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps include SharedSnippet step 'I visit my snippets page' do - visit user_snippets_path(current_user) + visit dashboard_snippets_path end step 'I should see "Personal snippet one" in snippets' do -- cgit v1.2.1 From 1489d225d6763b0fdc3f418692e646587e06938a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 15:14:14 +0100 Subject: Move partial to right place and fix tests. --- app/controllers/groups_controller.rb | 2 +- app/controllers/projects_controller.rb | 2 +- app/controllers/snippets_controller.rb | 2 +- .../dashboard/_zero_authorized_projects.html.haml | 53 ---------------------- .../projects/_zero_authorized_projects.html.haml | 53 ++++++++++++++++++++++ spec/controllers/root_controller_spec.rb | 8 ++-- spec/routing/routing_spec.rb | 6 +-- 7 files changed, 63 insertions(+), 63 deletions(-) delete mode 100644 app/views/dashboard/_zero_authorized_projects.html.haml create mode 100644 app/views/dashboard/projects/_zero_authorized_projects.html.haml diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 85bf44ee9a5..486c6b2819c 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -15,7 +15,7 @@ class GroupsController < Groups::ApplicationController layout :determine_layout def index - redirect_to (current_user ? dashboard_groups_path : explore_groups_path) + redirect_to(current_user ? dashboard_groups_path : explore_groups_path) end def new diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 540bfa9ac07..0bbaa93b0df 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -11,7 +11,7 @@ class ProjectsController < ApplicationController layout :determine_layout def index - redirect_to (current_user ? root_path : explore_root_path) + redirect_to(current_user ? root_path : explore_root_path) end def new diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index f5938da936f..9f9f9a92f11 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -26,7 +26,7 @@ class SnippetsController < ApplicationController render 'index' else - redirect_to (current_user ? dashboard_snippets_path : explore_snippets_path) + redirect_to(current_user ? dashboard_snippets_path : explore_snippets_path) end end diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml deleted file mode 100644 index 4e7d6639727..00000000000 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ /dev/null @@ -1,53 +0,0 @@ -- publicish_project_count = Project.publicish(current_user).count -%h3.page-title Welcome to GitLab! -%p.light Self hosted Git management application. -%hr -%div - .dashboard-intro-icon - %i.fa.fa-bookmark-o - .dashboard-intro-text - %p.slead - You don't have access to any projects right now. - %br - - if current_user.can_create_project? - You can create up to - %strong= pluralize(current_user.projects_limit, "project") + "." - - else - If you are added to a project, it will be displayed here. - - - if current_user.can_create_project? - .link_holder - = link_to new_project_path, class: "btn btn-new" do - %i.fa.fa-plus - New Project - -- if current_user.can_create_group? - %hr - %div - .dashboard-intro-icon - %i.fa.fa-users - .dashboard-intro-text - %p.slead - You can create a group for several dependent projects. - %br - Groups are the best way to manage projects and members. - .link_holder - = link_to new_group_path, class: "btn btn-new" do - %i.fa.fa-plus - New Group - --if publicish_project_count > 0 - %hr - %div - .dashboard-intro-icon - %i.fa.fa-globe - .dashboard-intro-text - %p.slead - There are - %strong= publicish_project_count - public projects on this server. - %br - Public projects are an easy way to allow everyone to have read-only access. - .link_holder - = link_to trending_explore_projects_path, class: "btn btn-new" do - Browse public projects diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml new file mode 100644 index 00000000000..4e7d6639727 --- /dev/null +++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml @@ -0,0 +1,53 @@ +- publicish_project_count = Project.publicish(current_user).count +%h3.page-title Welcome to GitLab! +%p.light Self hosted Git management application. +%hr +%div + .dashboard-intro-icon + %i.fa.fa-bookmark-o + .dashboard-intro-text + %p.slead + You don't have access to any projects right now. + %br + - if current_user.can_create_project? + You can create up to + %strong= pluralize(current_user.projects_limit, "project") + "." + - else + If you are added to a project, it will be displayed here. + + - if current_user.can_create_project? + .link_holder + = link_to new_project_path, class: "btn btn-new" do + %i.fa.fa-plus + New Project + +- if current_user.can_create_group? + %hr + %div + .dashboard-intro-icon + %i.fa.fa-users + .dashboard-intro-text + %p.slead + You can create a group for several dependent projects. + %br + Groups are the best way to manage projects and members. + .link_holder + = link_to new_group_path, class: "btn btn-new" do + %i.fa.fa-plus + New Group + +-if publicish_project_count > 0 + %hr + %div + .dashboard-intro-icon + %i.fa.fa-globe + .dashboard-intro-text + %p.slead + There are + %strong= publicish_project_count + public projects on this server. + %br + Public projects are an easy way to allow everyone to have read-only access. + .link_holder + = link_to trending_explore_projects_path, class: "btn btn-new" do + Browse public projects diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index abbbf6855fc..64dfe8f34e3 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe RootController do - describe 'GET show' do + describe 'GET index' do context 'with a user' do let(:user) { create(:user) } @@ -16,15 +16,15 @@ describe RootController do end it 'redirects to their specified dashboard' do - get :show + get :index expect(response).to redirect_to starred_dashboard_projects_path end end context 'who uses the default dashboard setting' do it 'renders the default dashboard' do - get :show - expect(response).to render_template 'dashboard/show' + get :index + expect(response).to render_template 'dashboard/projects/index' end end end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index dd045826692..dfa18f69e05 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -206,7 +206,7 @@ end # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests describe DashboardController, "routing" do it "to #index" do - expect(get("/dashboard")).to route_to('dashboard#show') + expect(get("/dashboard")).to route_to('dashboard/projects#index') end it "to #issues" do @@ -220,8 +220,8 @@ end # root / root#show describe RootController, 'routing' do - it 'to #show' do - expect(get('/')).to route_to('root#show') + it 'to #index' do + expect(get('/')).to route_to('root#index') end end -- 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 f87f6480b2ff6a80b50aa275f9c5d60c30d75b40 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Sep 2015 17:14:02 +0100 Subject: Use new routing helper --- app/controllers/invites_controller.rb | 4 ++-- app/controllers/projects/project_members_controller.rb | 2 +- app/controllers/projects_controller.rb | 2 +- app/views/dashboard/_activities.html.haml | 2 +- features/steps/shared/paths.rb | 2 +- spec/features/atom/dashboard_spec.rb | 4 ++-- spec/features/profiles/preferences_spec.rb | 2 +- spec/features/security/dashboard_access_spec.rb | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index eb3c8233530..8ef10a17f55 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -24,7 +24,7 @@ class InvitesController < ApplicationController path = if current_user - dashboard_path + dashboard_projects_path else new_user_session_path end @@ -73,7 +73,7 @@ class InvitesController < ApplicationController path = group_path(group) else label = "who knows what" - path = dashboard_path + path = dashboard_projects_path end [label, path] diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index b82b6f45d59..cf73bc01c8f 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -78,7 +78,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController @project.project_members.find_by(user_id: current_user).destroy respond_to do |format| - format.html { redirect_to dashboard_path } + format.html { redirect_to dashboard_projects_path } format.js { render nothing: true } end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0bbaa93b0df..f4d1a828aab 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -109,7 +109,7 @@ class ProjectsController < ApplicationController if request.referer.include?('/admin') redirect_to admin_namespaces_projects_path else - redirect_to dashboard_path + redirect_to dashboard_projects_path end rescue Projects::DestroyService::DestroyError => ex redirect_to edit_project_path(@project), alert: ex.message diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml index 1db56542afd..19d919f9b6a 100644 --- a/app/views/dashboard/_activities.html.haml +++ b/app/views/dashboard/_activities.html.haml @@ -5,7 +5,7 @@ - if current_user %ul.nav.nav-pills.event_filter.pull-right %li.pull-right - = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do + = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do %i.fa.fa-rss = render 'shared/event_filter' diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index b4deccb6520..2ec126e1e94 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -68,7 +68,7 @@ module SharedPaths # ---------------------------------------- step 'I visit dashboard page' do - visit dashboard_path + visit dashboard_projects_path end step 'I visit dashboard activity page' do diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb index ad157d742ff..f81a3c117ff 100644 --- a/spec/features/atom/dashboard_spec.rb +++ b/spec/features/atom/dashboard_spec.rb @@ -6,7 +6,7 @@ describe "Dashboard Feed", feature: true do context "projects atom feed via private token" do it "should render projects atom feed" do - visit dashboard_path(:atom, private_token: user.private_token) + visit dashboard_projects_path(:atom, private_token: user.private_token) expect(body).to have_selector('feed title') end end @@ -20,7 +20,7 @@ describe "Dashboard Feed", feature: true do project.team << [user, :master] issue_event(issue, user) note_event(note, user) - visit dashboard_path(:atom, private_token: user.private_token) + visit dashboard_projects_path(:atom, private_token: user.private_token) end it "should have issue opened event" do diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb index 9bc6145dda4..8f645438cff 100644 --- a/spec/features/profiles/preferences_spec.rb +++ b/spec/features/profiles/preferences_spec.rb @@ -70,7 +70,7 @@ describe 'Profile > Preferences', feature: true do expect(page.current_path).to eq starred_dashboard_projects_path click_link 'Your Projects' - expect(page.current_path).to eq dashboard_path + expect(page.current_path).to eq dashboard_projects_path end end diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb index c38cddbb904..03019139415 100644 --- a/spec/features/security/dashboard_access_spec.rb +++ b/spec/features/security/dashboard_access_spec.rb @@ -4,7 +4,7 @@ describe "Dashboard access", feature: true do include AccessMatchers describe "GET /dashboard" do - subject { dashboard_path } + subject { dashboard_projects_path } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } -- cgit v1.2.1 From 983a102bd028bf7a6335b6674059eb004d4171f3 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Tue, 8 Sep 2015 12:28:28 -0500 Subject: Added meta tag for referrer, so that only the origin is sent to third party sites, instead of the entire URL, thus avoiding the leak of sensitive information like password reset tokens. --- app/views/layouts/_head.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 397649dacf8..c3b137e3ddf 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -3,6 +3,7 @@ %meta{charset: "utf-8"} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} %meta{content: "GitLab Community Edition", name: "description"} + %meta{name: 'referrer', content: 'origin'} %title= page_title -- cgit v1.2.1 From 4d6f8a6aee345d12d81f03f8dc1dfdfc2e3bd777 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Sep 2015 13:31:54 -0400 Subject: Bump gitlab-shell to v2.6.5 --- GITLAB_SHELL_VERSION | 2 +- doc/install/installation.md | 2 +- doc/update/6.x-or-7.x-to-7.14.md | 4 ++-- doc/update/7.13-to-7.14.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 2714f5313ae..57cf282ebbc 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.4 +2.6.5 diff --git a/doc/install/installation.md b/doc/install/installation.md index 52031e9b9a1..ee13b0f2537 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -299,7 +299,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.3] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.5] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md index a1488474f60..5bc1f84270a 100644 --- a/doc/update/6.x-or-7.x-to-7.14.md +++ b/doc/update/6.x-or-7.x-to-7.14.md @@ -127,7 +127,7 @@ sudo apt-get install nodejs ```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 ``` ## 7. Install libs, migrations, etc. @@ -167,7 +167,7 @@ git diff 6-0-stable:config/gitlab.yml.example 7.14-stable:config/gitlab.yml.exam * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/unicorn.rb.example but with your settings. -* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings. +* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.5/config.yml.example but with your settings. * Copy rack attack middleware config ```bash diff --git a/doc/update/7.13-to-7.14.md b/doc/update/7.13-to-7.14.md index 7c2d3f4498a..6dd9727fb49 100644 --- a/doc/update/7.13-to-7.14.md +++ b/doc/update/7.13-to-7.14.md @@ -63,7 +63,7 @@ sudo -u git -H git checkout 7-14-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 libs, migrations, etc. -- cgit v1.2.1 From e156f42079ebf8247b6a39fa6314d4d5c6b73d12 Mon Sep 17 00:00:00 2001 From: Jared Szechy Date: Tue, 4 Aug 2015 18:21:12 -0400 Subject: FogBugz project import --- CHANGELOG | 1 + Gemfile | 4 + Gemfile.lock | 6 + app/controllers/application_controller.rb | 7 +- app/controllers/import/fogbugz_controller.rb | 106 ++++++++ app/models/application_setting.rb | 2 +- app/models/project.rb | 2 + app/services/projects/download_service.rb | 43 +++ app/views/import/fogbugz/new.html.haml | 25 ++ app/views/import/fogbugz/new_user_map.html.haml | 49 ++++ app/views/import/fogbugz/status.html.haml | 51 ++++ app/views/projects/new.html.haml | 5 + app/workers/repository_import_worker.rb | 35 ++- config/initializers/1_settings.rb | 2 +- config/routes.rb | 9 + doc/workflow/importing/README.md | 1 + .../fogbugz_importer/fogbugz_import_finished.png | Bin 0 -> 53276 bytes .../fogbugz_importer/fogbugz_import_login.png | Bin 0 -> 44444 bytes .../fogbugz_import_select_fogbogz.png | Bin 0 -> 35415 bytes .../fogbugz_import_select_project.png | Bin 0 -> 62552 bytes .../fogbugz_importer/fogbugz_import_user_map.png | Bin 0 -> 157856 bytes .../importing/import_projects_from_fogbugz.md | 29 ++ lib/gitlab/fogbugz_import/client.rb | 56 ++++ lib/gitlab/fogbugz_import/importer.rb | 298 +++++++++++++++++++++ lib/gitlab/fogbugz_import/project_creator.rb | 38 +++ lib/gitlab/fogbugz_import/repository.rb | 31 +++ lib/gitlab/import_sources.rb | 1 + spec/controllers/import/fogbugz_controller_spec.rb | 39 +++ spec/services/projects/download_service_spec.rb | 65 +++++ 29 files changed, 889 insertions(+), 16 deletions(-) create mode 100644 app/controllers/import/fogbugz_controller.rb create mode 100644 app/services/projects/download_service.rb create mode 100644 app/views/import/fogbugz/new.html.haml create mode 100644 app/views/import/fogbugz/new_user_map.html.haml create mode 100644 app/views/import/fogbugz/status.html.haml create mode 100644 doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png create mode 100644 doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png create mode 100644 doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png create mode 100644 doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png create mode 100644 doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png create mode 100644 doc/workflow/importing/import_projects_from_fogbugz.md create mode 100644 lib/gitlab/fogbugz_import/client.rb create mode 100644 lib/gitlab/fogbugz_import/importer.rb create mode 100644 lib/gitlab/fogbugz_import/project_creator.rb create mode 100644 lib/gitlab/fogbugz_import/repository.rb create mode 100644 spec/controllers/import/fogbugz_controller_spec.rb create mode 100644 spec/services/projects/download_service_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 247eb1e3643..dd48bab978c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,7 @@ v 7.14.1 - Only include base URL in OmniAuth full_host parameter (Stan Hu) - Fix Error 500 in API when accessing a group that has an avatar (Stan Hu) - Ability to enable SSL verification for Webhooks + - Add FogBugz project import (Jared Szechy) v 7.14.0 - Fix bug where non-project members of the target project could set labels on new merge requests. diff --git a/Gemfile b/Gemfile index cca8fc38e57..d609ff6610f 100644 --- a/Gemfile +++ b/Gemfile @@ -157,6 +157,9 @@ gem "slack-notifier", "~> 1.0.0" # Asana integration gem 'asana', '~> 0.0.6' +# FogBugz integration +gem 'ruby-fogbugz' + # d3 gem 'd3_rails', '~> 3.5.5' @@ -259,6 +262,7 @@ group :test do gem 'email_spec', '~> 1.6.0' gem 'webmock', '~> 1.21.0' gem 'test_after_commit' + gem 'sham_rack' end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index 2df318f3382..dce7e4964a6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -575,6 +575,8 @@ GEM powerpack (~> 0.0.6) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) + ruby-fogbugz (0.1.1) + crack ruby-progressbar (1.7.1) ruby-saml (1.0.0) nokogiri (>= 1.5.10) @@ -609,6 +611,8 @@ GEM thor (~> 0.14) settingslogic (2.0.9) sexp_processor (4.4.5) + sham_rack (1.3.6) + rack shoulda-matchers (2.8.0) activesupport (>= 3.0.0) sidekiq (3.3.0) @@ -845,12 +849,14 @@ DEPENDENCIES rqrcode-rails3 rspec-rails (~> 3.3.0) rubocop (= 0.28.0) + ruby-fogbugz sanitize (~> 2.0) sass-rails (~> 4.0.5) sdoc seed-fu select2-rails (~> 3.5.9) settingslogic + sham_rack shoulda-matchers (~> 2.8.0) sidekiq (~> 3.3) sidetiq (= 0.6.3) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cb1cf13d34d..4c112534ae6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,5 @@ require 'gon' +require 'fogbugz' class ApplicationController < ActionController::Base include Gitlab::CurrentSettings @@ -20,7 +21,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :abilities, :can?, :current_application_settings - helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :git_import_enabled? + helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -337,6 +338,10 @@ class ApplicationController < ActionController::Base current_application_settings.import_sources.include?('google_code') end + def fogbugz_import_enabled? + current_application_settings.import_sources.include?('fogbugz') + end + def git_import_enabled? current_application_settings.import_sources.include?('git') end diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb new file mode 100644 index 00000000000..bda534fb4de --- /dev/null +++ b/app/controllers/import/fogbugz_controller.rb @@ -0,0 +1,106 @@ +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 + + end + + def callback + 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' + end + session[:fogbugz_token] = res.get_token + session[:fogbugz_uri] = params[:uri] + + redirect_to new_user_map_import_fogbugz_path + end + + def new_user_map + + end + + def create_user_map + user_map = params[:users] + + unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? } + flash.now[:alert] = 'All users must have a name.' + + render 'new_user_map' and return + end + + session[:fogbugz_user_map] = user_map + + flash[:notice] = 'The user map has been saved. Continue by selecting the projects you want to import.' + + redirect_to status_import_fogbugz_path + end + + def status + unless client.valid? + return redirect_to new_import_fogbugz_path + end + + @repos = client.repos + + @already_added_projects = current_user.created_projects.where(import_type: 'fogbugz') + already_added_projects_names = @already_added_projects.pluck(:import_source) + + @repos.reject! { |repo| already_added_projects_names.include? repo.name } + end + + def jobs + jobs = current_user.created_projects.where(import_type: 'fogbugz').to_json(only: [:id, :import_status]) + render json: jobs + end + + def create + @repo_id = params[:repo_id] + repo = client.repo(@repo_id) + fb_session = { uri: session[:fogbugz_uri], token: session[:fogbugz_token] } + @target_namespace = current_user.namespace + @project_name = repo.name + + namespace = @target_namespace + + umap = session[:fogbugz_user_map] || client.user_map + + @project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, namespace, current_user, umap).execute + end + + private + + def client + @client ||= Gitlab::FogbugzImport::Client.new(token: session[:fogbugz_token], uri: session[:fogbugz_uri]) + end + + def user_map + @user_map ||= begin + user_map = client.user_map + + stored_user_map = session[:fogbugz_user_map] + user_map.update(stored_user_map) if stored_user_map + + user_map + end + end + + def fogbugz_unauthorized(exception) + flash[:alert] = exception.message + redirect_to new_import_fogbugz_path + end + + def import_params + params.permit(:uri, :email, :password) + end + + def verify_fogbugz_import_enabled + not_found! unless fogbugz_import_enabled? + end +end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 8f27e35d723..c8841178e93 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -83,7 +83,7 @@ class ApplicationSetting < ActiveRecord::Base default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], - import_sources: ['github','bitbucket','gitlab','gitorious','google_code','git'] + import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] ) end diff --git a/app/models/project.rb b/app/models/project.rb index 8e33a4b2f0f..c2d7fa5fcb8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -43,6 +43,8 @@ class Project < ActiveRecord::Base extend Gitlab::ConfigHelper extend Enumerize + UNKNOWN_IMPORT_URL = 'http://unknown.git' + default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :issues_enabled, gitlab_config_features.issues diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb new file mode 100644 index 00000000000..99f22293d0d --- /dev/null +++ b/app/services/projects/download_service.rb @@ -0,0 +1,43 @@ +module Projects + class DownloadService < BaseService + + WHITELIST = [ + /^[^.]+\.fogbugz.com$/ + ] + + def initialize(project, url) + @project, @url = project, url + end + + def execute + return nil unless valid_url?(@url) + + uploader = FileUploader.new(@project) + uploader.download!(@url) + uploader.store! + + filename = uploader.image? ? uploader.file.basename : uploader.file.filename + + { + 'alt' => filename, + 'url' => uploader.secure_url, + 'is_image' => uploader.image? + } + end + + private + + def valid_url?(url) + url && http?(url) && valid_domain?(url) + end + + def http?(url) + url =~ /\A#{URI::regexp(['http', 'https'])}\z/ + end + + def valid_domain?(url) + host = URI.parse(url).host + WHITELIST.any? { |entry| entry === host } + end + end +end diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml new file mode 100644 index 00000000000..e1bb88ca4ed --- /dev/null +++ b/app/views/import/fogbugz/new.html.haml @@ -0,0 +1,25 @@ +- page_title "FogBugz Import" +%h3.page-title + %i.fa.fa-bug + Import projects from FogBugz +%hr + += form_tag callback_import_fogbugz_path, class: 'form-horizontal' do + %p + To get started you enter your FogBugz URL and login information below. + In the next steps, you'll be able to map users and select the projects + you want to import. + .form-group + = label_tag :uri, 'FogBugz URL', class: 'control-label' + .col-sm-4 + = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control' + .form-group + = label_tag :email, 'FogBugz Email', class: 'control-label' + .col-sm-4 + = text_field_tag :email, nil, class: 'form-control' + .form-group + = label_tag :password, 'FogBugz Password', class: 'control-label' + .col-sm-4 + = password_field_tag :password, nil, class: 'form-control' + .form-actions + = submit_tag 'Continue to the next step', class: 'btn btn-create' diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml new file mode 100644 index 00000000000..25cebfb3665 --- /dev/null +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -0,0 +1,49 @@ +- page_title 'User map', 'FogBugz import' +%h3.page-title + %i.fa.fa-bug + Import projects from FogBugz +%hr + += form_tag create_user_map_import_fogbugz_path, class: 'form-horizontal' do + %p + Customize how FogBugz email addresses and usernames are imported into GitLab. + In the next step, you'll be able to select the projects you want to import. + %p + The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames wil be imported into GitLab. You can change this by populating the table below. + %ul + %li + %strong Default: Map a FogBugz account ID to a full name + %p + An empty GitLab User field will add the FogBugz user's full name + (e.g. "By John Smith") in the description of all issues and comments. + It will also associate and/or assign these issues and comments with + the project creator. + %li + %strong Map a FogBugz account ID to a GitLab user + %p + Selecting a GitLab user will add a link to the GitLab user in the descriptions + of issues and comments (e.g. "By @johnsmith"). It will also + associate and/or assign these issues and comments with the selected user. + + %table.table + %thead + %tr + %th ID + %th Name + %th Email + %th GitLab User + %tbody + - @user_map.each do |id, user| + %tr + %td= id + %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control' + %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control' + %td + = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control', + scope: :all, email_user: true, selected: user[:gitlab_user]) + + .form-actions + = submit_tag 'Continue to the next step', class: 'btn btn-create' + +:coffeescript + new UsersSelect() diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml new file mode 100644 index 00000000000..f179ece402d --- /dev/null +++ b/app/views/import/fogbugz/status.html.haml @@ -0,0 +1,51 @@ +- page_title "FogBugz import" +%h3.page-title + %i.fa.fa-bug + Import projects from FogBugz + +- if @repos.any? + %p.light + Select projects you want to import. + %p.light + Optionally, you can + = link_to 'customize', new_user_map_import_fogbugz_path + how FogBugz email addresses and usernames are imported into GitLab. + %hr + %p + = button_tag 'Import all projects', class: 'btn btn-success js-import-all' + +%table.table.import-jobs + %thead + %tr + %th From FogBugz + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = project.import_source + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name + + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = repo.name + %td.import-target + = "#{current_user.username}/#{repo.name}" + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" + +:coffeescript + new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}") diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 636218368cc..bccea21e7a8 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -72,6 +72,11 @@ %i.fa.fa-google Google Code + - if fogbugz_import_enabled? + = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do + %i.fa.fa-bug + Fogbugz + - if git_import_enabled? = link_to "#", class: 'btn js-toggle-button import_git' do %i.fa.fa-git diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index f2ba2e15e7b..ea2808045eb 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -7,22 +7,31 @@ class RepositoryImportWorker def perform(project_id) project = Project.find(project_id) - import_result = gitlab_shell.send(:import_repository, + unless project.import_url == Project::UNKNOWN_IMPORT_URL + import_result = gitlab_shell.send(:import_repository, project.path_with_namespace, project.import_url) - return project.import_fail unless import_result + return project.import_fail unless import_result + else + unless project.create_repository + return project.import_fail + end + end - data_import_result = if project.import_type == 'github' - Gitlab::GithubImport::Importer.new(project).execute - elsif project.import_type == 'gitlab' - Gitlab::GitlabImport::Importer.new(project).execute - elsif project.import_type == 'bitbucket' - Gitlab::BitbucketImport::Importer.new(project).execute - elsif project.import_type == 'google_code' - Gitlab::GoogleCodeImport::Importer.new(project).execute - else - true - end + data_import_result = case project.import_type + when 'github' + Gitlab::GithubImport::Importer.new(project).execute + when 'gitlab' + Gitlab::GitlabImport::Importer.new(project).execute + when 'bitbucket' + Gitlab::BitbucketImport::Importer.new(project).execute + when 'google_code' + Gitlab::GoogleCodeImport::Importer.new(project).execute + when 'fogbugz' + Gitlab::FogbugzImport::Importer.new(project).execute + else + true + end return project.import_fail unless data_import_result Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index c47e5dab27c..689c3f3049d 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -158,7 +158,7 @@ Settings.gitlab.default_projects_features['snippets'] = false if Settings. Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root) Settings.gitlab['restricted_signup_domains'] ||= [] -Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','git'] +Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] # # Reply by email diff --git a/config/routes.rb b/config/routes.rb index 25c286b3083..5bf721d6b13 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,15 @@ Gitlab::Application.routes.draw do get :new_user_map, path: :user_map post :create_user_map, path: :user_map end + + resource :fogbugz, only: [:create, :new], controller: :fogbugz do + get :status + post :callback + get :jobs + + get :new_user_map, path: :user_map + post :create_user_map, path: :user_map + end end # diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md index 5cde90993d2..7ccf06fbd60 100644 --- a/doc/workflow/importing/README.md +++ b/doc/workflow/importing/README.md @@ -3,6 +3,7 @@ 1. [Bitbucket](import_projects_from_bitbucket.md) 2. [GitHub](import_projects_from_github.md) 3. [GitLab.com](import_projects_from_gitlab_com.md) +4. [FogBugz](import_projects_from_fogbugz.md) 4. [SVN](migrating_from_svn.md) ### Note diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png new file mode 100644 index 00000000000..205c515bd3f Binary files /dev/null and b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png differ diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png new file mode 100644 index 00000000000..a1e348d46ad Binary files /dev/null and b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png differ diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png new file mode 100644 index 00000000000..ed362846909 Binary files /dev/null and b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png differ diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png new file mode 100644 index 00000000000..d2fbd0267bd Binary files /dev/null and b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png differ diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png new file mode 100644 index 00000000000..b1cc4b58525 Binary files /dev/null and b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png differ diff --git a/doc/workflow/importing/import_projects_from_fogbugz.md b/doc/workflow/importing/import_projects_from_fogbugz.md new file mode 100644 index 00000000000..71af0f9ea44 --- /dev/null +++ b/doc/workflow/importing/import_projects_from_fogbugz.md @@ -0,0 +1,29 @@ +# Import your project from FogBugz to GitLab + +It only takes a few simple steps to import your project from FogBugz. +The importer will import all of your cases and comments with original case +numbers and timestamps. You will also have the opportunity to map FogBugz +users to GitLab users. + +* From your GitLab dashboard click 'New project' + +* Click on the 'FogBugz' button + +![FogBugz](fogbugz_importer/fogbugz_import_select_fogbogz.png) + +* Enter your FogBugz URL, email address, and password. + +![Login](fogbugz_importer/fogbugz_import_login.png) + +* Create mapping from FogBugz users to GitLab users. + +![User Map](fogbugz_importer/fogbugz_import_user_map.png) + +* Select the projects you wish to import by clicking the Import buttons + +![Import Project](fogbugz_importer/fogbugz_import_select_project.png) + +* Once the import has finished click the link to take you to the project +dashboard. Follow the directions to push your existing repository. + +![Finished](fogbugz_importer/fogbugz_import_finished.png) diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb new file mode 100644 index 00000000000..431d50882fd --- /dev/null +++ b/lib/gitlab/fogbugz_import/client.rb @@ -0,0 +1,56 @@ +require 'fogbugz' + +module Gitlab + module FogbugzImport + class Client + attr_reader :api + + def initialize(options = {}) + if options[:uri] && options[:token] + @api = ::Fogbugz::Interface.new(options) + elsif options[:uri] && options[:email] && options[:password] + @api = ::Fogbugz::Interface.new(options) + @api.authenticate + @api + end + end + + def get_token + @api.token + end + + def valid? + !get_token.blank? + end + + def user_map + users = {} + res = @api.command(:listPeople) + res['people']['person'].each do |user| + users[user['ixPerson']] = { name: user['sFullName'], email: user['sEmail'] } + end + users + end + + def repos + res = @api.command(:listProjects) + @repos ||= res['projects']['project'].map { |proj| FogbugzImport::Repository.new(proj) } + end + + def repo(id) + repos.find { |r| r.id.to_s == id.to_s } + end + + def cases(project_id) + project_name = repo(project_id).name + res = @api.command(:search, q: "project:'#{project_name}'", cols: 'ixPersonAssignedTo,ixPersonOpenedBy,ixPersonClosedBy,sStatus,sPriority,sCategory,fOpen,sTitle,sLatestTextSummary,dtOpened,dtClosed,dtResolved,dtLastUpdated,events') + return [] unless res['cases']['count'].to_i > 0 + res['cases']['case'] + end + + def categories + @api.command(:listCategories) + end + end + end +end diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb new file mode 100644 index 00000000000..61e08b23543 --- /dev/null +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -0,0 +1,298 @@ +module Gitlab + module FogbugzImport + class Importer + attr_reader :project, :repo + + def initialize(project) + @project = project + + import_data = project.import_data.try(:data) + repo_data = import_data['repo'] if import_data + @repo = FogbugzImport::Repository.new(repo_data) + + @known_labels = Set.new + end + + def execute + return true unless repo.valid? + + data = project.import_data.try(:data) + + client = Gitlab::FogbugzImport::Client.new(token: data['fb_session']['token'], uri: data['fb_session']['uri']) + + @cases = client.cases(@repo.id.to_i) + @categories = client.categories + + import_cases + + true + end + + private + + def user_map + @user_map ||= begin + user_map = Hash.new + import_data = project.import_data.try(:data) + stored_user_map = import_data['user_map'] if import_data + user_map.update(stored_user_map) if stored_user_map + + user_map + end + end + + def import_labels + @categories['categories']['category'].each do |label| + create_label(label['sCategory']) + @known_labels << name + end + end + + def nice_label_color(name) + case name + when 'Blocker' + '#ff0000' + when 'Crash' + '#ffcfcf' + when 'Major' + '#deffcf' + when 'Minor' + '#cfe9ff' + when 'Bug' + '#d9534f' + when 'Feature' + '#44ad8e' + when 'Technical Task' + '#4b6dd0' + else + '#e2e2e2' + end + end + + def create_label(name) + color = nice_label_color(name) + Label.create!(project_id: project.id, title: name, color: color) + end + + def user_info(person_id) + user_hash = user_map[person_id.to_s] + + user_name = '' + gitlab_id = nil + + unless user_hash.nil? + user_name = user_hash['name'] + if user = User.find_by(id: user_hash['gitlab_user']) + user_name = "@#{user.username}" + gitlab_id = user.id + end + end + + { name: user_name, gitlab_id: gitlab_id } + end + + def import_cases + return unless @cases + + while bug = @cases.shift + author = user_info(bug['ixPersonOpenedBy'])[:name] + date = DateTime.parse(bug['dtOpened']) + + comments = bug['events']['event'] + + content = format_content(opened_content(comments)) + body = format_issue_body(author, date, content) + + labels = [] + [bug['sCategory'], bug['sPriority']].each do |label| + unless label.blank? + labels << label + unless @known_labels.include?(label) + create_label(label) + @known_labels << label + end + end + end + + assignee_id = user_info(bug['ixPersonAssignedTo'])[:gitlab_id] + author_id = user_info(bug['ixPersonOpenedBy'])[:gitlab_id] || project.creator_id + + issue = Issue.create!( + project_id: project.id, + title: bug['sTitle'], + description: body, + author_id: author_id, + assignee_id: assignee_id, + state: bug['fOpen'] == 'true' ? 'opened' : 'closed' + ) + issue.add_labels_by_names(labels) + + if issue.iid != bug['ixBug'] + issue.update_attribute(:iid, bug['ixBug']) + end + + import_issue_comments(issue, comments) + + issue.update_attribute(:created_at, date) + + last_update = DateTime.parse(bug['dtLastUpdated']) + issue.update_attribute(:updated_at, last_update) + end + end + + def opened_content(comments) + while comment = comments.shift + if comment['sVerb'] == 'Opened' + return comment['s'] + end + end + '' + end + + def import_issue_comments(issue, comments) + Note.transaction do + while comment = comments.shift + verb = comment['sVerb'] + + next if verb == 'Opened' || verb === 'Closed' + + content = format_content(comment['s']) + attachments = format_attachments(comment['rgAttachments']) + updates = format_updates(comment) + + next if content.blank? && attachments.empty? && updates.empty? + + author = user_info(comment['ixPerson'])[:name] + author_id = user_info(comment['ixPerson'])[:gitlab_id] || project.creator_id + date = DateTime.parse(comment['dt']) + + body = format_issue_comment_body( + comment['ixBugEvent'], + author, + date, + content, + attachments, + updates + ) + + note = Note.create!( + project_id: project.id, + noteable_type: "Issue", + noteable_id: issue.id, + author_id: author_id, + note: body + ) + + note.update_attribute(:created_at, date) + note.update_attribute(:updated_at, date) + end + end + end + + def linkify_issues(s) + s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2') + s = s.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2') + s + end + + def escape_for_markdown(s) + s = s.gsub(/^#/, "\\#") + s = s.gsub(/^-/, "\\-") + s = s.gsub("`", "\\~") + s = s.gsub("\r", "") + s = s.gsub("\n", " \n") + s + end + + def format_content(raw_content) + return raw_content if raw_content.nil? + linkify_issues(escape_for_markdown(raw_content)) + end + + def format_attachments(raw_attachments) + return [] unless raw_attachments + + attachments = case raw_attachments['attachment'] + when Array + raw_attachments['attachment'] + when Hash + [raw_attachments['attachment']] + else + [] + end + + attachments.map! { |a| format_attachment(a) } + attachments.compact + end + + def format_attachment(attachment) + link = build_attachment_url(attachment['sURL']) + + res = ::Projects::DownloadService.new(project, link).execute + + return nil if res.nil? + + text = "[#{res['alt']}](#{res['url']})" + text = "!#{text}" if res['is_image'] + text + end + + def build_attachment_url(rel_url) + data = project.import_data.try(:data) + uri = data['fb_session']['uri'] + token = data['fb_session']['token'] + "#{uri}/#{rel_url}&token=#{token}" + end + + def format_updates(comment) + updates = [] + + if comment['sChanges'] + updates << "*Changes: #{linkify_issues(comment['sChanges'].chomp)}*" + end + + if comment['evtDescription'] + updates << "*#{comment['evtDescription']}*" + end + + updates + end + + def format_issue_body(author, date, content) + body = [] + body << "*By #{author} on #{date} (imported from FogBugz)*" + body << '---' + + if content.blank? + content = '*(No description has been entered for this issue)*' + end + body << content + + body.join("\n\n") + end + + def format_issue_comment_body(id, author, date, content, attachments, updates) + body = [] + body << "*By #{author} on #{date} (imported from FogBugz)*" + body << '---' + + if content.blank? + content = "*(No comment has been entered for this change)*" + end + body << content + + if updates.any? + body << '---' + body += updates + end + + if attachments.any? + body << '---' + body += attachments + end + + body.join("\n\n") + end + end + end +end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb new file mode 100644 index 00000000000..f02ea43910f --- /dev/null +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -0,0 +1,38 @@ +module Gitlab + module FogbugzImport + class ProjectCreator + attr_reader :repo, :fb_session, :namespace, :current_user, :user_map + + def initialize(repo, fb_session, namespace, current_user, user_map = nil) + @repo = repo + @fb_session = fb_session + @namespace = namespace + @current_user = current_user + @user_map = user_map + end + + def execute + project = ::Projects::CreateService.new(current_user, + name: repo.safe_name, + path: repo.path, + namespace: namespace, + creator: current_user, + visibility_level: Gitlab::VisibilityLevel::INTERNAL, + import_type: 'fogbugz', + import_source: repo.name, + import_url: Project::UNKNOWN_IMPORT_URL + ).execute + + import_data = project.create_import_data( + data: { + 'repo' => repo.raw_data, + 'user_map' => user_map, + 'fb_session' => fb_session + } + ) + + project + end + end + end +end diff --git a/lib/gitlab/fogbugz_import/repository.rb b/lib/gitlab/fogbugz_import/repository.rb new file mode 100644 index 00000000000..d1dc63db2b2 --- /dev/null +++ b/lib/gitlab/fogbugz_import/repository.rb @@ -0,0 +1,31 @@ +module Gitlab + module FogbugzImport + class Repository + attr_accessor :raw_data + + def initialize(raw_data) + @raw_data = raw_data + end + + def valid? + raw_data.is_a?(Hash) + end + + def id + raw_data['ixProject'] + end + + def name + raw_data['sProject'] + end + + def safe_name + name.gsub(/[^\s\w.-]/, '') + end + + def path + safe_name.gsub(/[\s]/, '_') + end + end + end +end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 991b70aab6a..ccfdfbe73e8 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -19,6 +19,7 @@ module Gitlab 'GitLab.com' => 'gitlab', 'Gitorious.org' => 'gitorious', 'Google Code' => 'google_code', + 'FogBugz' => 'fogbugz', 'Any repo by URL' => 'git', } end diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb new file mode 100644 index 00000000000..27b11267d2a --- /dev/null +++ b/spec/controllers/import/fogbugz_controller_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' +require_relative 'import_spec_helper' + +describe Import::FogbugzController do + include ImportSpecHelper + + let(:user) { create(:user) } + + before do + sign_in(user) + end + + describe 'GET status' do + before do + @repo = OpenStruct.new(name: 'vim') + stub_client(valid?: true) + end + + it 'assigns variables' do + @project = create(:project, import_type: 'fogbugz', creator_id: user.id) + stub_client(repos: [@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([@repo]) + end + + it 'does not show already added project' do + @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim') + stub_client(repos: [@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([]) + end + end +end diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb new file mode 100644 index 00000000000..f12e09c58c3 --- /dev/null +++ b/spec/services/projects/download_service_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Projects::DownloadService do + describe 'File service' do + before do + @user = create :user + @project = create :project, creator_id: @user.id, namespace: @user.namespace + end + + context 'for a URL that is not on whitelist' do + before do + url = 'https://code.jquery.com/jquery-2.1.4.min.js' + @link_to_file = download_file(@project, url) + end + + it { expect(@link_to_file).to eq(nil) } + end + + context 'for URLs that are on the whitelist' do + before do + sham_rack_app = ShamRack.at('mycompany.fogbugz.com').stub + sham_rack_app.register_resource('/rails_sample.jpg', File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'), 'image/jpg') + sham_rack_app.register_resource('/doc_sample.txt', File.read(Rails.root + 'spec/fixtures/doc_sample.txt'), 'text/plain') + end + + after do + ShamRack.unmount_all + end + + context 'an image file' do + before do + url = 'http://mycompany.fogbugz.com/rails_sample.jpg' + @link_to_file = download_file(@project, url) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file['is_image']).to be true } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('rails_sample.jpg') } + it { expect(@link_to_file['alt']).to eq('rails_sample') } + end + + context 'a txt file' do + before do + url = 'http://mycompany.fogbugz.com/doc_sample.txt' + @link_to_file = download_file(@project, url) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file['is_image']).to be false } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('doc_sample.txt') } + it { expect(@link_to_file['alt']).to eq('doc_sample.txt') } + end + end + end + + def download_file(repository, url) + Projects::DownloadService.new(repository, url).execute + end +end -- 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 be59f4338ffc1e075e32b09d43c19503b1216533 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 09:25:49 +0100 Subject: Fix RootController --- app/controllers/root_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index a3aa86de5b4..54171ff67c5 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -7,7 +7,7 @@ # For users who haven't customized the setting, we simply delegate to # `DashboardController#show`, which is the default. class RootController < Dashboard::ProjectsController - before_action :redirect_to_custom_dashboard, only: [:show] + before_action :redirect_to_custom_dashboard, only: [:index] def index super -- cgit v1.2.1 From b7431ec042bc14052c407fd277177681e189c892 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 4 Sep 2015 21:40:26 +0300 Subject: Make all group public --- CHANGELOG | 2 +- app/models/ability.rb | 2 +- app/models/group.rb | 4 ---- app/views/groups/show.html.haml | 14 +++++++------- features/explore/groups.feature | 14 -------------- features/groups.feature | 11 +++++++++++ features/steps/groups.rb | 20 ++++++++++++++++++++ spec/controllers/namespaces_controller_spec.rb | 6 ++---- spec/controllers/uploads_controller_spec.rb | 8 -------- spec/features/security/group_access_spec.rb | 18 +++++++++--------- 10 files changed, 51 insertions(+), 48 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dd48bab978c..e7f209c1cd0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,7 @@ v 8.0.0 (unreleased) - Fix 500 error when submit project snippet without body - Improve search page usability - Bring more UI consistency in way how projects, snippets and groups lists are rendered - - Make all profiles public + - Make all profiles and group public - Fixed login failure when extern_uid changes (Joel Koglin) - Don't notify users without access to the project when they are (accidentally) mentioned in a note. - Retrieving oauth token with LDAP credentials diff --git a/app/models/ability.rb b/app/models/ability.rb index f8e5afa9b01..64cfdb6ea89 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -54,7 +54,7 @@ class Ability nil end - if group && group.public_profile? + if group [:read_group] else [] diff --git a/app/models/group.rb b/app/models/group.rb index 9cd146bb73b..702d7825d57 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -119,10 +119,6 @@ class Group < Namespace end end - def public_profile? - projects.public_only.any? - end - def post_create_hook Gitlab::AppLogger.info("Group \"#{name}\" was created") diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 0577f4ec142..7fd1b3f18c8 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -22,16 +22,16 @@ - if current_user = render "events/event_last_push", event: @last_push - - if current_user - %ul.nav.nav-pills.event_filter.pull-right - %li - = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do - %i.fa.fa-rss + %ul.nav.nav-pills.event_filter.pull-right + %li + = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do + %i.fa.fa-rss = render 'shared/event_filter' %hr .content_list = spinner - %aside.side.col-md-5 - = render "projects", projects: @projects + - if @projects.any? + %aside.side.col-md-5 + = render "projects", projects: @projects diff --git a/features/explore/groups.feature b/features/explore/groups.feature index c11634bd74a..a42e59c98f2 100644 --- a/features/explore/groups.feature +++ b/features/explore/groups.feature @@ -3,20 +3,6 @@ Feature: Explore Groups Background: Given group "TestGroup" has private project "Enterprise" - Scenario: I should not see group with private projects as visitor - When I visit group "TestGroup" page - Then I should be redirected to sign in page - - Scenario: I should not see group with private projects group as user - When I sign in as a user - And I visit group "TestGroup" page - Then page status code should be 404 - - Scenario: I should not see group with private and internal projects as visitor - Given group "TestGroup" has internal project "Internal" - When I visit group "TestGroup" page - Then I should be redirected to sign in page - Scenario: I should see group with private and internal projects as user Given group "TestGroup" has internal project "Internal" When I sign in as a user diff --git a/features/groups.feature b/features/groups.feature index d5272fdddcf..db37fa3b375 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -159,3 +159,14 @@ Feature: Groups When I visit group "Owned" projects page Then I should see group "Owned" projects list And I should see "archived" label + + # Public group + @javascript + Scenario: Signed out user should see group + Given "Mary Jane" is owner of group "Owned" + And I am a signed out user + And Group "Owned" has a public project "Public-project" + When I visit group "Owned" page + Then I should see group "Owned" + Then I should see project "Public-project" + diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 18a1c4d32ce..45201c85f26 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -17,6 +17,26 @@ class Spinach::Features::Groups < Spinach::FeatureSteps find(:css, 'button.btn-new').click end + step 'I should see group "Owned"' do + expect(page).to have_content '@owned' + end + + step 'I am a signed out user' do + logout + end + + step 'Group "Owned" has a public project "Public-project"' do + group = Group.find_by(name: "Owned") + + @project = create :empty_project, :public, + group: group, + name: "Public-project" + end + + step 'I should see project "Public-project"' do + expect(page).to have_content 'Public-project' + end + step 'I select "Mike" as "Reporter"' do user = User.find_by(name: "Mike") diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 9c8619722cd..74702f93302 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -46,13 +46,11 @@ describe NamespacesController do context "when the project doesn't have public projects" do context "when not signed in" do - it "redirects to the sign in page" do + it "does not redirect to the sign in page" do get :show, id: group.path - - expect(response).to redirect_to(new_user_session_path) + expect(response).not_to redirect_to(new_user_session_path) end end - context "when signed in" do before do sign_in(user) diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 0f9780356b1..af5d043cf02 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -156,14 +156,6 @@ describe UploadsController do end context "when the project doesn't have public projects" do - context "when not signed in" do - it "redirects to the sign in page" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - - expect(response).to redirect_to(new_user_session_path) - end - end - context "when signed in" do before do sign_in(user) diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb index 8ce15388605..3f708b5ebe7 100644 --- a/spec/features/security/group_access_spec.rb +++ b/spec/features/security/group_access_spec.rb @@ -68,7 +68,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end context 'with no projects' do @@ -77,8 +77,8 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:reporter) } it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end end @@ -118,7 +118,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end context 'with no projects' do @@ -128,7 +128,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end end @@ -168,7 +168,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end context 'with no projects' do @@ -178,7 +178,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end end @@ -218,7 +218,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end context 'with no projects' do @@ -228,7 +228,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end end -- cgit v1.2.1 From 5c961b9cc5a48cfdfe7c1bee91f29138e167b058 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 09:28:09 +0100 Subject: Fix spec --- features/steps/invites.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/invites.rb b/features/steps/invites.rb index 5e8feff5095..dac972172aa 100644 --- a/features/steps/invites.rb +++ b/features/steps/invites.rb @@ -63,7 +63,7 @@ class Spinach::Features::Invites < Spinach::FeatureSteps end step 'I should be redirected to the dashboard' do - expect(current_path).to eq(dashboard_path) + expect(current_path).to eq(dashboard_projects_path) end step 'I should see a notice telling me I have declined' do -- cgit v1.2.1 From 89f166021f12aec575ec1b9f4dc82a824bc19950 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 09:29:42 +0100 Subject: Fix snippets exploration test --- features/steps/shared/paths.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 2ec126e1e94..eb978620da6 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -460,7 +460,7 @@ module SharedPaths end step 'I visit snippets page' do - visit snippets_path + visit explore_snippets_path end step 'I visit new snippet page' do -- cgit v1.2.1 From e4b30f9db3d0b2a2d3b7274d312872fe41d22104 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 10:22:19 +0100 Subject: Fix help access spec --- spec/features/security/dashboard_access_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb index 03019139415..788581a26cb 100644 --- a/spec/features/security/dashboard_access_spec.rb +++ b/spec/features/security/dashboard_access_spec.rb @@ -40,7 +40,7 @@ describe "Dashboard access", feature: true do it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :visitor } end describe "GET /projects/new" do -- cgit v1.2.1 From c9d78a00c583c0439aeea7416eeea2e50e0d59e4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 11:36:55 +0200 Subject: Fix UI bugs with discussions Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/timeline.scss | 19 ++++++++++++++++++- app/assets/stylesheets/pages/note_form.scss | 6 +++--- app/views/projects/notes/_discussion.html.haml | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss index 668a6f848cc..74bbaabad39 100644 --- a/app/assets/stylesheets/generic/timeline.scss +++ b/app/assets/stylesheets/generic/timeline.scss @@ -4,14 +4,19 @@ margin: 0; padding: 0; - > li { + .timeline-entry { padding: $gl-padding; border-color: #f1f2f4; margin-left: -$gl-padding; margin-right: -$gl-padding; color: $gl-gray; + border-bottom: 1px solid #f1f2f4; border-right: 1px solid #f1f2f4; + &:last-child { + border-bottom: none; + } + .avatar { margin-right: 15px; } @@ -33,6 +38,13 @@ color: $gl-gray !important; } } + + .diff-file { + border: 1px solid $border-color; + border-bottom: none; + margin-left: 0; + margin-right: 0; + } } @media (max-width: $screen-xs-max) { @@ -51,3 +63,8 @@ } } } + +.discussion .timeline-entry { + margin: 0; + border-right: none; +} diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 85804f5ee61..b311d26d675 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -146,9 +146,9 @@ } .discussion-reply-holder { - background: #f9f9f9; + background: $background-color; padding: 10px 15px; - border-top: 1px solid #DDD; + border-top: 1px solid $border-color; } } @@ -170,6 +170,6 @@ background: #FFF; padding: 5px; margin-top: -11px; - border: 1px solid #DDD; + border: 1px solid $border-color; font-size: 13px; } diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml index 6daf2d15d07..b8068835b3a 100644 --- a/app/views/projects/notes/_discussion.html.haml +++ b/app/views/projects/notes/_discussion.html.haml @@ -1,5 +1,5 @@ - note = discussion_notes.first -.timeline-entry.prepend-top-default +.timeline-entry .timeline-entry-inner .timeline-icon = link_to user_path(note.author) do -- 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 d3730b071365dd3dbfa0b8ec4433e1f18a93c969 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Sep 2015 14:54:34 +0100 Subject: Fix highlighting of deleted lines in diffs. --- CHANGELOG | 1 + config/initializers/rouge_diff_lexer.rb | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 config/initializers/rouge_diff_lexer.rb diff --git a/CHANGELOG b/CHANGELOG index 247eb1e3643..ddb31fc430f 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 + - Fix highlighting of deleted lines in diffs. v 7.14.1 - Improve abuse reports management from admin area diff --git a/config/initializers/rouge_diff_lexer.rb b/config/initializers/rouge_diff_lexer.rb new file mode 100644 index 00000000000..fdb2d7b748e --- /dev/null +++ b/config/initializers/rouge_diff_lexer.rb @@ -0,0 +1,24 @@ +# Here until https://github.com/jneen/rouge/pull/297 is merged into Rouge and the gem is updated in GitLab. +module Rouge + module Lexers + class Diff + def self.analyze_text(text) + return 1 if text.start_with?('Index: ') + return 1 if text.start_with?('diff ') + return 0.9 if text.start_with?('--- ') + end + + state :root do + rule(/^ .*\n/, Text) + rule(/^---\n/, Text) + rule(/^\+.*\n/, Generic::Inserted) + rule(/^-+.*\n/, Generic::Deleted) + rule(/^!.*\n/, Generic::Strong) + rule(/^@.*\n/, Generic::Subheading) + rule(/^([Ii]ndex|diff).*\n/, Generic::Heading) + rule(/^=.*\n/, Generic::Heading) + rule(/.*\n/, Text) + end + end + end +end -- 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 0d66d4d342c72d141578d746aad766a6234fc872 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Sep 2015 15:19:58 +0100 Subject: Fix reading session_expire_delay when application settings are not yet created and migrations are not yet done --- config/initializers/session_store.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 6d274cd95a1..88651394d1d 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -2,7 +2,12 @@ require 'gitlab/current_settings' include Gitlab::CurrentSettings -Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay + +# allow it to fail: it may to do so when create_from_defaults is executed before migrations are actually done +begin + Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay +rescue +end Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. -- 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 fa40759768518d3457cc95204d6496db999aa7ca Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 22:36:13 +0200 Subject: Separate profile settings link from the rest on sidebar Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_dashboard.html.haml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 0cf1c3d5d27..7d3764a76a3 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -37,14 +37,15 @@ = icon('clipboard fw') %span Snippets + = nav_link(controller: :help) do + = link_to help_path, title: 'Help', data: {placement: 'right'} do + = icon('question-circle fw') + %span + Help - if current_user + %li.separate-item = nav_link(controller: :profile) do = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('user fw') %span Profile Settings - = nav_link(controller: :help) do - = link_to help_path, title: 'Help', data: {placement: 'right'} do - = icon('question-circle fw') - %span - Help -- cgit v1.2.1 From 1e3ce07714efcc5ac18bb2f167c5b4956e114839 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 23:05:47 +0200 Subject: Use page title for header title too Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/blocks.scss | 4 ++++ app/views/layouts/nav/_profile.html.haml | 2 +- app/views/layouts/profile.html.haml | 3 ++- app/views/profiles/accounts/show.html.haml | 9 +++------ app/views/profiles/applications.html.haml | 9 ++++----- app/views/profiles/audit_log.html.haml | 9 ++++++--- app/views/profiles/emails/index.html.haml | 10 ++++------ app/views/profiles/keys/index.html.haml | 13 +++++++------ app/views/profiles/notifications/show.html.haml | 8 ++++---- app/views/profiles/passwords/edit.html.haml | 10 +++++----- app/views/profiles/passwords/new.html.haml | 2 +- app/views/profiles/preferences/show.html.haml | 8 ++++---- app/views/profiles/show.html.haml | 9 ++------- 13 files changed, 47 insertions(+), 49 deletions(-) diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss index 27a4c4db8c8..48d9f890f62 100644 --- a/app/assets/stylesheets/generic/blocks.scss +++ b/app/assets/stylesheets/generic/blocks.scss @@ -27,6 +27,10 @@ border-bottom: 1px solid #e7e9ed; color: $gl-gray; + &.top-block { + border-top: none; + } + &.middle-block { margin-top: 0; margin-bottom: 0; diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 33fd5fcef6c..abdb7544651 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -11,7 +11,7 @@ = link_to profile_path, title: 'Profile', data: {placement: 'right'} do = icon('user fw') %span - Profile + Profile Settings = nav_link(controller: [:accounts, :two_factor_auths]) do = link_to profile_account_path, title: 'Account', data: {placement: 'right'} do = icon('gear fw') diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 77d2ccbf762..b80ce0dfc75 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,5 +1,6 @@ - page_title "Profile Settings" -- header_title "Profile Settings", profile_path +- unless @header_title + - header_title "Profile Settings", profile_path - sidebar "profile" = render template: "layouts/application" diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 767fe2e0e9a..2d2a9ac8009 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,9 +1,6 @@ - page_title "Account" -%h3.page-title - = page_title -%p.light - Change your username and basic account settings. -%hr +- header_title page_title, profile_account_path + - if current_user.ldap_user? .alert.alert-info Some options are unavailable for LDAP accounts @@ -69,7 +66,7 @@ - button_based_providers.each do |provider| .btn-group = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true" - + - if auth_active?(provider) = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do = icon('close') diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml index 3a3e6e1b1c4..2342936a5d5 100644 --- a/app/views/profiles/applications.html.haml +++ b/app/views/profiles/applications.html.haml @@ -1,13 +1,12 @@ - page_title "Applications" -%h3.page-title - = page_title -%p.light +- header_title page_title, applications_profile_path + +.gray-content-block.top-block - if user_oauth_applications? - Manage applications that can use GitLab as an OAuth provider, + Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account. - else Manage applications that you've authorized to use your account. -%hr - if user_oauth_applications? .oauth-applications diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml index 698d6037428..8fdba45b193 100644 --- a/app/views/profiles/audit_log.html.haml +++ b/app/views/profiles/audit_log.html.haml @@ -1,5 +1,8 @@ - page_title "Audit Log" -%h3.page-title Audit Log -%p.light History of authentications +- header_title page_title, audit_log_profile_path -= render 'event_table', events: @events \ No newline at end of file +.gray-content-block.top-block + History of authentications + +.prepend-top-default += render 'event_table', events: @events diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index 66812872c41..1d140347a5f 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -1,12 +1,10 @@ - page_title "Emails" -%h3.page-title - = page_title -%p.light - Control emails linked to your account -%hr +- header_title page_title, profile_emails_path +.gray-content-block.top-block + Control emails linked to your account -%ul +%ul.prepend-top-default %li Your %b Primary Email diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml index 06655f7ba3a..14adba1c797 100644 --- a/app/views/profiles/keys/index.html.haml +++ b/app/views/profiles/keys/index.html.haml @@ -1,11 +1,12 @@ - page_title "SSH Keys" -%h3.page-title - = page_title +- header_title page_title, profile_keys_path + +.gray-content-block.top-block .pull-right = link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new" -%p.light - Before you can add an SSH key you need to - = link_to "generate it.", help_page_path("ssh", "README") -%hr + .oneline + Before you can add an SSH key you need to + = link_to "generate it.", help_page_path("ssh", "README") +.prepend-top-default = render 'key_table' diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index db7fa2eabe3..ea4e5f3e182 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -1,10 +1,10 @@ - page_title "Notifications" -%h3.page-title - = page_title -%p.light +- header_title page_title, profile_notifications_path + +.gray-content-block.top-block These are your global notification settings. -%hr +.prepend-top-default = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications form-horizontal global-notifications-form' } do |f| -if @user.errors.any? %div.alert.alert-danger diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 399ae98adf9..fab7c45c9b2 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -1,13 +1,13 @@ - page_title "Password" -%h3.page-title - = page_title -%p.light +- header_title page_title, edit_profile_password_path + +.gray-content-block.top-block - if @user.password_automatically_set? Set your password. - else Change your password or recover your current one. -%hr -.update-password + +.update-password.prepend-top-default = form_for @user, url: profile_password_path, method: :put, html: { class: 'form-horizontal' } do |f| %div %p.slead diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index 9c6204963e0..d165f758c81 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -12,7 +12,7 @@ %ul - @user.errors.full_messages.each do |msg| %li= msg - + - unless @user.password_automatically_set? .form-group = f.label :current_password, class: 'control-label' diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index aa0361a0a1b..f8cbdb79329 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -1,11 +1,11 @@ - page_title 'Preferences' -%h3.page-title - = page_title -%p.light +- header_title page_title, profile_preferences_path + +.gray-content-block.top-block These settings allow you to customize the appearance and behavior of the site. They are saved with your account and will persist to any device you use to access the site. -%hr +.prepend-top-default = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f| .panel.panel-default.application-theme diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index c519e52e596..47412e2ef0c 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,14 +1,9 @@ -- page_title "Profile" -%h3.page-title - = page_title -%p.light +.gray-content-block.top-block This information will appear on your profile. - if current_user.ldap_user? Some options are unavailable for LDAP accounts -%hr - - +.prepend-top-default = form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" }, authenticity_token: true do |f| -if @user.errors.any? %div.alert.alert-danger -- cgit v1.2.1 From 10b6a9248159830f45012a1d551e82d86aeb75d0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 23:29:19 +0200 Subject: Style panels and add blank container Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/gl_bootstrap.scss | 14 +++++++++++++- app/assets/stylesheets/base/gl_variables.scss | 11 ++++++----- app/assets/stylesheets/generic/buttons.scss | 2 +- app/assets/stylesheets/generic/sidebar.scss | 6 ++++++ app/helpers/page_layout_helper.rb | 22 ++++++++++++++++++++++ app/views/layouts/_page.html.haml | 2 +- app/views/profiles/preferences/show.html.haml | 4 ++-- app/views/projects/edit.html.haml | 7 ++++--- 8 files changed, 55 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss index ae72c5b8d97..94508b902c4 100644 --- a/app/assets/stylesheets/base/gl_bootstrap.scss +++ b/app/assets/stylesheets/base/gl_bootstrap.scss @@ -157,8 +157,10 @@ * */ .panel { + box-shadow: none; .panel-heading { - font-weight: bold; + font-size: 17px; + line-height: 38px; .panel-head-actions { position: relative; @@ -182,6 +184,10 @@ .pagination { margin: 0; } + + .btn { + min-width: 124px; + } } &.panel-small { @@ -209,6 +215,12 @@ } } +.alert-help { + background-color: $background-color; + border: 1px solid $border-color; + color: $gl-gray; +} + // Typography ================================================================= .text-primary, diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss index d18b48eaca9..bfef5f78f83 100644 --- a/app/assets/stylesheets/base/gl_variables.scss +++ b/app/assets/stylesheets/base/gl_variables.scss @@ -114,11 +114,12 @@ $alert-border-radius: 0; // //## -$panel-border-radius: 0; -$panel-default-text: $text-color; -$panel-default-border: #E7E9ED; -$panel-default-heading-bg: #F8FAFC; - +$panel-border-radius: 2px; +$panel-default-text: $text-color; +$panel-default-border: $border-color; +$panel-default-heading-bg: $background-color; +$panel-footer-bg: $background-color; +$panel-inner-border: $border-color; //== Wells // diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index e8237509092..46ef595ddf0 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -10,7 +10,7 @@ } &.btn-save { - @extend .btn-primary; + @extend .btn-success; } &.btn-remove { diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss index 22720c2e1d5..f501bd579e3 100644 --- a/app/assets/stylesheets/generic/sidebar.scss +++ b/app/assets/stylesheets/generic/sidebar.scss @@ -28,6 +28,12 @@ padding: $gl-padding; border: 1px solid #e7e9ed; min-height: 90vh; + + &.container-blank { + background: none; + padding: 0; + border: none; + } } } diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 8473d6d75d0..bfbaf11c25a 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -31,4 +31,26 @@ module PageLayoutHelper @fluid_layout end end + + def blank_container(enabled = false) + if @blank_container.nil? + @blank_container = enabled + else + @blank_container + end + end + + def container_class + css_class = "container-fluid" + + if fluid_layout + css_class += " container-limited" + end + + if blank_container + css_class += " container-blank" + end + + css_class + end end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index c1746676ae2..707905ee92a 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -19,7 +19,7 @@ = current_user.username .content-wrapper = render "layouts/flash" - %div{ class: fluid_layout ? "container-fluid" : "container-fluid container-limited" } + %div{ class: container_class } .content .clearfix = yield diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index f8cbdb79329..60289bfe7cd 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -1,11 +1,11 @@ - page_title 'Preferences' - header_title page_title, profile_preferences_path +- @blank_container = true -.gray-content-block.top-block +.alert.alert-help These settings allow you to customize the appearance and behavior of the site. They are saved with your account and will persist to any device you use to access the site. -.prepend-top-default = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f| .panel.panel-default.application-theme diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index e8e65d87f47..90dce739992 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -1,10 +1,11 @@ +- @blank_container = true + .project-edit-container .project-edit-errors .project-edit-content - %div - %h3.page-title + .panel.panel-default + .panel-heading Project settings - %hr .panel-body = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal fieldset-form" }, authenticity_token: true do |f| -- cgit v1.2.1 From 12ad9b4ba0341ac7b5012dd52b1d6cd6e68f778a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 23:38:27 +0200 Subject: Enable blank container for group settings page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/gl_bootstrap.scss | 10 +++++++--- app/views/groups/edit.html.haml | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss index 94508b902c4..eb8d23d6453 100644 --- a/app/assets/stylesheets/base/gl_bootstrap.scss +++ b/app/assets/stylesheets/base/gl_bootstrap.scss @@ -156,12 +156,16 @@ * Add some extra stuff to panels * */ + +.container-blank .panel .panel-heading { + font-size: 17px; + line-height: 38px; +} + .panel { box-shadow: none; - .panel-heading { - font-size: 17px; - line-height: 38px; + .panel-heading { .panel-head-actions { position: relative; top: -5px; diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 2ff4b7e23ea..ac7d9ba0f4f 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,3 +1,4 @@ +- @blank_container = true .panel.panel-default .panel-heading %strong= @group.name -- cgit v1.2.1 From 960a1880ab09f8c12d6f7a7c22296c876ddad1ac Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Sep 2015 23:49:34 +0200 Subject: Fix last element of sidebar being hidden for small screens Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/sidebar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss index f501bd579e3..41ffd358576 100644 --- a/app/assets/stylesheets/generic/sidebar.scss +++ b/app/assets/stylesheets/generic/sidebar.scss @@ -39,7 +39,7 @@ .nav-sidebar { margin-top: 14 + $header-height; - margin-bottom: 50px; + margin-bottom: 100px; transition-duration: .3s; list-style: none; overflow: hidden; -- cgit v1.2.1 From def939a5628fb3df24b39dbe7b39636968c294ed Mon Sep 17 00:00:00 2001 From: Darby Date: Wed, 9 Sep 2015 15:04:38 -0700 Subject: Turned off autocomplete for new issue titles --- app/views/shared/issuable/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 09327d645f3..1aa1e3c6c97 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -9,7 +9,7 @@ = f.label :title, class: 'control-label' do %strong= 'Title *' .col-sm-10 - = f.text_field :title, maxlength: 255, autofocus: true, + = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', class: 'form-control pad js-gfm-input', required: true - if issuable.is_a?(MergeRequest) -- cgit v1.2.1 From a0359fbd77c0a4fcbac30c15544b1a3432bfe8bf Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 00:34:09 +0200 Subject: Improve accounts page UI Signed-off-by: Dmitriy Zaporozhets --- app/views/profiles/accounts/show.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 2d2a9ac8009..cd7b1b0fe03 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,5 +1,6 @@ - page_title "Account" - header_title page_title, profile_account_path +- @blank_container = true - if current_user.ldap_user? .alert.alert-info -- cgit v1.2.1 From 19cba632d846c9fcf736133eb62db1b8905c5009 Mon Sep 17 00:00:00 2001 From: Duke Date: Wed, 9 Sep 2015 21:20:51 -0300 Subject: fix 7.14 stable branch name --- doc/update/6.x-or-7.x-to-7.14.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md index 5bc1f84270a..b34fb12da6f 100644 --- a/doc/update/6.x-or-7.x-to-7.14.md +++ b/doc/update/6.x-or-7.x-to-7.14.md @@ -162,7 +162,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab TIP: to see what changed in `gitlab.yml.example` in this release use next command: ``` -git diff 6-0-stable:config/gitlab.yml.example 7.14-stable:config/gitlab.yml.example +git diff 6-0-stable:config/gitlab.yml.example 7-14-stable:config/gitlab.yml.example ``` * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/gitlab.yml.example but with your settings. -- cgit v1.2.1 From 253bc0ca15a9b2f459aef94a33de926984747f35 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 00:19:53 -0700 Subject: Fix test --- features/steps/admin/users.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/features/steps/admin/users.rb b/features/steps/admin/users.rb index 2e17d5c4c2e..4bc290b6bdf 100644 --- a/features/steps/admin/users.rb +++ b/features/steps/admin/users.rb @@ -4,11 +4,13 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps include SharedAdmin before do - allow(Devise).to receive(:omniauth_providers).and_return([:twitter, :twitter_updated]) + allow(Gitlab::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated]) + allow_any_instance_of(ApplicationHelper).to receive(:user_omniauth_authorize_path).and_return(root_path) end after do - allow(Devise).to receive(:omniauth_providers).and_call_original + allow(Gitlab::OAuth::Provider).to receive(:providers).and_call_original + allow_any_instance_of(ApplicationHelper).to receive(:user_omniauth_authorize_path).and_call_original end step 'I should see all users' do -- cgit v1.2.1 From af3b6f6362d34077d100385b71f9fe2a0ce8a446 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 09:51:37 +0200 Subject: Fix fixed layout Signed-off-by: Dmitriy Zaporozhets --- app/helpers/page_layout_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index bfbaf11c25a..df37be51ce9 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -43,7 +43,7 @@ module PageLayoutHelper def container_class css_class = "container-fluid" - if fluid_layout + unless fluid_layout css_class += " container-limited" end -- 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 86ef2eb7281346df5b473f3cf3196a1f76caf002 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 12:35:37 +0200 Subject: Conver forked from button to text Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/projects.scss | 1 - app/views/projects/_home_panel.html.haml | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 361fd63bc79..d3c84595701 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -48,7 +48,6 @@ p { color: #7f8fa4; - display: inline; } } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b93036e78e6..dbecd1e7192 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -7,6 +7,13 @@ - if @project.description.present? = markdown(@project.description, pipeline: :description) + - if forked_from_project = @project.forked_from_project + %p + Forked from + = link_to project_path(forked_from_project) do + = forked_from_project.namespace.try(:name) + + .project-repo-buttons = render 'projects/buttons/star' @@ -14,12 +21,6 @@ - unless empty_repo = render 'projects/buttons/fork' - - if forked_from_project = @project.forked_from_project - = link_to project_path(forked_from_project), class: 'btn' do - = icon("code-fork fw") - Forked from - = forked_from_project.namespace.try(:name) - - if can? current_user, :download_code, @project = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do = icon('download fw') -- cgit v1.2.1 From 6933da1ee0eb42146978be751e4feaa889e4790a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 12:38:07 +0200 Subject: Refactor md variables Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/mixins.scss | 4 ++-- app/assets/stylesheets/base/variables.scss | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index 0f661d6b1b6..a2f6c3e21f4 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -55,10 +55,10 @@ } @mixin md-typography { - color: #444; + color: $md-text-color; a { - color: #3084bb; + color: $md-link-color; } img { diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss index 21462b31127..2fc7bf1720a 100644 --- a/app/assets/stylesheets/base/variables.scss +++ b/app/assets/stylesheets/base/variables.scss @@ -2,6 +2,8 @@ $hover: #FFFAF1; $gl-text-color: #54565b; $gl-header-color: #4c4e54; $gl-link-color: #333c48; +$md-text-color: #444; +$md-link-color: #3084bb; $nprogress-color: #c0392b; $gl-font-size: 15px; $list-font-size: 15px; -- 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 df51b029abb92c78c168c9f9e18f5a90c7da32f6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 13:14:25 +0200 Subject: Improve project fork page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/projects.scss | 26 +++++++++----------------- app/views/projects/forks/new.html.haml | 21 +++++++++++---------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d3c84595701..53004fca350 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -203,26 +203,18 @@ ul.nav.nav-projects-tabs { } .fork-namespaces { - .thumbnail { + .fork-thumbnail { + text-align: center; + margin-bottom: $gl-padding; - &.fork-exists-thumbnail { - border-color: #EEE; - - .caption { - color: #999; - } - } - - &.fork-thumbnail { - border-color: #AAA; - - &:hover { - background-color: $hover; - } + .caption { + padding: $gl-padding 0; + min-height: 30px; } - a { - text-decoration: none; + img { + @include border-radius(50%); + max-width: 100px; } } } diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index b7a2ed68e25..cd5f3a5d39e 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -10,21 +10,22 @@ - group.each do |namespace| .col-md-2.col-sm-3 - if fork = namespace.find_fork_of(@project) - .thumbnail.fork-exists-thumbnail + .fork-thumbnail = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do - = image_tag namespace_icon(namespace, 200) + = image_tag namespace_icon(namespace, 100) .caption - %h4=namespace.human_name - %p - = namespace.path + %strong + = namespace.human_name + %div.text-primary + Already forked + - else - .thumbnail.fork-thumbnail + .fork-thumbnail = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do - = image_tag namespace_icon(namespace, 200) + = image_tag namespace_icon(namespace, 100) .caption - %h4=namespace.human_name - %p - = namespace.path + %strong + = namespace.human_name %p.light Fork is a copy of a project repository. -- cgit v1.2.1 From b08c5521f5858d6643d53b5a8e4bc180d2780022 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 13:25:09 +0200 Subject: Style blob show page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/files.scss | 5 ++++- app/assets/stylesheets/pages/diff.scss | 6 +++--- app/assets/stylesheets/pages/tree.scss | 7 ++----- app/views/projects/blob/_blob.html.haml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss index f845342c67b..952d4ecdbe9 100644 --- a/app/assets/stylesheets/generic/files.scss +++ b/app/assets/stylesheets/generic/files.scss @@ -3,7 +3,10 @@ * */ .file-holder { - border: 1px solid $border-color; + margin-left: -$gl-padding; + margin-right: -$gl-padding; + border: none; + border-top: 1px solid #E7E9EE; margin-bottom: 1em; table { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 487b600e31d..5e7e59a6af8 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -1,6 +1,6 @@ .diff-file { - margin-left: -16px; - margin-right: -16px; + margin-left: -$gl-padding; + margin-right: -$gl-padding; border: none; border-bottom: 1px solid #E7E9EE; @@ -8,7 +8,7 @@ position: relative; background: $background-color; border-bottom: 1px solid $border-color; - padding: 10px 15px; + padding: 10px 16px; color: #555; z-index: 10; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 587d09a04a5..d93b43b2045 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -132,17 +132,14 @@ list-style: none; margin: 0; padding: 0; - margin-bottom: 10px; + margin-bottom: 5px; .commit { - padding: 10px 15px; + padding: $gl-padding 0; .commit-row-title { - font-size: 13px; - .commit-row-message { font-weight: normal; - color: #555; } } } diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 65c3ab10e02..b4c7d8b9b71 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -15,7 +15,7 @@ - else = link_to title, '#' -%ul.blob-commit-info.well.hidden-xs +%ul.blob-commit-info.hidden-xs - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) = render blob_commit, project: @project -- cgit v1.2.1 From 059b8828d38e763d9776e55d5b4962f5fc419ddb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Sep 2015 13:36:42 +0200 Subject: Style syntax highlight area for white theme Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/files.scss | 32 +++++++++++++++++++++++++++-- app/assets/stylesheets/highlight/white.scss | 8 ++++++-- app/assets/stylesheets/pages/tree.scss | 30 --------------------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss index 952d4ecdbe9..9dd77747884 100644 --- a/app/assets/stylesheets/generic/files.scss +++ b/app/assets/stylesheets/generic/files.scss @@ -7,6 +7,7 @@ margin-right: -$gl-padding; border: none; border-top: 1px solid #E7E9EE; + border-bottom: 1px solid #E7E9EE; margin-bottom: 1em; table { @@ -52,7 +53,7 @@ } &.wiki { - padding: 25px; + padding: $gl-padding; .highlight { margin-bottom: 9px; @@ -93,7 +94,7 @@ border-right: none; } background: #fff; - padding: 8px; + padding: 10px $gl-padding; } .lines { pre { @@ -103,6 +104,33 @@ border: none; } } + img.avatar { + border: 0 none; + float: none; + margin: 0; + padding: 0; + } + td.blame-commit { + background: #f9f9f9; + min-width: 350px; + + .commit-author-link { + color: #888; + } + } + td.blame-numbers { + pre { + color: #AAA; + white-space: pre; + } + background: #f1f1f1; + border-left: 1px solid #DDD; + } + td.lines { + code { + font-family: $monospace_font; + } + } } &.logs { diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index e0edfb80b42..5de589109bd 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -5,15 +5,19 @@ pre.code.highlight.white, background-color: #fff; color: #333; - pre.highlight, .line-numbers, .line-numbers a { + background-color: $background-color !important; + color: $gl-gray !important; + } + + pre.highlight { background-color: #fff !important; color: #333 !important; } pre.code { - border-left: 1px solid #bbb; + border-left: 1px solid $border-color; } // highlight line via anchor diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index d93b43b2045..71ca37c0cd7 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -78,36 +78,6 @@ } } } - - .blame { - img.avatar { - border: 0 none; - float: none; - margin: 0; - padding: 0; - } - td.blame-commit { - background: #f9f9f9; - min-width: 350px; - - .commit-author-link { - color: #888; - } - } - td.blame-numbers { - pre { - color: #AAA; - white-space: pre; - } - background: #f1f1f1; - border-left: 1px solid #DDD; - } - td.lines { - code { - font-family: $monospace_font; - } - } - } } .tree-ref-holder { -- 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 9d3344adbbfc84ef8abc96366bdfa695293cd6c0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 08:23:10 -0700 Subject: Gracefully handle errors in syntax highlighting by leaving the block unformatted Closes #2433 --- CHANGELOG | 1 + lib/gitlab/markdown/syntax_highlight_filter.rb | 6 +++++- .../markdown/syntax_highlight_filter_spec.rb | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 36ee3e69ab3..2c135d3475c 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) + - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu) - 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/lib/gitlab/markdown/syntax_highlight_filter.rb b/lib/gitlab/markdown/syntax_highlight_filter.rb index 86f4385753a..f9527c7286e 100644 --- a/lib/gitlab/markdown/syntax_highlight_filter.rb +++ b/lib/gitlab/markdown/syntax_highlight_filter.rb @@ -21,7 +21,11 @@ module Gitlab language = node.attr('class') code = node.text - highlighted = block_code(code, language) + begin + highlighted = block_code(code, language) + rescue + highlighted = "
    #{code}
    " + end # Replace the parent `pre` element with the entire highlighted block node.parent.replace(highlighted) diff --git a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb new file mode 100644 index 00000000000..ecef31853f4 --- /dev/null +++ b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +module Gitlab::Markdown + describe SyntaxHighlightFilter do + include FilterSpecHelper + + let(:project) { create(:empty_project) } + let(:reference) { snippet.to_reference } + + it 'highlights valid code blocks' do + result = filter('
    def fun end')
    +      expect(result.to_html).to eq("
    def fun end
    \n") + end + + it 'passes through invalid code blocks' do + allow_any_instance_of(SyntaxHighlightFilter).to receive(:block_code).and_raise(StandardError) + + result = filter('
    This is a test
    ') + expect(result.to_html).to eq('
    This is a test
    ') + end + end +end -- cgit v1.2.1 From 9bda4b8d88fc93b8938d8c4d001d6660d112ae1c Mon Sep 17 00:00:00 2001 From: Petheo Bence Date: Thu, 3 Sep 2015 15:38:54 +0200 Subject: Added service API endpoint to retrieve service parameters --- CHANGELOG | 1 + doc/api/services.md | 160 +++++++++++++++++++++++++++++++++++++ lib/api/services.rb | 10 +++ lib/tasks/services.rake | 9 +++ spec/requests/api/services_spec.rb | 10 +++ 5 files changed, 190 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 36ee3e69ab3..710f6e6891c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ v 8.0.0 (unreleased) - Add support for Crowd - Global Labels that are available to all projects - Fix highlighting of deleted lines in diffs. + - Added service API endpoint to retrieve service parameters (Petheő Bence) v 7.14.1 - Improve abuse reports management from admin area diff --git a/doc/api/services.md b/doc/api/services.md index bc5049dd302..7d45b2cf463 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -27,6 +27,14 @@ Delete Asana service for a project. DELETE /projects/:id/services/asana ``` +### Get Asana service settings + +Get Asana service settings for a project. + +``` +GET /projects/:id/services/asana +``` + ## Assembla Project Management Software (Source Commits Endpoint) @@ -52,6 +60,14 @@ Delete Assembla service for a project. DELETE /projects/:id/services/assembla ``` +### Get Assembla service settings + +Get Assembla service settings for a project. + +``` +GET /projects/:id/services/assembla +``` + ## Atlassian Bamboo CI A continuous integration and build server @@ -81,6 +97,14 @@ Delete Atlassian Bamboo CI service for a project. DELETE /projects/:id/services/bamboo ``` +### Get Atlassian Bamboo CI service settings + +Get Atlassian Bamboo CI service settings for a project. + +``` +GET /projects/:id/services/bamboo +``` + ## Buildkite Continuous integration and deployments @@ -107,6 +131,14 @@ Delete Buildkite service for a project. DELETE /projects/:id/services/buildkite ``` +### Get Buildkite service settings + +Get Buildkite service settings for a project. + +``` +GET /projects/:id/services/buildkite +``` + ## Campfire Simple web-based real-time group chat @@ -133,6 +165,14 @@ Delete Campfire service for a project. DELETE /projects/:id/services/campfire ``` +### Get Campfire service settings + +Get Campfire service settings for a project. + +``` +GET /projects/:id/services/campfire +``` + ## Custom Issue Tracker Custom issue tracker @@ -161,6 +201,14 @@ Delete Custom Issue Tracker service for a project. DELETE /projects/:id/services/custom-issue-tracker ``` +### Get Custom Issue Tracker service settings + +Get Custom Issue Tracker service settings for a project. + +``` +GET /projects/:id/services/custom-issue-tracker +``` + ## Drone CI Drone is a Continuous Integration platform built on Docker, written in Go @@ -187,6 +235,14 @@ Delete Drone CI service for a project. DELETE /projects/:id/services/drone-ci ``` +### Get Drone CI service settings + +Get Drone CI service settings for a project. + +``` +GET /projects/:id/services/drone-ci +``` + ## Emails on push Email the commits and diff of each push to a list of recipients. @@ -213,6 +269,14 @@ Delete Emails on push service for a project. DELETE /projects/:id/services/emails-on-push ``` +### Get Emails on push service settings + +Get Emails on push service settings for a project. + +``` +GET /projects/:id/services/emails-on-push +``` + ## External Wiki Replaces the link to the internal wiki with a link to an external wiki. @@ -237,6 +301,14 @@ Delete External Wiki service for a project. DELETE /projects/:id/services/external-wiki ``` +### Get External Wiki service settings + +Get External Wiki service settings for a project. + +``` +GET /projects/:id/services/external-wiki +``` + ## Flowdock Flowdock is a collaboration web app for technical teams. @@ -261,6 +333,14 @@ Delete Flowdock service for a project. DELETE /projects/:id/services/flowdock ``` +### Get Flowdock service settings + +Get Flowdock service settings for a project. + +``` +GET /projects/:id/services/flowdock +``` + ## Gemnasium Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities. @@ -286,6 +366,14 @@ Delete Gemnasium service for a project. DELETE /projects/:id/services/gemnasium ``` +### Get Gemnasium service settings + +Get Gemnasium service settings for a project. + +``` +GET /projects/:id/services/gemnasium +``` + ## GitLab CI Continuous integration server from GitLab @@ -312,6 +400,14 @@ Delete GitLab CI service for a project. DELETE /projects/:id/services/gitlab-ci ``` +### Get GitLab CI service settings + +Get GitLab CI service settings for a project. + +``` +GET /projects/:id/services/gitlab-ci +``` + ## HipChat Private group chat and IM @@ -341,6 +437,14 @@ Delete HipChat service for a project. DELETE /projects/:id/services/hipchat ``` +### Get HipChat service settings + +Get HipChat service settings for a project. + +``` +GET /projects/:id/services/hipchat +``` + ## Irker (IRC gateway) Send IRC messages, on update, to a list of recipients through an Irker gateway. @@ -371,6 +475,14 @@ Delete Irker (IRC gateway) service for a project. DELETE /projects/:id/services/irker ``` +### Get Irker (IRC gateway) service settings + +Get Irker (IRC gateway) service settings for a project. + +``` +GET /projects/:id/services/irker +``` + ## JIRA Jira issue tracker @@ -400,6 +512,14 @@ Delete JIRA service for a project. DELETE /projects/:id/services/jira ``` +### Get JIRA service settings + +Get JIRA service settings for a project. + +``` +GET /projects/:id/services/jira +``` + ## PivotalTracker Project Management Software (Source Commits Endpoint) @@ -424,6 +544,14 @@ Delete PivotalTracker service for a project. DELETE /projects/:id/services/pivotaltracker ``` +### Get PivotalTracker service settings + +Get PivotalTracker service settings for a project. + +``` +GET /projects/:id/services/pivotaltracker +``` + ## Pushover Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop. @@ -452,6 +580,14 @@ Delete Pushover service for a project. DELETE /projects/:id/services/pushover ``` +### Get Pushover service settings + +Get Pushover service settings for a project. + +``` +GET /projects/:id/services/pushover +``` + ## Redmine Redmine issue tracker @@ -479,6 +615,14 @@ Delete Redmine service for a project. DELETE /projects/:id/services/redmine ``` +### Get Redmine service settings + +Get Redmine service settings for a project. + +``` +GET /projects/:id/services/redmine +``` + ## Slack A team communication tool for the 21st century @@ -505,6 +649,14 @@ Delete Slack service for a project. DELETE /projects/:id/services/slack ``` +### Get Slack service settings + +Get Slack service settings for a project. + +``` +GET /projects/:id/services/slack +``` + ## JetBrains TeamCity CI A continuous integration and build server @@ -534,3 +686,11 @@ Delete JetBrains TeamCity CI service for a project. DELETE /projects/:id/services/teamcity ``` +### Get JetBrains TeamCity CI service settings + +Get JetBrains TeamCity CI service settings for a project. + +``` +GET /projects/:id/services/teamcity +``` + diff --git a/lib/api/services.rb b/lib/api/services.rb index 73645cedea4..d170b3067ed 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -49,6 +49,16 @@ module API end end end + + # Get service settings for project + # + # Example Request: + # + # GET /project/:id/services/gitlab-ci + # + get ':id/services/:service_slug' do + present project_service + end end end end diff --git a/lib/tasks/services.rake b/lib/tasks/services.rake index 3f276a5e12e..39541c0b9c6 100644 --- a/lib/tasks/services.rake +++ b/lib/tasks/services.rake @@ -40,6 +40,15 @@ DELETE /projects/:id/services/<%= service[:dashed_name] %> ``` +### Get <%= service[:title] %> service settings + +Get <%= service[:title] %> service settings for a project. + +``` +GET /projects/:id/services/<%= service[:dashed_name] %> + +``` + <% end %> ERB diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index c297904614a..fb3b235446f 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -47,5 +47,15 @@ describe API::API, api: true do expect(project.send(service_method).activated?).to be_falsey end end + + describe "GET /projects/:id/services/#{service.dasherize}" do + include_context service + + it "should get #{service} settings" do + get api("/projects/#{project.id}/services/#{dashed_service}", user) + + expect(response.status).to eq(200) + end + end end end -- cgit v1.2.1 From c86865c1cf29cb39d0e2896351c3cf6c1925af6d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 10 Sep 2015 13:59:27 -0400 Subject: Add missing 7.14.x CHANGELOG entries --- CHANGELOG | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 402fe61bbce..b41237f8679 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,7 +9,6 @@ v 8.0.0 (unreleased) - Prevent anchors from being hidden by header (Stan Hu) - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu) - Sort issues by creation date in Bitbucket importer (Stan Hu) - - Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu) - Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu) - Improve dropdown positioning on the project home page (Hannes Rosenögger) - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu) @@ -37,15 +36,21 @@ v 8.0.0 (unreleased) - Retrieving oauth token with LDAP credentials - Load Application settings from running database unless env var USE_DB=false - Added Drone CI integration (Kirill Zaitsev) - - Refactored service API and added automatically service docs generator (Kirill Zaitsev) + - Refactored service API and added automatically service docs generator (Kirill Zaitsev) - Added web_url key project hook_attrs (Kirill Zaitsev) - 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 + - Fix bug which IE cannot show image at markdown when the image is raw file of gitlab - Add support for Crowd - Global Labels that are available to all projects - Fix highlighting of deleted lines in diffs. - Added service API endpoint to retrieve service parameters (Petheő Bence) +v 7.14.3 + - No changes + +v 7.14.2 + - Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu) + v 7.14.1 - Improve abuse reports management from admin area - Fix "Reload with full diff" URL button in compare branch view (Stan Hu) -- cgit v1.2.1 From 201f50b94f5fcd5fa2987fe1870947c4d3fa7739 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 11:45:46 -0700 Subject: Fix some CHANGELOG entries --- CHANGELOG | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b41237f8679..f03e7c489a0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -44,6 +44,7 @@ v 8.0.0 (unreleased) - Global Labels that are available to all projects - Fix highlighting of deleted lines in diffs. - Added service API endpoint to retrieve service parameters (Petheő Bence) + - Add FogBugz project import (Jared Szechy) v 7.14.3 - No changes @@ -55,12 +56,9 @@ v 7.14.1 - Improve abuse reports management from admin area - Fix "Reload with full diff" URL button in compare branch view (Stan Hu) - Disabled DNS lookups for SSH in docker image (Rowan Wookey) - -v 7.14.1 (unreleased) - Only include base URL in OmniAuth full_host parameter (Stan Hu) - Fix Error 500 in API when accessing a group that has an avatar (Stan Hu) - Ability to enable SSL verification for Webhooks - - Add FogBugz project import (Jared Szechy) v 7.14.0 - Fix bug where non-project members of the target project could set labels on new merge requests. -- cgit v1.2.1 From 3223a2a5ae477d82784998359f895ccddef42e1d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 11:51:48 -0700 Subject: Bump rouge to 1.10 to remove warning noise and fix other syntax highlighting bugs Closes #2128 Closes #2433 --- CHANGELOG | 1 + Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b41237f8679..8cce3a15438 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) + - Bump rouge to 1.10 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) - 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) diff --git a/Gemfile.lock b/Gemfile.lock index dce7e4964a6..647eb17d974 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -291,7 +291,7 @@ GEM github-markup (~> 1.3.1) gollum-grit_adapter (~> 0.1, >= 0.1.1) nokogiri (~> 1.6.4) - rouge (~> 1.9) + rouge (~> 1.10) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (5.0.1) @@ -544,7 +544,7 @@ GEM netrc (~> 0.7) rinku (1.7.3) rotp (1.6.1) - rouge (1.9.1) + rouge (1.10.0) rqrcode (0.4.2) rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) -- cgit v1.2.1 From 7cbf5e4d18f6ba0bf1afbddcb090fa39837e9529 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 5 Sep 2015 17:50:47 -0400 Subject: Prevent result of SyntaxHighlightFilter being sanitized --- lib/gitlab/markdown/sanitization_filter.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb index 68ed57f6257..54faf981b7d 100644 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ b/lib/gitlab/markdown/sanitization_filter.rb @@ -67,12 +67,16 @@ module Gitlab def clean_spans lambda do |env| - return unless env[:node_name] == 'span' - return unless env[:node].has_attribute?('class') + node = env[:node] - unless has_ancestor?(env[:node], 'pre') - env[:node].remove_attribute('class') + return unless node.name == 'span' + return unless node.has_attribute?('class') + + unless has_ancestor?(node, 'pre') + node.remove_attribute('class') end + + {node_whitelist: [node]} end end end -- cgit v1.2.1 From 404a01ab148417f6314c94d213769dd72fd0589e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 10 Sep 2015 15:28:42 -0400 Subject: Add specs for syntax_highlight JS Also makes it work when given a parent element containing a `.js-syntax-highlight` element so for dynamically-added things like notes or Markdown previews, we can more accurately target just the element we care about. --- app/assets/javascripts/syntax_highlight.coffee | 10 +++++- spec/javascripts/syntax_highlight_spec.js.coffee | 42 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 spec/javascripts/syntax_highlight_spec.js.coffee diff --git a/app/assets/javascripts/syntax_highlight.coffee b/app/assets/javascripts/syntax_highlight.coffee index 71295cd4b08..980f0232d10 100644 --- a/app/assets/javascripts/syntax_highlight.coffee +++ b/app/assets/javascripts/syntax_highlight.coffee @@ -1,3 +1,5 @@ +# Syntax Highlighter +# # Applies a syntax highlighting color scheme CSS class to any element with the # `js-syntax-highlight` class # @@ -6,7 +8,13 @@ #
    # $.fn.syntaxHighlight = -> - $(this).addClass(gon.user_color_scheme) + if $(this).hasClass('js-syntax-highlight') + # Given the element itself, apply highlighting + $(this).addClass(gon.user_color_scheme) + else + # Given a parent element, recurse to any of its applicable children + $children = $(this).find('.js-syntax-highlight') + $children.syntaxHighlight() if $children.length $(document).on 'ready page:load', -> $('.js-syntax-highlight').syntaxHighlight() diff --git a/spec/javascripts/syntax_highlight_spec.js.coffee b/spec/javascripts/syntax_highlight_spec.js.coffee new file mode 100644 index 00000000000..6a73b6bf32c --- /dev/null +++ b/spec/javascripts/syntax_highlight_spec.js.coffee @@ -0,0 +1,42 @@ +#= require syntax_highlight + +describe 'Syntax Highlighter', -> + stubUserColorScheme = (value) -> + window.gon ?= {} + window.gon.user_color_scheme = value + + describe 'on a js-syntax-highlight element', -> + beforeEach -> + fixture.set('
    ') + + it 'applies syntax highlighting', -> + stubUserColorScheme('monokai') + + $('.js-syntax-highlight').syntaxHighlight() + + expect($('.js-syntax-highlight')).toHaveClass('monokai') + + describe 'on a parent element', -> + beforeEach -> + fixture.set """ +
    +
    +
    +
    +
    + """ + + it 'applies highlighting to all applicable children', -> + stubUserColorScheme('monokai') + + $('.parent').syntaxHighlight() + + expect($('.parent, .foo')).not.toHaveClass('monokai') + expect($('.monokai').length).toBe(2) + + it 'prevents an infinite loop when no matches exist', -> + fixture.set('
    ') + + highlight = -> $('div').syntaxHighlight() + + expect(highlight).not.toThrow() -- cgit v1.2.1 From 4e54b82326e0a24c3b5196956f1e957b3f7c02e9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 10 Sep 2015 15:29:56 -0400 Subject: Apply syntax highlighting to Markdown previews --- app/assets/javascripts/dropzone_input.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index a0dcaa8c27a..6f789e668af 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -167,6 +167,7 @@ class @DropzoneInput dataType: "json" ).success (data) -> preview.html data.body + preview.syntaxHighlight() renderReferencedUsers data.references.users -- cgit v1.2.1 From 19c0bf2723bb0895159e4e4e0c6f47bc73157449 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 10 Sep 2015 15:30:06 -0400 Subject: Simplify syntax highlighting of new notes --- app/assets/javascripts/notes.js.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b7f2c63c5a7..ce638c2641b 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -122,8 +122,9 @@ class @Notes # or skip if rendered if @isNewNote(note) @note_ids.push(note.id) - $('ul.main-notes-list').append(note.html) - $('.js-syntax-highlight').syntaxHighlight() + $('ul.main-notes-list'). + append(note.html). + syntaxHighlight() @initTaskList() ### -- cgit v1.2.1 From e3c97ede9663fe7905fcb350875c46526cb4f832 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 10 Sep 2015 16:06:24 -0400 Subject: RU-BO-COOOOOOOOP --- lib/gitlab/markdown/sanitization_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb index 54faf981b7d..e368de7d848 100644 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ b/lib/gitlab/markdown/sanitization_filter.rb @@ -76,7 +76,7 @@ module Gitlab node.remove_attribute('class') end - {node_whitelist: [node]} + { node_whitelist: [node] } end end end -- cgit v1.2.1 From e20529cec4f6de0c2c1589da99abb8714ea1c1c1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 13:38:43 -0700 Subject: Bump rouge to 1.10.1 --- CHANGELOG | 2 +- Gemfile.lock | 4 ++-- config/initializers/rouge_diff_lexer.rb | 24 ------------------------ 3 files changed, 3 insertions(+), 27 deletions(-) delete mode 100644 config/initializers/rouge_diff_lexer.rb diff --git a/CHANGELOG b/CHANGELOG index 8cce3a15438..c325d6b6125 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.0.0 (unreleased) - - Bump rouge to 1.10 to remove warning noise and fix other syntax highlighting bugs (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) - 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) diff --git a/Gemfile.lock b/Gemfile.lock index 647eb17d974..f3091857b7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -291,7 +291,7 @@ GEM github-markup (~> 1.3.1) gollum-grit_adapter (~> 0.1, >= 0.1.1) nokogiri (~> 1.6.4) - rouge (~> 1.10) + rouge (~> 1.10.1) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (5.0.1) @@ -544,7 +544,7 @@ GEM netrc (~> 0.7) rinku (1.7.3) rotp (1.6.1) - rouge (1.10.0) + rouge (1.10.1) rqrcode (0.4.2) rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) diff --git a/config/initializers/rouge_diff_lexer.rb b/config/initializers/rouge_diff_lexer.rb deleted file mode 100644 index fdb2d7b748e..00000000000 --- a/config/initializers/rouge_diff_lexer.rb +++ /dev/null @@ -1,24 +0,0 @@ -# Here until https://github.com/jneen/rouge/pull/297 is merged into Rouge and the gem is updated in GitLab. -module Rouge - module Lexers - class Diff - def self.analyze_text(text) - return 1 if text.start_with?('Index: ') - return 1 if text.start_with?('diff ') - return 0.9 if text.start_with?('--- ') - end - - state :root do - rule(/^ .*\n/, Text) - rule(/^---\n/, Text) - rule(/^\+.*\n/, Generic::Inserted) - rule(/^-+.*\n/, Generic::Deleted) - rule(/^!.*\n/, Generic::Strong) - rule(/^@.*\n/, Generic::Subheading) - rule(/^([Ii]ndex|diff).*\n/, Generic::Heading) - rule(/^=.*\n/, Generic::Heading) - rule(/.*\n/, Text) - end - end - end -end -- cgit v1.2.1 From 267687993a437e0651eb5064c04479e65a43251f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Sep 2015 14:21:14 -0700 Subject: Add comments and clean up test for !1274 --- lib/gitlab/markdown/syntax_highlight_filter.rb | 2 ++ spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/markdown/syntax_highlight_filter.rb b/lib/gitlab/markdown/syntax_highlight_filter.rb index f9527c7286e..8597e02f0de 100644 --- a/lib/gitlab/markdown/syntax_highlight_filter.rb +++ b/lib/gitlab/markdown/syntax_highlight_filter.rb @@ -24,6 +24,8 @@ module Gitlab begin highlighted = block_code(code, language) rescue + # Gracefully handle syntax highlighter bugs/errors to ensure + # users can still access an issue/comment/etc. highlighted = "
    #{code}
    " end diff --git a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb index ecef31853f4..6a490673728 100644 --- a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb +++ b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb @@ -4,9 +4,6 @@ module Gitlab::Markdown describe SyntaxHighlightFilter do include FilterSpecHelper - let(:project) { create(:empty_project) } - let(:reference) { snippet.to_reference } - it 'highlights valid code blocks' do result = filter('
    def fun end')
           expect(result.to_html).to eq("
    def fun end
    \n") -- cgit v1.2.1 From 9995f0806b29934cf498607f59d2c5ec358a0d5a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 1 Sep 2015 00:56:40 -0700 Subject: Import forked repositories asynchronously to prevent large repositories from timing out Use import_status to track async import status and give feedback to the user Closes #2388 Closes #2400 --- CHANGELOG | 1 + app/controllers/projects/forks_controller.rb | 12 ++++++---- app/models/project.rb | 24 +++++++++++-------- app/services/projects/create_service.rb | 4 +--- app/workers/repository_fork_worker.rb | 34 +++++++++++++++++++++++++++ features/steps/project/fork.rb | 2 +- spec/services/projects/create_service_spec.rb | 11 +++++++++ spec/services/projects/fork_service_spec.rb | 5 ++-- spec/workers/repository_fork_worker_spec.rb | 29 +++++++++++++++++++++++ 9 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 app/workers/repository_fork_worker.rb create mode 100644 spec/workers/repository_fork_worker_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 2ae83d5b3ce..ddfd384f8c8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.0.0 (unreleased) - 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) - Fix broken Wiki Page History (Stan Hu) + - Import forked repositories asynchronously to prevent large repositories from timing out (Stan Hu) - Prevent anchors from being hidden by header (Stan Hu) - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu) - Sort issues by creation date in Bitbucket importer (Stan Hu) diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 9e72597ea87..8a785076bb7 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -13,10 +13,14 @@ class Projects::ForksController < Projects::ApplicationController @forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute if @forked_project.saved? && @forked_project.forked? - redirect_to( - namespace_project_path(@forked_project.namespace, @forked_project), - notice: 'Project was successfully forked.' - ) + if @forked_project.import_in_progress? + redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project) + else + redirect_to( + namespace_project_path(@forked_project.namespace, @forked_project), + notice: 'Project was successfully forked.' + ) + end else render :error end diff --git a/app/models/project.rb b/app/models/project.rb index 072d7d73f41..49525eb9227 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -144,7 +144,7 @@ class Project < ActiveRecord::Base validates_uniqueness_of :path, scope: :namespace_id validates :import_url, format: { with: /\A#{URI.regexp(%w(ssh git http https))}\z/, message: 'should be a valid url' }, - if: :import? + if: :external_import? validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create validate :avatar_type, @@ -275,7 +275,13 @@ class Project < ActiveRecord::Base end def add_import_job - RepositoryImportWorker.perform_in(2.seconds, id) + if forked? + unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) + import_fail + end + else + RepositoryImportWorker.perform_in(2.seconds, id) + end end def clear_import_data @@ -283,6 +289,10 @@ class Project < ActiveRecord::Base end def import? + external_import? || forked? + end + + def external_import? import_url.present? end @@ -702,14 +712,8 @@ class Project < ActiveRecord::Base end def create_repository - if forked? - if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path) - true - else - errors.add(:base, 'Failed to fork repository via gitlab-shell') - false - end - else + # Forked import is handled asynchronously + unless forked? if gitlab_shell.add_repository(path_with_namespace) true else diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 1bb2462565a..e54a13ed6c5 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -55,9 +55,7 @@ module Projects @project.save if @project.persisted? && !@project.import? - unless @project.create_repository - raise 'Failed to create repository' - end + raise 'Failed to create repository' unless @project.create_repository end end diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb new file mode 100644 index 00000000000..acd1c43f06b --- /dev/null +++ b/app/workers/repository_fork_worker.rb @@ -0,0 +1,34 @@ +class RepositoryForkWorker + include Sidekiq::Worker + include Gitlab::ShellAdapter + + sidekiq_options queue: :gitlab_shell + + def perform(project_id, source_path, target_path) + project = Project.find_by_id(project_id) + + unless project.present? + logger.error("Project #{project_id} no longer exists!") + return + end + + result = gitlab_shell.fork_repository(source_path, target_path) + + unless result + logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}") + project.import_fail + project.save + return + end + + if project.valid_repo? + ProjectCacheWorker.perform_async(project.id) + project.import_finish + else + project.import_fail + logger.error("Project #{id} had an invalid repository after fork") + end + + project.save + end +end diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index 0e433781d7a..370960845cc 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -15,7 +15,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps end step 'I should see the forked project page' do - expect(page).to have_content "Project was successfully forked." + expect(page).to have_content "Forked from" end step 'I already have a project named "Shop" in my namespace' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index ff4ed2dd484..25277f07482 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -96,6 +96,17 @@ describe Projects::CreateService do expect(project.saved?).to be(true) end end + + context 'repository creation' do + it 'should synchronously create the repository' do + expect_any_instance_of(Project).to receive(:create_repository) + + project = create_project(@user, @opts) + expect(project).to be_valid + expect(project.owner).to eq(@user) + expect(project.namespace).to eq(@user.namespace) + end + end end def create_project(user, opts) diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index c04e842c67e..7c4bb74b77f 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -28,8 +28,7 @@ describe Projects::ForkService do context 'fork project failure' do it "fails due to transaction failure" do @to_project = fork_project(@from_project, @to_user, false) - expect(@to_project.errors).not_to be_empty - expect(@to_project.errors[:base]).to include("Failed to fork repository via gitlab-shell") + expect(@to_project.import_failed?) end end @@ -100,7 +99,7 @@ describe Projects::ForkService do end def fork_project(from_project, user, fork_success = true, params = {}) - allow_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(fork_success) + allow(RepositoryForkWorker).to receive(:perform_async).and_return(fork_success) Projects::ForkService.new(from_project, user, params).execute end end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb new file mode 100644 index 00000000000..aa031106968 --- /dev/null +++ b/spec/workers/repository_fork_worker_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe RepositoryForkWorker do + let(:project) { create(:project) } + let(:fork_project) { create(:project, forked_from_project: project) } + + subject { RepositoryForkWorker.new } + + describe "#perform" do + it "creates a new repository from a fork" do + expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).with( + project.path_with_namespace, + fork_project.namespace.path). + and_return(true) + expect(ProjectCacheWorker).to receive(:perform_async) + + subject.perform(project.id, + project.path_with_namespace, + fork_project.namespace.path) + end + + it "handles bad fork" do + expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false) + subject.perform(project.id, + project.path_with_namespace, + fork_project.namespace.path) + 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 001738e992515400453abd7575493e382e5d1e0f Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 12 Sep 2015 14:01:01 +0200 Subject: Improve english of error message --- app/services/files/create_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 91d715b2d63..c2da0c30bad 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -22,7 +22,7 @@ module Files 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 -- cgit v1.2.1 From f07fabc1cd9db2bc03dd455e404b70bb17a16de9 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Sat, 12 Sep 2015 09:50:44 -0500 Subject: Keep add-diff-note button the same size on hover. Fixes #2497 --- app/assets/stylesheets/pages/notes.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 2544356a5f6..2a77f065aed 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -235,8 +235,6 @@ ul.notes { filter: alpha(opacity=0); &:hover { - width: 38px; - font-size: 20px; background: $gl-info; color: #FFF; @include show-add-diff-note; -- cgit v1.2.1 From 8e523f49a0b6b251a06dbcabfb59d7e905b87d03 Mon Sep 17 00:00:00 2001 From: karen Carias Date: Sun, 13 Sep 2015 21:27:29 -0700 Subject: added note --- doc/raketasks/backup_restore.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 6a68c8e8286..4ff5e74d438 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -369,4 +369,7 @@ For more information see similar questions on postgresql issue tracker[here](htt ## Note This documentation is for GitLab CE. -We backup GitLab.com and make sure your data is secure, but you can't use these methods to export / backup your data yourself from GitLab.com. +We backup GitLab.com and make sure your data is secure, but you can't use these methods +to export / backup your data yourself from GitLab.com. + +Issues are stored in the database. They can't be stored in Git itself. -- cgit v1.2.1 From ef0f4b5144fd85465f6a94286b2d67cdb271be0e Mon Sep 17 00:00:00 2001 From: karen Carias Date: Sun, 13 Sep 2015 21:34:31 -0700 Subject: note about global keys --- doc/ssh/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 7cdcd11c04c..cb4c46edbf0 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -72,6 +72,9 @@ access can happen through being a direct member of the project, or through a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more information. +Deploy keys are added by project. It is not possible yet to have default +or global deploy keys. + ## Applications ### Eclipse -- cgit v1.2.1 From 0ced0ace28a56787442a6db5915fc43e6665f3ea Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 14 Sep 2015 09:54:38 +0300 Subject: Add LATEST_TAG variable --- doc/update/patch_versions.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 22b9be059d6..a66a863f6c4 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -1,7 +1,7 @@ # Universal update guide for patch versions *Make sure you view this [upgrade guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/patch_versions.md) from the `master` branch for the most up to date instructions.* -For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/). +For example from 7.14.0 to 7.14.3, also see the [semantic versioning specification](http://semver.org/). ### 0. Backup @@ -23,17 +23,16 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production cd /home/git/gitlab sudo -u git -H git fetch --all sudo -u git -H git checkout -- Gemfile.lock db/schema.rb -sudo -u git -H git checkout LATEST_TAG +LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`) +sudo -u git -H git checkout $LATEST_TAG -b $LATEST_TAG ``` -Replace LATEST_TAG with the latest GitLab tag you want to upgrade to, for example `v6.6.3`. - ### 3. Update gitlab-shell to the corresponding version ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` +sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` ``` ### 4. Install libs, migrations, etc. -- 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 4c060074b792519e23403c0b21d4c2b3b49a8f36 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 11:05:23 +0200 Subject: Skip auth for group page but return auth for other group pages --- app/assets/stylesheets/generic/sidebar.scss | 2 +- app/controllers/groups_controller.rb | 2 +- app/models/ability.rb | 2 +- app/models/group.rb | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss index 22720c2e1d5..fdc554f3a91 100644 --- a/app/assets/stylesheets/generic/sidebar.scss +++ b/app/assets/stylesheets/generic/sidebar.scss @@ -146,7 +146,6 @@ } .collapse-nav a { - left: 0px; width: $sidebar_collapsed_width; } @@ -165,6 +164,7 @@ width: $sidebar_width; position: fixed; bottom: 0; + left: 0; font-size: 13px; background: transparent; height: 40px; diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 279c6ef0f4d..9f4702c6f53 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -4,7 +4,7 @@ class GroupsController < Groups::ApplicationController before_action :group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:new, :create] + before_action :authorize_read_group!, except: [:show, :new, :create] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] diff --git a/app/models/ability.rb b/app/models/ability.rb index 64cfdb6ea89..f8e5afa9b01 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -54,7 +54,7 @@ class Ability nil end - if group + if group && group.public_profile? [:read_group] else [] diff --git a/app/models/group.rb b/app/models/group.rb index 702d7825d57..9cd146bb73b 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -119,6 +119,10 @@ class Group < Namespace end end + def public_profile? + projects.public_only.any? + end + def post_create_hook Gitlab::AppLogger.info("Group \"#{name}\" was created") -- 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 4e1c6019c8f0d3b00e32fd6cd546d2ef445a285d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 11:16:54 +0200 Subject: Allow access to group from root url --- app/controllers/namespaces_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/namespaces_controller.rb b/app/controllers/namespaces_controller.rb index 83eec1bf4a2..282012c60a1 100644 --- a/app/controllers/namespaces_controller.rb +++ b/app/controllers/namespaces_controller.rb @@ -14,7 +14,7 @@ class NamespacesController < ApplicationController if user redirect_to user_path(user) - elsif group && can?(current_user, :read_group, group) + elsif group redirect_to group_path(group) elsif current_user.nil? authenticate_user! -- 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 1d2173819bf004eaf9676aeb72b2ae6f79db9a64 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 11:41:19 +0200 Subject: Show activity/projects depends on access --- app/views/groups/show.html.haml | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 7fd1b3f18c8..79bfb1ad177 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -16,22 +16,26 @@ = render 'shared/show_aside' - .row - %section.activities.col-md-7 - .hidden-xs - - if current_user - = render "events/event_last_push", event: @last_push + - if can?(current_user, :read_group, @group) + .row + %section.activities.col-md-7 + .hidden-xs + - if current_user + = render "events/event_last_push", event: @last_push - %ul.nav.nav-pills.event_filter.pull-right - %li - = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do - %i.fa.fa-rss + %ul.nav.nav-pills.event_filter.pull-right + %li + = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do + %i.fa.fa-rss - = render 'shared/event_filter' - %hr + = render 'shared/event_filter' + %hr - .content_list - = spinner - - if @projects.any? + .content_list + = spinner %aside.side.col-md-5 = render "projects", projects: @projects + - else + %p + = icon('lock') + This is a private group -- cgit v1.2.1 From 66840f03cc3444c1ce5027e654632d1a65845752 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 12:15:11 +0200 Subject: Fix tests for group access --- spec/features/security/group_access_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb index 3f708b5ebe7..4b78e3a61f0 100644 --- a/spec/features/security/group_access_spec.rb +++ b/spec/features/security/group_access_spec.rb @@ -118,7 +118,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -128,7 +128,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end end @@ -168,7 +168,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -178,7 +178,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end end @@ -218,7 +218,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end context 'with no projects' do @@ -228,7 +228,7 @@ describe 'Group access', feature: true do it { is_expected.to be_allowed_for group_member(:guest) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } - it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_denied_for :visitor } end end -- cgit v1.2.1 From b8225324ef24eeb6f0b0b6cefb54282788a80210 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 12:46:24 +0200 Subject: Hide group sidebar menu if user cant access it --- app/views/layouts/nav/_group.html.haml | 59 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 695ce68a201..6e0ced2e75e 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -12,34 +12,35 @@ = icon('dashboard fw') %span Group - - if current_user - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do - = icon('clock-o fw') + - if can?(current_user, :read_group, @group) + - if current_user + = nav_link(controller: [:group, :milestones]) do + = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do + = icon('exclamation-circle fw') %span - Milestones - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do - = icon('exclamation-circle fw') - %span - Issues - - if current_user - %span.count= Issue.opened.of_group(@group).count - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do - = icon('tasks fw') - %span - Merge Requests - - if current_user - %span.count= MergeRequest.opened.of_group(@group).count - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do - = icon('users fw') - %span - Members - - if can?(current_user, :admin_group, @group) - = nav_link(html_options: { class: "separate-item" }) do - = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do - = icon ('cogs fw') + Issues + - if current_user + %span.count= Issue.opened.of_group(@group).count + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do + = icon('tasks fw') + %span + Merge Requests + - if current_user + %span.count= MergeRequest.opened.of_group(@group).count + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do + = icon('users fw') %span - Settings + Members + - if can?(current_user, :admin_group, @group) + = nav_link(html_options: { class: "separate-item" }) do + = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do + = icon ('cogs fw') + %span + Settings -- 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 db197aa61a3451bbe8eff1dea5dc992997b11b61 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 14:16:08 +0200 Subject: Fix tests and improve wording --- app/views/groups/show.html.haml | 3 +-- spec/controllers/namespaces_controller_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 79bfb1ad177..2e81dffcee5 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -37,5 +37,4 @@ = render "projects", projects: @projects - else %p - = icon('lock') - This is a private group + This group does not have public projects diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 74702f93302..77436958711 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -84,10 +84,10 @@ describe NamespacesController do end context "when the user doesn't have access to the project" do - it "responds with status 404" do + it "redirects to the group's page" do get :show, id: group.path - expect(response.status).to eq(404) + expect(response).to redirect_to(group_path(group)) end end end -- cgit v1.2.1 From 3e97de838c6a2a37e1daa5ab8c3cd1fa350b1a7e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 15:01:04 +0200 Subject: Hide search panel for group page if you dont have access --- app/views/groups/show.html.haml | 3 +++ app/views/layouts/header/_default.html.haml | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 2e81dffcee5..a9ba9d2ba10 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,3 +1,6 @@ +- unless can?(current_user, :read_group, @group) + - @disable_search_panel = true + = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3892f71c0e3..c31b1cbe9a8 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -7,8 +7,9 @@ .navbar-collapse.collapse %ul.nav.navbar-nav.pull-right - %li.hidden-sm.hidden-xs - = render 'layouts/search' + - unless @disable_search_panel + %li.hidden-sm.hidden-xs + = render 'layouts/search' %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('search') -- cgit v1.2.1 From 539f30ddcfd0cdbe54525e80ee4a7ac46df2bcf0 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 14 Sep 2015 08:45:16 -0500 Subject: Fix tooltip display in list views --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a803b66c502..c3da54fd554 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -201,7 +201,7 @@ module ApplicationHelper 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 } + data: { toggle: 'tooltip', placement: placement, container: 'body' } element += javascript_tag "$('.js-timeago').timeago()" unless skip_js -- 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 0c8f07774c2a128aa8aff6be00ed91be2a08cddb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 19:46:58 +0200 Subject: Add page titles to header for group and project Signed-off-by: Dmitriy Zaporozhets --- app/helpers/gitlab_routing_helper.rb | 8 ++++ app/helpers/projects_helper.rb | 69 ++++++++++++++++++++++++-------- app/views/groups/edit.html.haml | 2 + app/views/groups/projects.html.haml | 2 + app/views/layouts/nav/_project.html.haml | 4 +- app/views/layouts/project.html.haml | 2 +- 6 files changed, 67 insertions(+), 20 deletions(-) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index d0fae255a04..e0816f4e714 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -17,6 +17,14 @@ module GitlabRoutingHelper namespace_project_path(project.namespace, project, *args) end + def project_files_path(project, *args) + namespace_project_tree_path(project.namespace, project, @ref || project.repository.root_ref) + end + + def project_commits_path(project, *args) + namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref) + end + def activity_project_path(project, *args) activity_namespace_project_path(project.namespace, project, *args) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index ab9b068de05..268c563700c 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -43,24 +43,22 @@ module ProjectsHelper 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)) + def project_title(project, name = nil, url = nil) + namespace_link = + if project.group + link_to(simple_sanitize(project.group.name), group_path(project.group)) + else + owner = project.namespace.owner + link_to(simple_sanitize(owner.name), user_path(owner)) end + + project_link = link_to(simple_sanitize(project.name), project_path(project)) + + full_title = namespace_link + ' / ' + project_link + full_title += ' · '.html_safe + link_to(simple_sanitize(name), url) if name + + content_tag :span do + full_title end end @@ -315,6 +313,43 @@ module ProjectsHelper end end + def detect_project_title(project) + current_ref = @ref || project.repository.root_ref + + name, url = + if current_controller? 'wikis' + ['Wiki', get_project_wiki_path(project)] + elsif current_controller? 'project_members' + ['Members', namespace_project_project_members_path(project.namespace, project)] + elsif current_controller? 'labels' + ['Labels', namespace_project_labels_path(project.namespace, project)] + elsif current_controller? 'members' + ['Members', project_files_path(project)] + elsif current_controller? 'commits' + ['Commits', project_commits_path(project)] + elsif current_controller? 'graphs' + ['Graphs', namespace_project_graph_path(project.namespace, project, current_ref)] + elsif current_controller? 'network' + ['Network', namespace_project_network_path(project.namespace, project, current_ref)] + elsif current_controller? 'milestones' + ['Milestones', namespace_project_milestones_path(project.namespace, project)] + elsif current_controller? 'snippets' + ['Snippets', namespace_project_snippets_path(project.namespace, project)] + elsif current_controller? 'issues' + ['Issues', namespace_project_issues_path(project.namespace, project)] + elsif current_controller? 'merge_requests' + ['Merge Requests', namespace_project_merge_requests_path(project.namespace, project)] + elsif current_controller? 'tree', 'blob' + ['Files', project_files_path(project)] + elsif current_path? 'projects#activity' + ['Activity', activity_project_path(project)] + else + [nil, nil] + end + + project_title(project, name, url) + end + private def filename_path(project, filename) diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index ac7d9ba0f4f..ae8fc9f85f0 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,4 +1,6 @@ +- header_title group_title(@group, "Settings", edit_group_path(@group)) - @blank_container = true + .panel.panel-default .panel-heading %strong= @group.name diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index d06cfa7ff9f..f1d507a50c7 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,4 +1,6 @@ - page_title "Projects" +- header_title group_title(@group, "Projects", projects_group_path(@group)) + .panel.panel-default .panel-heading %strong= @group.name diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 1d22a7442e3..2159d931da2 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -26,14 +26,14 @@ Activity - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do + = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do = icon('files-o fw') %span Files - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches)) do - = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do + = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do = icon('history fw') %span Commits diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 44afa33dfe5..5c4dd67f0ec 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -1,5 +1,5 @@ - page_title @project.name_with_namespace -- header_title project_title(@project) +- header_title detect_project_title(@project) - sidebar "project" unless sidebar - content_for :scripts_body_top do -- cgit v1.2.1 From d3be65a811f1e5e30cba919e65de162217d9f7cc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 20:08:35 +0200 Subject: Remove repeating titles on members and label pages Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/blocks.scss | 2 +- app/views/projects/labels/index.html.haml | 15 ++++---- app/views/projects/project_members/index.html.haml | 42 +++++++++++----------- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss index 48d9f890f62..1bd016e0df2 100644 --- a/app/assets/stylesheets/generic/blocks.scss +++ b/app/assets/stylesheets/generic/blocks.scss @@ -51,6 +51,6 @@ } .oneline { - line-height: 44px; + line-height: 42px; } } diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index d44fe486212..284adb40e97 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,14 +1,15 @@ - page_title "Labels" -- if can? current_user, :admin_label, @project - = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do - New label -%h3.page-title - Labels -%hr + +.gray-content-block.top-block + - if can? current_user, :admin_label, @project + = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do + New label + .oneline + Labels can be applied to issues and merge requests. .labels - if @labels.present? - %ul.bordered-list.manage-labels-list + %ul.content-list.manage-labels-list = render @labels = paginate @labels, theme: 'gitlab' - else diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 162583e4b1d..a40d1513671 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,30 +1,28 @@ - page_title "Members" -%h3.page-title - Users with access to this project -%p.light +.gray-content-block.top-block + .clearfix.js-toggle-container + = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' } + = button_tag 'Search', class: 'btn' + + - if can?(current_user, :admin_project_member, @project) + %span.pull-right + = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do + Add members + %i.fa.fa-chevron-down + = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do + Import members + + .js-toggle-content.hide.new-group-member-holder + = render "new_project_member" + +%p.prepend-top-default.light + Users with access to this project are listed below. Read more about project permissions %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" -%hr - -.clearfix.js-toggle-container - = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do - .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' } - = button_tag 'Search', class: 'btn' - - - if can?(current_user, :admin_project_member, @project) - %span.pull-right - = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do - Add members - %i.fa.fa-chevron-down - = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do - Import members - - .js-toggle-content.hide.new-group-member-holder - = render "new_project_member" - = render "team", members: @project_members - if @group -- cgit v1.2.1 From b5c681c0c931df9408dc266a5d2d0326e23c7cb8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 14 Sep 2015 20:23:01 +0200 Subject: Add current_ref helper method Signed-off-by: Dmitriy Zaporozhets --- app/helpers/projects_helper.rb | 6 ++++-- app/views/layouts/nav/_project.html.haml | 4 ++-- app/views/projects/commits/_head.html.haml | 4 ++-- app/views/projects/show.html.haml | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 268c563700c..6a2de0de77c 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -313,9 +313,11 @@ module ProjectsHelper end end - def detect_project_title(project) - current_ref = @ref || project.repository.root_ref + def current_ref + @ref || @repository.try(:root_ref) + end + def detect_project_title(project) name, url = if current_controller? 'wikis' ['Wiki', get_project_wiki_path(project)] diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2159d931da2..8ce46d4865b 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -40,14 +40,14 @@ - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do = icon('code-fork fw') %span Network - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do + = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do = icon('area-chart fw') %span Graphs diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 50c0fd6803d..a849bf84698 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -1,10 +1,10 @@ %ul.center-top-menu = nav_link(controller: [:commit, :commits]) do - = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do + = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do Commits %span.badge= number_with_delimiter(@repository.commit_count) = nav_link(controller: :compare) do - = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref) do + = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do Compare = nav_link(html_options: {class: branches_tab_class}) do diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index d5c4ee71978..aa5914e3ed9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -14,7 +14,7 @@ .project-stats.gray-content-block %ul.nav.nav-pills %li - = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do + = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do = pluralize(number_with_delimiter(@project.commit_count), 'commit') %li = link_to namespace_project_branches_path(@project.namespace, @project) do -- 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 84508c40b7a132b775717d2aeef313d4e370a2b9 Mon Sep 17 00:00:00 2001 From: Darby Date: Mon, 14 Sep 2015 16:37:11 -0700 Subject: PAtrially working notification button --- app/controllers/projects_controller.rb | 1 + app/helpers/notifications_helper.rb | 31 ++++++++++++++++++++++ app/views/projects/_home_panel.html.haml | 2 ++ .../projects/buttons/_notifications.html.haml | 15 +++++++++++ 4 files changed, 49 insertions(+) create mode 100644 app/views/projects/buttons/_notifications.html.haml diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index dafc11d0707..2c80f237f86 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -82,6 +82,7 @@ class ProjectsController < ApplicationController if @project.empty_repo? render 'projects/empty' else + @membership_id = @project.project_members.where(user_id: current_user.id).first render :show end else diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 2f8e64c375f..db6fe3b29ed 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -12,4 +12,35 @@ module NotificationsHelper icon('circle-o', class: 'ns-default') end end + + def notification_list_item(notification_level) + case notification_level + when Notification::N_DISABLED + content_tag(:li) do + icon('microphone-slash') do + 'Disabled' + end + end + when Notification::N_PARTICIPATING + content_tag(:li) do + icon('volume-up') do + 'Participating' + end + end + when Notification::N_WATCH + content_tag(:li) do + icon('globe') do + 'Watch' + end + end + when Notification::N_MENTION + content_tag(:li) do + icon('at') do + 'Mention' + end + end + else + # do nothing + end + end end diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b93036e78e6..67a00b4ebef 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -25,6 +25,8 @@ = icon('download fw') Download + = render 'projects/buttons/notifications' + = render 'projects/buttons/dropdown' = render "shared/clone_panel" diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml new file mode 100644 index 00000000000..fb30868fc6b --- /dev/null +++ b/app/views/projects/buttons/_notifications.html.haml @@ -0,0 +1,15 @@ +- if current_user and !@membership_id.nil? + %span.dropdown + = form_tag profile_notifications_path, method: :put, remote: true do + = hidden_field_tag :notification_type, 'project' + = hidden_field_tag :notification_id, @membership_id + %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} + = icon('bell') + Notifications + %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown + - Notification.notification_levels.each do | level | + = notification_list_item(level) + + + + -- cgit v1.2.1 From ab56718feb0e155ae889afe900a009594f8acfa1 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 18:40:47 -0500 Subject: Fixed notification level list item helper. --- app/helpers/notifications_helper.rb | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index db6fe3b29ed..3cc59f9ca5b 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -17,27 +17,23 @@ module NotificationsHelper case notification_level when Notification::N_DISABLED content_tag(:li) do - icon('microphone-slash') do - 'Disabled' - end + icon('microphone-slash') + 'Disabled' end when Notification::N_PARTICIPATING content_tag(:li) do - icon('volume-up') do - 'Participating' - end + icon('volume-up') + 'Participating' end when Notification::N_WATCH content_tag(:li) do - icon('globe') do - 'Watch' - end + icon('globe') + 'Watch' end when Notification::N_MENTION content_tag(:li) do - icon('at') do - 'Mention' - end + icon('at') + 'Mention' end else # do nothing -- 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 adcae6ebd597c731b22a748b882b668e6301763f Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 19:27:54 -0500 Subject: Notification level can now be saved from within the project view. --- app/assets/javascripts/project.js.coffee | 6 ++++++ app/assets/stylesheets/pages/projects.scss | 4 ++++ app/controllers/projects_controller.rb | 2 +- app/helpers/notifications_helper.rb | 20 ++++++++++++-------- app/models/notification.rb | 2 +- app/views/projects/buttons/_notifications.html.haml | 19 ++++++++----------- 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 39a433dfc91..94380ccac7b 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -24,3 +24,9 @@ class @Project $.cookie('hide_no_password_message', 'false', { path: path }) $(@).parents('.no-password-message').remove() e.preventDefault() + + $('.update-notification').on 'click', (e) -> + e.preventDefault() + level = $(this).data('notification-level') + $('#notification_level').val(level) + $('#notification-form').submit() \ No newline at end of file diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 361fd63bc79..8021b3bbffa 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -338,3 +338,7 @@ pre.light-well { margin-top: -1px; } } + +.inline-form { + display: inline-block; +} diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 2c80f237f86..fad5a706f49 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -82,7 +82,7 @@ class ProjectsController < ApplicationController if @project.empty_repo? render 'projects/empty' else - @membership_id = @project.project_members.where(user_id: current_user.id).first + @membership_id = @project.project_members.where(user_id: current_user.id).first.id render :show end else diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 3cc59f9ca5b..4aa9ccedda0 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -17,23 +17,27 @@ module NotificationsHelper case notification_level when Notification::N_DISABLED content_tag(:li) do - icon('microphone-slash') - 'Disabled' + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do + icon('microphone-slash', text: 'Disabled') + end end when Notification::N_PARTICIPATING content_tag(:li) do - icon('volume-up') - 'Participating' + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do + icon('volume-up', text: 'Participating') + end end when Notification::N_WATCH content_tag(:li) do - icon('globe') - 'Watch' + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do + icon('globe', text: 'Watch') + end end when Notification::N_MENTION content_tag(:li) do - icon('at') - 'Mention' + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do + icon('at', text: 'Mention') + end end else # do nothing diff --git a/app/models/notification.rb b/app/models/notification.rb index 1395274173d..828378655ce 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -12,7 +12,7 @@ class Notification class << self def notification_levels - [N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION] + [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH] end def options_with_labels diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index fb30868fc6b..76e462a8986 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,15 +1,12 @@ - if current_user and !@membership_id.nil? - %span.dropdown - = form_tag profile_notifications_path, method: :put, remote: true do - = hidden_field_tag :notification_type, 'project' - = hidden_field_tag :notification_id, @membership_id + = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do + = hidden_field_tag :notification_type, 'project' + = hidden_field_tag :notification_id, @membership_id + = hidden_field_tag :notification_level + %span.dropdown %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} = icon('bell') Notifications - %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - - Notification.notification_levels.each do | level | - = notification_list_item(level) - - - - + %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown + - Notification.notification_levels.each do |level| + = notification_list_item(level) \ No newline at end of file -- cgit v1.2.1 From dd2ffafea4c4cd0a2c9ccebe6d52e00afe371535 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 19:30:58 -0500 Subject: Added spacing to icons in Notification drop down --- app/helpers/notifications_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 4aa9ccedda0..0e4fd039831 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -18,25 +18,25 @@ module NotificationsHelper when Notification::N_DISABLED content_tag(:li) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do - icon('microphone-slash', text: 'Disabled') + icon('microphone-slash fw', text: 'Disabled') end end when Notification::N_PARTICIPATING content_tag(:li) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do - icon('volume-up', text: 'Participating') + icon('volume-up fw', text: 'Participating') end end when Notification::N_WATCH content_tag(:li) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do - icon('globe', text: 'Watch') + icon('globe fw', text: 'Watch') end end when Notification::N_MENTION content_tag(:li) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do - icon('at', text: 'Mention') + icon('at fw', text: 'Mention') end end else -- cgit v1.2.1 From 8a6fb46dff970d599860b06a6c9105c503cbb89c Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 19:40:45 -0500 Subject: Corrected `case` indentation to conform with Rubocop --- app/helpers/notifications_helper.rb | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 0e4fd039831..829993fd77f 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -15,32 +15,32 @@ module NotificationsHelper def notification_list_item(notification_level) case notification_level - when Notification::N_DISABLED - content_tag(:li) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do - icon('microphone-slash fw', text: 'Disabled') - end + when Notification::N_DISABLED + content_tag(:li) do + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do + icon('microphone-slash fw', text: 'Disabled') end - when Notification::N_PARTICIPATING - content_tag(:li) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do - icon('volume-up fw', text: 'Participating') - end + end + when Notification::N_PARTICIPATING + content_tag(:li) do + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do + icon('volume-up fw', text: 'Participating') end - when Notification::N_WATCH - content_tag(:li) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do - icon('globe fw', text: 'Watch') - end + end + when Notification::N_WATCH + content_tag(:li) do + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do + icon('globe fw', text: 'Watch') end - when Notification::N_MENTION - content_tag(:li) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do - icon('at fw', text: 'Mention') - end + end + when Notification::N_MENTION + content_tag(:li) do + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do + icon('at fw', text: 'Mention') end - else - # do nothing + end + else + # do nothing end end end -- cgit v1.2.1 From 01cc20378be1b77bd2a40a6af257e2cf116ce26a Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 20:33:24 -0500 Subject: Notification dropdown now shows currently active level, and also shows what the actual value of `global` is, if the project has the notification level set to global. --- app/assets/javascripts/project.js.coffee | 4 +++- app/controllers/projects_controller.rb | 4 +++- app/helpers/notifications_helper.rb | 19 ++++++++++++++----- app/views/projects/buttons/_notifications.html.haml | 6 +++--- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 94380ccac7b..f524627e0be 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -29,4 +29,6 @@ class @Project e.preventDefault() level = $(this).data('notification-level') $('#notification_level').val(level) - $('#notification-form').submit() \ No newline at end of file + $('#notification-form').submit() + $(this).parents('ul').find('li.active').removeClass('active') + $(this).parent().addClass('active') \ No newline at end of file diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index fad5a706f49..14c3a6028c9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -82,7 +82,9 @@ class ProjectsController < ApplicationController if @project.empty_repo? render 'projects/empty' else - @membership_id = @project.project_members.where(user_id: current_user.id).first.id + unless current_user.nil? + @membership = @project.project_members.where(user_id: current_user.id).first + end render :show end else diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 829993fd77f..48dc198e4e2 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -13,28 +13,28 @@ module NotificationsHelper end end - def notification_list_item(notification_level) + def notification_list_item(notification_level, user_membership) case notification_level when Notification::N_DISABLED - content_tag(:li) do + content_tag(:li, class: active_level_for(user_membership, 'disabled?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do icon('microphone-slash fw', text: 'Disabled') end end when Notification::N_PARTICIPATING - content_tag(:li) do + content_tag(:li, class: active_level_for(user_membership, 'participating?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do icon('volume-up fw', text: 'Participating') end end when Notification::N_WATCH - content_tag(:li) do + content_tag(:li, class: active_level_for(user_membership, 'watch?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do icon('globe fw', text: 'Watch') end end when Notification::N_MENTION - content_tag(:li) do + content_tag(:li, class: active_level_for(user_membership, 'mention?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do icon('at fw', text: 'Mention') end @@ -43,4 +43,13 @@ module NotificationsHelper # do nothing end end + + def active_level_for(user_membership, level) + value = Notification.new(user_membership) + if value.global? + return 'active' if current_user.notification.send(level) + elsif value.send(level) + return 'active' + end + end end diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 76e462a8986..6d9d48994e1 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,7 +1,7 @@ -- if current_user and !@membership_id.nil? +- if current_user and !@membership.nil? = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do = hidden_field_tag :notification_type, 'project' - = hidden_field_tag :notification_id, @membership_id + = hidden_field_tag :notification_id, @membership.id = hidden_field_tag :notification_level %span.dropdown %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} @@ -9,4 +9,4 @@ Notifications %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - Notification.notification_levels.each do |level| - = notification_list_item(level) \ No newline at end of file + = notification_list_item(level, @membership) \ No newline at end of file -- cgit v1.2.1 From fa6df2228e7d8957db4b37a44b687c71aac52449 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 14 Sep 2015 20:50:13 -0500 Subject: Replaced `this` with `@` in the coffee file and added an arrow that clearly marks the Notifications button as a drop down --- app/assets/javascripts/project.js.coffee | 6 +++--- app/views/projects/buttons/_notifications.html.haml | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index f524627e0be..e187ec6ab77 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -27,8 +27,8 @@ class @Project $('.update-notification').on 'click', (e) -> e.preventDefault() - level = $(this).data('notification-level') + level = $(@).data 'notification-level' $('#notification_level').val(level) $('#notification-form').submit() - $(this).parents('ul').find('li.active').removeClass('active') - $(this).parent().addClass('active') \ No newline at end of file + $(@).parents('ul').find('li.active').removeClass 'active' + $(@).parent().addClass 'active' \ No newline at end of file diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 6d9d48994e1..e782aeb3616 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -7,6 +7,7 @@ %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} = icon('bell') Notifications + = icon('angle-down') %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - Notification.notification_levels.each do |level| = notification_list_item(level, @membership) \ No newline at end of file -- 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 d3d03d1362e576d194782a655cdfe9bc6ed5c596 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 30 Aug 2015 21:51:34 -0700 Subject: Create a "destroyed Milestone" event and keep Milestone events around in the DB for posterity. Also fix issue where destroying a Milestone would cause odd, transient messages like "created milestone" or "imported milestone". Add "in" preposition when creating and destroying milestones Closes #2382 --- app/controllers/projects/milestones_controller.rb | 7 +------ app/helpers/events_helper.rb | 11 +++++++++++ app/models/event.rb | 12 ++++++++++-- app/models/milestone.rb | 2 +- app/services/event_create_service.rb | 4 ++++ app/services/milestones/destroy_service.rb | 22 ++++++++++++++++++++++ app/views/events/event/_common.html.haml | 5 +++-- features/project/issues/milestones.feature | 8 ++++++-- features/steps/project/issues/milestones.rb | 7 ++++++- features/steps/shared/project.rb | 5 +++++ .../projects/milestones_controller_spec.rb | 4 ++++ spec/services/event_create_service_spec.rb | 10 ++++++++++ 12 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 app/services/milestones/destroy_service.rb diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 9efe9704d1e..86f4a02a6e9 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -66,12 +66,7 @@ class Projects::MilestonesController < Projects::ApplicationController def destroy return access_denied! unless can?(current_user, :admin_milestone, @project) - update_params = { milestone: nil } - @milestone.issues.each do |issue| - Issues::UpdateService.new(@project, current_user, update_params).execute(issue) - end - - @milestone.destroy + Milestones::DestroyService.new(project, current_user).execute(milestone) respond_to do |format| format.html { redirect_to namespace_project_milestones_path } diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 76602614bcd..6f69c2a9f32 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -46,6 +46,14 @@ module EventsHelper } end + def event_preposition(event) + if event.push? || event.commented? || event.target + "at" + elsif event.milestone? + "in" + end + end + def event_feed_title(event) words = [] words << event.author_name @@ -62,6 +70,9 @@ module EventsHelper words << "##{truncate event.note_target_iid}" end words << "at" + elsif event.milestone? + words << "##{event.target_iid}" if event.target_iid + words << "in" elsif event.target words << "##{event.target_iid}:" words << event.target.title if event.target.respond_to?(:title) diff --git a/app/models/event.rb b/app/models/event.rb index 78f16c6304e..47600c57e35 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -27,6 +27,7 @@ class Event < ActiveRecord::Base MERGED = 7 JOINED = 8 # User joined project LEFT = 9 # User left project + DESTROYED = 10 delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true @@ -48,6 +49,7 @@ class Event < ActiveRecord::Base scope :code_push, -> { where(action: PUSHED) } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } scope :with_associations, -> { includes(project: :namespace) } + scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } class << self def reset_event_cache_for(target) @@ -71,7 +73,7 @@ class Event < ActiveRecord::Base elsif created_project? true else - (issue? || merge_request? || note? || milestone?) && target + ((issue? || merge_request? || note?) && target) || milestone? end end @@ -115,6 +117,10 @@ class Event < ActiveRecord::Base action == LEFT end + def destroyed? + action == DESTROYED + end + def commented? action == COMMENTED end @@ -124,7 +130,7 @@ class Event < ActiveRecord::Base end def created_project? - created? && !target + created? && !target && target_type.nil? end def created_target? @@ -180,6 +186,8 @@ class Event < ActiveRecord::Base 'joined' elsif left? 'left' + elsif destroyed? + 'destroyed' elsif commented? "commented on" elsif created_project? diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c6aff6f709f..d979a35084b 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -61,7 +61,7 @@ class Milestone < ActiveRecord::Base false end end - + def open_items_count self.issues.opened.count + self.merge_requests.opened.count end diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 103d6b0a08b..07fc77001a5 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -46,6 +46,10 @@ class EventCreateService create_record_event(milestone, current_user, Event::REOPENED) end + def destroy_milestone(milestone, current_user) + create_record_event(milestone, current_user, Event::DESTROYED) + end + def leave_note(note, current_user) create_record_event(note, current_user, Event::COMMENTED) end diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb new file mode 100644 index 00000000000..7ce7d259d0b --- /dev/null +++ b/app/services/milestones/destroy_service.rb @@ -0,0 +1,22 @@ +module Milestones + class DestroyService < Milestones::BaseService + def execute(milestone) + + Milestone.transaction do + update_params = { milestone: nil } + milestone.issues.each do |issue| + Issues::UpdateService.new(project, current_user, update_params).execute(issue) + end + + event_service.destroy_milestone(milestone, current_user) + + Event.for_milestone_id(milestone.id).each do |event| + event.target_id = nil + event.save + end + + milestone.destroy + end + end + end +end diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index a39e62e9dac..4ecf1c33d2a 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -5,13 +5,14 @@ - if event.target %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] - at + + = event_preposition(event) - if event.project = link_to_project event.project - else = event.project_name - + - if event.target.respond_to?(:title) .event-body .event-note diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature index bfbaaec5a35..c1a20e9b488 100644 --- a/features/project/issues/milestones.feature +++ b/features/project/issues/milestones.feature @@ -12,13 +12,17 @@ Feature: Project Issues Milestones Given I click link "v2.2" Then I should see milestone "v2.2" - Scenario: I create new milestone + @javascript + Scenario: I create and delete new milestone Given I click link "New Milestone" And I submit new milestone "v2.3" Then I should see milestone "v2.3" + Given I click link to remove milestone + When I visit project "Shop" activity page + Then I should see deleted milestone activity Scenario: I delete new milestone - Given I click link to remove milestone "v2.2" + Given I click link to remove milestone And I should see no milestones @javascript diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index 61e62c2adbd..c8708572ec6 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -49,6 +49,11 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps create(:closed_issue, project: project, milestone: milestone) end + step 'I should see deleted milestone activity' do + expect(page).to have_content('opened milestone in') + expect(page).to have_content('destroyed milestone in') + end + When 'I click link "All Issues"' do click_link 'All Issues' end @@ -57,7 +62,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps expect(page).to have_selector('#tab-issues li.issue-row', count: 4) end - step 'I click link to remove milestone "v2.2"' do + step 'I click link to remove milestone' do click_link 'Remove' end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index ccbe8f96a4c..a9cf426852e 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -51,6 +51,11 @@ module SharedProject visit namespace_project_path(project.namespace, project) end + step 'I visit project "Shop" activity page' do + project = Project.find_by(name: 'Shop') + visit namespace_project_path(project.namespace, project) + end + step 'project "Shop" has push event' do @project = Project.find_by(name: "Shop") diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index d3868c13202..35446640929 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -15,8 +15,12 @@ describe Projects::MilestonesController do describe "#destroy" do it "should remove milestone" do expect(issue.milestone_id).to eq(milestone.id) + delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js expect(response).to be_success + + expect(Event.first.action).to eq(Event::DESTROYED) + expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound) issue.reload expect(issue.milestone_id).to eq(nil) diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index 007a9eed192..7756b973ecd 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -99,5 +99,15 @@ describe EventCreateService do expect { service.close_milestone(milestone, user) }.to change { Event.count } end end + + describe :destroy_mr do + let(:milestone) { create(:milestone) } + + it { expect(service.destroy_milestone(milestone, user)).to be_truthy } + + it "should create new event" do + expect { service.destroy_milestone(milestone, user) }.to change { Event.count } + end + end end 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 52d7813f2eb554d82b6c435a3207fc2e59c76e70 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 15 Sep 2015 16:10:29 +0200 Subject: Improve repo cleanup task Clean up more than just global (legacy) repos. Also, instead of deleting, just rename. --- lib/tasks/gitlab/cleanup.rake | 45 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index 6b1e3716147..f97d4b86e0f 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -46,43 +46,20 @@ namespace :gitlab do desc "GitLab | Cleanup | Clean repositories" task repos: :environment do warn_user_is_not_gitlab - remove_flag = ENV['REMOVE'] - - git_base_path = Gitlab.config.gitlab_shell.repos_path - all_dirs = Dir.glob(git_base_path + '/*') - - global_projects = Project.in_namespace(nil).pluck(:path) - - puts git_base_path.yellow - puts "Looking for global repos to remove... " - - # skip non git repo - all_dirs.select! do |dir| - dir =~ /.git$/ - end - - # skip existing repos - all_dirs.reject! do |dir| - repo_name = File.basename dir - path = repo_name.gsub(/\.git$/, "") - global_projects.include?(path) - end - all_dirs.each do |dir_path| - if remove_flag - if FileUtils.rm_rf dir_path - puts "Removed...#{dir_path}".red - else - puts "Cannot remove #{dir_path}".red - end - else - puts "Can be removed: #{dir_path}".red + move_suffix = "+orphaned+#{Time.now.to_i}" + repo_root = Gitlab.config.gitlab_shell.repos_path + # Look for global repos (legacy, depth 1) and normal repos (depth 2) + IO.popen(%W(find #{repo_root} -mindepth 1 -maxdepth 2 -name *.git)) do |find| + find.each_line do |path| + path.chomp! + repo_with_namespace = path.sub(repo_root + '/', '').chomp('.git').chomp('.wiki') + next if Project.find_with_namespace(repo_with_namespace) + new_path = path + move_suffix + puts path.inspect + ' -> ' + new_path.inspect + File.rename(path, new_path) end end - - unless remove_flag - puts "To cleanup this directories run this command with REMOVE=true".yellow - end end desc "GitLab | Cleanup | Block users that have been removed in LDAP" -- 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 ed38627ec55ce4c3352f44167c4fc75c59841315 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 15 Sep 2015 17:07:51 +0200 Subject: Guard against trailing slashes in repos_path --- lib/tasks/gitlab/cleanup.rake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index f97d4b86e0f..9f5852ac613 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -53,7 +53,11 @@ namespace :gitlab do IO.popen(%W(find #{repo_root} -mindepth 1 -maxdepth 2 -name *.git)) do |find| find.each_line do |path| path.chomp! - repo_with_namespace = path.sub(repo_root + '/', '').chomp('.git').chomp('.wiki') + repo_with_namespace = path. + sub(repo_root, ''). + sub(%r{^/*}, ''). + chomp('.git'). + chomp('.wiki') next if Project.find_with_namespace(repo_with_namespace) new_path = path + move_suffix puts path.inspect + ' -> ' + new_path.inspect -- 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 7bb22831a13b021c7b2d1cc8542808243a659d38 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 16 Sep 2015 09:03:32 +0200 Subject: Update mail_room --- Gemfile | 2 +- Gemfile.lock | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 6d371f27a34..f6b2a0a41da 100644 --- a/Gemfile +++ b/Gemfile @@ -273,6 +273,6 @@ gem "newrelic_rpm" gem 'octokit', '3.7.0' -gem "mail_room", "~> 0.4.2" +gem "mail_room", "~> 0.5.1" gem 'email_reply_parser' diff --git a/Gemfile.lock b/Gemfile.lock index ab743c7d590..6a82d99a14f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -370,7 +370,7 @@ GEM systemu (~> 2.6.2) mail (2.6.3) mime-types (>= 1.16, < 3) - mail_room (0.4.2) + mail_room (0.5.1) method_source (0.8.2) mime-types (1.25.1) mimemagic (0.3.0) @@ -813,7 +813,7 @@ DEPENDENCIES jquery-ui-rails kaminari (~> 0.15.1) letter_opener - mail_room (~> 0.4.2) + mail_room (~> 0.5.1) minitest (~> 5.3.0) mousetrap-rails mysql2 @@ -889,3 +889,6 @@ DEPENDENCIES virtus webmock (~> 1.21.0) wikicloth (= 0.8.1) + +BUNDLED WITH + 1.10.5 -- 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 4b95b0ebec28fb8205e4f672d2df4e3ace7d5785 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 16 Sep 2015 14:43:27 +0200 Subject: Fix Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b033f6fab63..ed5a34a4b6f 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) -- 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 4ae13f5f4245f9882e0bdfeb9ee68c85b2ea979e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 20:59:25 +0200 Subject: Fix displaying public projects --- app/views/ci/projects/_public.html.haml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/views/ci/projects/_public.html.haml b/app/views/ci/projects/_public.html.haml index c2157ab741a..bcbd60b83f0 100644 --- a/app/views/ci/projects/_public.html.haml +++ b/app/views/ci/projects/_public.html.haml @@ -2,11 +2,6 @@ %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 -- 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 From 04a164c2dc3d186bc8e600df5d790eba535b610b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 21:59:10 +0200 Subject: Fix 500 on CI project settings --- 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 79e8fd3a295..876ae5182d4 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_ci_project(@project)} + CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@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 e4ac2d582c729cc22e0da7ab894be04a187eb006 Mon Sep 17 00:00:00 2001 From: Jungkook Park Date: Fri, 11 Sep 2015 12:42:14 +0900 Subject: add repository field to issue hook data add a test for to_hook_data of issue model update CHANGELOG --- CHANGELOG | 1 + app/models/concerns/issuable.rb | 6 ++++++ doc/web_hooks/web_hooks.md | 6 ++++++ spec/models/concerns/issuable_spec.rb | 16 ++++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index ee70789babc..527fa38b223 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ v 8.0.0 (unreleased) - Added service API endpoint to retrieve service parameters (Petheő Bence) - Add FogBugz project import (Jared Szechy) - Sort users autocomplete lists by user (Allister Antosik) + - Webhook for issue now contains repository field (Jungkook Park) v 7.14.3 - No changes diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 40642dc63ba..4db4ffb2e79 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -140,6 +140,12 @@ module Issuable { object_kind: self.class.name.underscore, user: user.hook_attrs, + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url + }, object_attributes: hook_attrs } end diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 09400d9b163..f4701bb6db2 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -121,6 +121,12 @@ X-Gitlab-Event: Issue Hook "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, + "repository": { + "name": "Gitlab Test", + "url": "http://example.com/gitlabhq/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlabhq/gitlab-test" + }, "object_attributes": { "id": 301, "title": "New API: create/update/delete file", diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index b6d80451d2e..8f706f8934b 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Issue, "Issuable" do let(:issue) { create(:issue) } + let(:user) { create(:user) } describe "Associations" do it { is_expected.to belong_to(:project) } @@ -66,4 +67,19 @@ describe Issue, "Issuable" do expect(issue.new?).to be_falsey end end + + + describe "#to_hook_data" do + let(:hook_data) { issue.to_hook_data(user) } + + it "returns correct hook data" do + expect(hook_data[:object_kind]).to eq("issue") + expect(hook_data[:user]).to eq(user.hook_attrs) + expect(hook_data[:repository][:name]).to eq(issue.project.name) + expect(hook_data[:repository][:url]).to eq(issue.project.url_to_repo) + expect(hook_data[:repository][:description]).to eq(issue.project.description) + expect(hook_data[:repository][:homepage]).to eq(issue.project.web_url) + expect(hook_data[:object_attributes]).to eq(issue.hook_attrs) + end + end end -- cgit v1.2.1 From de1ffce7391a9e6adf07a8be60cd36bbc95ef2f3 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 16 Sep 2015 18:03:48 -0500 Subject: Added Spinach tests and updated CHANGELOG --- CHANGELOG | 1 + features/project/project.feature | 6 ++++++ features/steps/project/project.rb | 14 ++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 36ee3e69ab3..4e0e5d44be8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ v 8.0.0 (unreleased) - Add support for Crowd - Global Labels that are available to all projects - Fix highlighting of deleted lines in diffs. + - Project notification level can be set on the project page itself v 7.14.1 - Improve abuse reports management from admin area diff --git a/features/project/project.feature b/features/project/project.feature index 089ffcba14a..b3fb0794547 100644 --- a/features/project/project.feature +++ b/features/project/project.feature @@ -74,3 +74,9 @@ Feature: Project Given I disable snippets in project When I visit project "Shop" page Then I should not see "Snippets" button + + @javascript + Scenario: I edit Project Notifications + Given I click notifications drop down button + When I choose Mention setting + Then I should see Notification saved message diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 0404fd5e594..54c026395fc 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -130,4 +130,18 @@ class Spinach::Features::Project < Spinach::FeatureSteps step 'I should see back to group button' do expect(page).to have_content 'Back to Group' end + + step 'I click notifications drop down button' do + click_link 'Notifications' + end + + step 'I choose Mention setting' do + click_link 'Mention' + end + + step 'I should see Notification saved message' do + page.within '.flash-container' do + expect(page).to have_content 'Notification settings saved' + end + end end -- cgit v1.2.1 From 4c98357f16b1acfa793d8a5b28c7147e21f20356 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 16 Sep 2015 19:46:24 -0500 Subject: Added Global to the drop downs and updated the label of the button to show the current level instead of `Notifications` --- app/assets/javascripts/project.js.coffee | 12 ++++++++++-- app/helpers/notifications_helper.rb | 22 +++++++++++++++------- app/models/notification.rb | 19 ++++++++++++++++++- .../projects/buttons/_notifications.html.haml | 6 +++--- features/steps/project/project.rb | 4 ++-- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index e187ec6ab77..0ea8fffce07 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -27,8 +27,16 @@ class @Project $('.update-notification').on 'click', (e) -> e.preventDefault() - level = $(@).data 'notification-level' - $('#notification_level').val(level) + notification_level = $(@).data 'notification-level' + $('#notification_level').val(notification_level) $('#notification-form').submit() + label = null + switch notification_level + when 0 then label = ' Disabled ' + when 1 then label = ' Participating ' + when 2 then label = ' Watching ' + when 3 then label = ' Global ' + when 4 then label = ' On Mention ' + $('#notifications-button').empty().append("" + label + "") $(@).parents('ul').find('li.active').removeClass 'active' $(@).parent().addClass 'active' \ No newline at end of file diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 48dc198e4e2..4fd06bebc2a 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -24,19 +24,25 @@ module NotificationsHelper when Notification::N_PARTICIPATING content_tag(:li, class: active_level_for(user_membership, 'participating?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do - icon('volume-up fw', text: 'Participating') + icon('volume-up fw', text: 'Participate') end end when Notification::N_WATCH content_tag(:li, class: active_level_for(user_membership, 'watch?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do - icon('globe fw', text: 'Watch') + icon('eye fw', text: 'Watch') end end when Notification::N_MENTION content_tag(:li, class: active_level_for(user_membership, 'mention?')) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do - icon('at fw', text: 'Mention') + icon('at fw', text: 'On mention') + end + end + when Notification::N_GLOBAL + content_tag(:li, class: active_level_for(user_membership, 'global?')) do + link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do + icon('globe fw', text: 'Global') end end else @@ -44,12 +50,14 @@ module NotificationsHelper end end + def notification_label(user_membership) + Notification.new(user_membership).to_s + end + def active_level_for(user_membership, level) value = Notification.new(user_membership) - if value.global? - return 'active' if current_user.notification.send(level) - elsif value.send(level) - return 'active' + if value.send(level) + 'active' end end end diff --git a/app/models/notification.rb b/app/models/notification.rb index 828378655ce..171b8df45c2 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -26,7 +26,7 @@ class Notification end def project_notification_levels - [N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION] + [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL] end end @@ -57,4 +57,21 @@ class Notification def level target.notification_level end + + def to_s + case level + when N_DISABLED + 'Disabled' + when N_PARTICIPATING + 'Participating' + when N_WATCH + 'Watching' + when N_MENTION + 'On mention' + when N_GLOBAL + 'Global' + else + # do nothing + end + end end diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index e782aeb3616..9bb46157229 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -4,10 +4,10 @@ = hidden_field_tag :notification_id, @membership.id = hidden_field_tag :notification_level %span.dropdown - %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} = icon('bell') - Notifications + = notification_label(@membership) = icon('angle-down') %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - - Notification.notification_levels.each do |level| + - Notification.project_notification_levels.each do |level| = notification_list_item(level, @membership) \ No newline at end of file diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 54c026395fc..390a0ba9703 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -132,11 +132,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps end step 'I click notifications drop down button' do - click_link 'Notifications' + click_link 'notifications-button' end step 'I choose Mention setting' do - click_link 'Mention' + click_link 'On mention' end step 'I should see Notification saved message' do -- cgit v1.2.1 From 825db23f6750c6194932d420fa548ccfa12eddd3 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 16 Sep 2015 20:04:12 -0500 Subject: Updated label from `Mention` to `On Mention`. --- app/views/profiles/notifications/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index ea4e5f3e182..8eebd96b674 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -33,7 +33,7 @@ = f.label :notification_level, value: Notification::N_MENTION do = f.radio_button :notification_level, Notification::N_MENTION .level-title - Mention + On Mention %p You will receive notifications only for comments in which you were @mentioned .radio -- cgit v1.2.1 From 23f9a64566ec0da50c9ee842ba749a8f33eacb8e Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Wed, 16 Sep 2015 21:02:40 -0500 Subject: Use the `find_by_user_id` magic finder instead of `where`. --- app/controllers/projects_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 009e856d93c..9aed6a19e54 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -87,7 +87,7 @@ class ProjectsController < ApplicationController render 'projects/empty' else unless current_user.nil? - @membership = @project.project_members.where(user_id: current_user.id).first + @membership = @project.project_members.find_by_user_id(current_user.id) end render :show end -- cgit v1.2.1 From b71a9fcdebe722442e404334954be1908de22a22 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Sep 2015 21:24:53 -0700 Subject: Fix HTML link that was improperly escaped in new user e-mail Closes https://github.com/gitlabhq/gitlabhq/issues/9640 --- CHANGELOG | 1 + app/views/notify/new_user_email.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ee70789babc..082970e0cfd 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 HTML link that was improperly escaped in new user e-mail (Stan Hu) - 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) diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml index 4feacdaacff..6b9b42dcf37 100644 --- a/app/views/notify/new_user_email.html.haml +++ b/app/views/notify/new_user_email.html.haml @@ -13,4 +13,4 @@ %p = link_to "Click here to set your password", edit_password_url(@user, reset_password_token: @token) %p - = reset_token_expire_message + = raw reset_token_expire_message -- cgit v1.2.1 From ce4defaf2f9195df4680823d8c9f0f49bbced21b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 15 Sep 2015 13:19:32 +0000 Subject: enable SSL by default --- app/models/hooks/web_hook.rb | 2 +- db/migrate/20150915001905_enable_ssl_verification_by_default.rb | 5 +++++ .../20150916000405_enable_ssl_verification_for_web_hooks.rb | 8 ++++++++ db/schema.rb | 5 ++--- 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20150915001905_enable_ssl_verification_by_default.rb create mode 100644 db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 9a8251bdad5..a078accbdbd 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -25,7 +25,7 @@ class WebHook < ActiveRecord::Base default_value_for :note_events, false default_value_for :merge_requests_events, false default_value_for :tag_push_events, false - default_value_for :enable_ssl_verification, false + default_value_for :enable_ssl_verification, true # HTTParty timeout default_timeout Gitlab.config.gitlab.webhook_timeout diff --git a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb new file mode 100644 index 00000000000..6e924262a13 --- /dev/null +++ b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb @@ -0,0 +1,5 @@ +class EnableSslVerificationByDefault < ActiveRecord::Migration + def change + change_column :web_hooks, :enable_ssl_verification, :boolean, default: true + end +end diff --git a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb new file mode 100644 index 00000000000..90ce6c2db3d --- /dev/null +++ b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb @@ -0,0 +1,8 @@ +class EnableSslVerificationForWebHooks < ActiveRecord::Migration + def up + execute("UPDATE web_hooks SET enable_ssl_verification = true") + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index 48314b8db6a..a3a69a4b626 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,8 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150916145038) do - +ActiveRecord::Schema.define(version: 20150916000405) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -782,7 +781,7 @@ ActiveRecord::Schema.define(version: 20150916145038) do t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false t.boolean "note_events", default: false, null: false - t.boolean "enable_ssl_verification", default: false + t.boolean "enable_ssl_verification", default: true end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree -- cgit v1.2.1 From eb1444ee1126414f638861b6ba0f4316345fe72a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 10:31:08 +0200 Subject: Fix schema downgrade introduced by: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1299 --- db/schema.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index a3a69a4b626..151f34e1965 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150916000405) do +ActiveRecord::Schema.define(version: 20150916145038) do + # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 8379a1c754d9a54cf63f78502dc690f0a09c0eea Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 17 Sep 2015 12:14:28 +0200 Subject: Add Gmail actions button for GitLab doc. --- doc/integration/gmail_action_buttons_for_gitlab.md | 22 +++++++++++++++++++++ doc/integration/gmail_actions_button.png | Bin 0 -> 17321 bytes 2 files changed, 22 insertions(+) create mode 100644 doc/integration/gmail_action_buttons_for_gitlab.md create mode 100644 doc/integration/gmail_actions_button.png diff --git a/doc/integration/gmail_action_buttons_for_gitlab.md b/doc/integration/gmail_action_buttons_for_gitlab.md new file mode 100644 index 00000000000..21f34db6ed5 --- /dev/null +++ b/doc/integration/gmail_action_buttons_for_gitlab.md @@ -0,0 +1,22 @@ +# Gmail actions buttons for GitLab + +GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview). + +If correctly setup, emails that require an action will be marked in Gmail. + +![gmail_actions_button.png](gmail_actions_button.png.png) + +To get this functioning, you need to be registered with Google. +[See how to register with Google in this document.](https://developers.google.com/gmail/markup/registering-with-google) + +*This process has a lot of steps so make sure that you fulfill all requirements set by Google.* +*Your application will be rejected by Google if you fail to do so.* + +Pay close attention to: + +* Email account used by GitLab to send notification emails needs to have "Consistent history of sending a high volume of mail from your domain (order of hundred emails a day minimum to Gmail) for a few weeks at least". +* "A very very low rate of spam complaints from users." +* Emails must be authenticated via DKIM or SPF +* Before sending the final form("Gmail Schema Whitelist Request"), you must send a real email from your production server. This means that you will have to find a way to send this email from the email address you are registering. You can do this by, for example, forwarding the real email from the email address you are registering or going into the rails console on the GitLab server and triggering the email sending from there. + +You can check how it looks going through all the steps laid out in the "Registering with Google" doc in [this GitLab.com issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/1517). diff --git a/doc/integration/gmail_actions_button.png b/doc/integration/gmail_actions_button.png new file mode 100644 index 00000000000..b08f54d137b Binary files /dev/null and b/doc/integration/gmail_actions_button.png differ -- cgit v1.2.1 From d89ae7df2cf02198570dddd80dfef60796b017b5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Sep 2015 12:16:24 +0200 Subject: Move project header title definition to view in question. --- app/helpers/groups_helper.rb | 10 +++---- app/helpers/projects_helper.rb | 35 ---------------------- .../groups/milestones/_header_title.html.haml | 1 + app/views/groups/milestones/show.html.haml | 2 ++ app/views/layouts/dashboard.html.haml | 3 +- app/views/layouts/group.html.haml | 5 ++-- app/views/layouts/group_settings.html.haml | 1 + app/views/layouts/profile.html.haml | 3 +- app/views/layouts/project.html.haml | 4 +-- app/views/layouts/project_settings.html.haml | 1 + app/views/projects/activity.html.haml | 2 ++ app/views/projects/blame/show.html.haml | 2 ++ app/views/projects/blob/_header_title.html.haml | 1 + app/views/projects/blob/edit.html.haml | 2 ++ app/views/projects/blob/new.html.haml | 3 ++ app/views/projects/blob/show.html.haml | 1 + app/views/projects/branches/index.html.haml | 1 + app/views/projects/branches/new.html.haml | 2 ++ app/views/projects/commit/show.html.haml | 1 + app/views/projects/commits/_header_title.html.haml | 1 + app/views/projects/commits/show.html.haml | 1 + app/views/projects/compare/index.html.haml | 1 + app/views/projects/compare/show.html.haml | 1 + app/views/projects/graphs/_header_title.html.haml | 1 + app/views/projects/graphs/commits.html.haml | 3 +- app/views/projects/graphs/show.html.haml | 3 +- app/views/projects/issues/_header_title.html.haml | 1 + app/views/projects/issues/index.html.haml | 2 ++ app/views/projects/issues/new.html.haml | 2 ++ app/views/projects/issues/show.html.haml | 2 ++ app/views/projects/labels/_header_title.html.haml | 1 + app/views/projects/labels/edit.html.haml | 2 ++ app/views/projects/labels/index.html.haml | 1 + app/views/projects/labels/new.html.haml | 2 ++ .../merge_requests/_header_title.html.haml | 1 + app/views/projects/merge_requests/_show.html.haml | 2 ++ app/views/projects/merge_requests/edit.html.haml | 2 ++ app/views/projects/merge_requests/index.html.haml | 2 ++ .../projects/merge_requests/invalid.html.haml | 2 ++ app/views/projects/merge_requests/new.html.haml | 2 ++ .../projects/milestones/_header_title.html.haml | 1 + app/views/projects/milestones/edit.html.haml | 1 + app/views/projects/milestones/index.html.haml | 1 + app/views/projects/milestones/new.html.haml | 1 + app/views/projects/milestones/show.html.haml | 2 ++ app/views/projects/network/show.html.haml | 1 + .../project_members/_header_title.html.haml | 1 + .../projects/project_members/import.html.haml | 2 ++ app/views/projects/project_members/index.html.haml | 1 + .../projects/snippets/_header_title.html.haml | 1 + app/views/projects/snippets/edit.html.haml | 2 ++ app/views/projects/snippets/index.html.haml | 2 ++ app/views/projects/snippets/new.html.haml | 2 ++ app/views/projects/snippets/show.html.haml | 2 ++ app/views/projects/tags/index.html.haml | 1 + app/views/projects/tags/new.html.haml | 2 ++ app/views/projects/tree/show.html.haml | 1 + app/views/projects/wikis/_header_title.html.haml | 1 + app/views/projects/wikis/edit.html.haml | 2 ++ app/views/projects/wikis/empty.html.haml | 2 ++ app/views/projects/wikis/git_access.html.haml | 2 ++ app/views/projects/wikis/history.html.haml | 4 ++- app/views/projects/wikis/pages.html.haml | 2 ++ app/views/projects/wikis/show.html.haml | 4 ++- 64 files changed, 103 insertions(+), 53 deletions(-) create mode 100644 app/views/groups/milestones/_header_title.html.haml create mode 100644 app/views/projects/blob/_header_title.html.haml create mode 100644 app/views/projects/commits/_header_title.html.haml create mode 100644 app/views/projects/graphs/_header_title.html.haml create mode 100644 app/views/projects/issues/_header_title.html.haml create mode 100644 app/views/projects/labels/_header_title.html.haml create mode 100644 app/views/projects/merge_requests/_header_title.html.haml create mode 100644 app/views/projects/milestones/_header_title.html.haml create mode 100644 app/views/projects/project_members/_header_title.html.haml create mode 100644 app/views/projects/snippets/_header_title.html.haml create mode 100644 app/views/projects/wikis/_header_title.html.haml diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 5e70de23f29..1d36969cd62 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -31,12 +31,12 @@ module GroupsHelper end end - def group_title(group, name, url) + def group_title(group, name = nil, url = nil) + full_title = link_to(simple_sanitize(group.name), group_path(group)) + full_title += ' · '.html_safe + link_to(simple_sanitize(name), url) if name + content_tag :span do - link_to( - simple_sanitize(group.name), group_path(group) - ) + ' · '.html_safe + - link_to(simple_sanitize(name), url) + full_title end end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 6a2de0de77c..a2b83c50c2e 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -317,41 +317,6 @@ module ProjectsHelper @ref || @repository.try(:root_ref) end - def detect_project_title(project) - name, url = - if current_controller? 'wikis' - ['Wiki', get_project_wiki_path(project)] - elsif current_controller? 'project_members' - ['Members', namespace_project_project_members_path(project.namespace, project)] - elsif current_controller? 'labels' - ['Labels', namespace_project_labels_path(project.namespace, project)] - elsif current_controller? 'members' - ['Members', project_files_path(project)] - elsif current_controller? 'commits' - ['Commits', project_commits_path(project)] - elsif current_controller? 'graphs' - ['Graphs', namespace_project_graph_path(project.namespace, project, current_ref)] - elsif current_controller? 'network' - ['Network', namespace_project_network_path(project.namespace, project, current_ref)] - elsif current_controller? 'milestones' - ['Milestones', namespace_project_milestones_path(project.namespace, project)] - elsif current_controller? 'snippets' - ['Snippets', namespace_project_snippets_path(project.namespace, project)] - elsif current_controller? 'issues' - ['Issues', namespace_project_issues_path(project.namespace, project)] - elsif current_controller? 'merge_requests' - ['Merge Requests', namespace_project_merge_requests_path(project.namespace, project)] - elsif current_controller? 'tree', 'blob' - ['Files', project_files_path(project)] - elsif current_path? 'projects#activity' - ['Activity', activity_project_path(project)] - else - [nil, nil] - end - - project_title(project, name, url) - end - private def filename_path(project, filename) diff --git a/app/views/groups/milestones/_header_title.html.haml b/app/views/groups/milestones/_header_title.html.haml new file mode 100644 index 00000000000..d7fabf53587 --- /dev/null +++ b/app/views/groups/milestones/_header_title.html.haml @@ -0,0 +1 @@ +- header_title group_title(@group, "Milestones", group_milestones_path(@group)) diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 8f2decb851f..0c213f42186 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,4 +1,6 @@ - page_title @group_milestone.title, "Milestones" += render "header_title" + %h4.page-title .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" } - if @group_milestone.closed? diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml index fad7de69432..cb96bcc2cf4 100644 --- a/app/views/layouts/dashboard.html.haml +++ b/app/views/layouts/dashboard.html.haml @@ -1,6 +1,5 @@ - page_title "Dashboard" -- unless @header_title - - header_title "Dashboard", root_path +- header_title "Dashboard", root_path unless header_title - sidebar "dashboard" = render template: "layouts/application" diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 4f00d01d4cd..31888c5580e 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,6 +1,5 @@ - page_title @group.name -- unless @header_title - - header_title @group.name, group_path(@group) -- sidebar "group" unless sidebar +- header_title group_title(@group) unless header_title +- sidebar "group" unless sidebar = render template: "layouts/application" diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index e303a561628..a1a1fc2f858 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,4 +1,5 @@ - page_title "Settings" +- header_title group_title(@group, "Settings", edit_group_path(@group)) - sidebar "group_settings" = render template: "layouts/group" diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index b80ce0dfc75..dfa6cc5702e 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,6 +1,5 @@ - page_title "Profile Settings" -- unless @header_title - - header_title "Profile Settings", profile_path +- header_title "Profile Settings", profile_path unless header_title - sidebar "profile" = render template: "layouts/application" diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 5c4dd67f0ec..78dafcd8bfa 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -1,6 +1,6 @@ - page_title @project.name_with_namespace -- header_title detect_project_title(@project) -- sidebar "project" unless sidebar +- header_title project_title(@project) unless header_title +- sidebar "project" unless sidebar - content_for :scripts_body_top do - if current_user diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml index 43401668334..59ce38f67bb 100644 --- a/app/views/layouts/project_settings.html.haml +++ b/app/views/layouts/project_settings.html.haml @@ -1,4 +1,5 @@ - page_title "Settings" +- header_title project_title(@project, "Settings", edit_project_path(@project)) - sidebar "project_settings" = render template: "layouts/project" diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index 5f6e5f3b644..555ed76426d 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1,2 +1,4 @@ - page_title "Activity" +- header_title project_title(@project, "Activity", activity_project_path(@project)) + = render 'projects/activity' diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index c1ec42aefca..6518c4173e1 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -1,4 +1,6 @@ - page_title "Blame", @blob.path, @ref +- header_title project_title(@project, "Files", project_files_path(@project)) + %h3.page-title Blame view #tree-holder.tree-holder diff --git a/app/views/projects/blob/_header_title.html.haml b/app/views/projects/blob/_header_title.html.haml new file mode 100644 index 00000000000..78c5ef20a5f --- /dev/null +++ b/app/views/projects/blob/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Files", project_files_path(@project)) diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 648f15418e0..a811adc5094 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -1,4 +1,6 @@ - page_title "Edit", @blob.path, @ref += render "header_title" + .file-editor %ul.center-top-menu.no-bottom.js-edit-mode %li.active diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 68c9ec7f802..126e21d7b4d 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,3 +1,6 @@ +- page_title "New File", @path.presence, @ref += render "header_title" + .gray-content-block.top-block Create a new file or = link_to 'upload', '#modal-upload-blob', diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 4e66a43bbd5..1f7859198d8 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -1,4 +1,5 @@ - page_title @blob.path, @ref += render "header_title" = render 'projects/last_push' diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 6e2dc2d2710..03ade02a0c8 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,4 +1,5 @@ - page_title "Branches" += render "projects/commits/header_title" = render "projects/commits/head" .gray-content-block .pull-right diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 29e82b93883..f5577042ca4 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -1,4 +1,6 @@ - page_title "New Branch" += render "projects/commits/header_title" + - if @error .alert.alert-danger %button{ type: "button", class: "close", "data-dismiss" => "alert"} × diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 60b112e67d4..f8681024d1b 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,4 +1,5 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" += render "projects/commits/header_title" = render "commit_box" = render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/notes/notes_with_form", view: params[:view] diff --git a/app/views/projects/commits/_header_title.html.haml b/app/views/projects/commits/_header_title.html.haml new file mode 100644 index 00000000000..e4385893dd9 --- /dev/null +++ b/app/views/projects/commits/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Commits", project_commits_path(@project)) diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index a01a99458a0..2dd99cc8215 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -1,4 +1,5 @@ - page_title "Commits", @ref += render "header_title" = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 43d00726c48..02be5a2d07f 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,4 +1,5 @@ - page_title "Compare" += render "projects/commits/header_title" = render "projects/commits/head" .gray-content-block diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 8800ffdf482..39755efd2fd 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,4 +1,5 @@ - page_title "#{params[:from]}...#{params[:to]}" += render "projects/commits/header_title" = render "projects/commits/head" diff --git a/app/views/projects/graphs/_header_title.html.haml b/app/views/projects/graphs/_header_title.html.haml new file mode 100644 index 00000000000..1e2f61cd22b --- /dev/null +++ b/app/views/projects/graphs/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Graphs", namespace_project_graph_path(@project.namespace, @project, current_ref)) diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index a357736bf52..bf0cac539b8 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,4 +1,5 @@ -- page_title "Commit statistics" +- page_title "Commits", "Graphs" += render "header_title" .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs_commits' = render 'head' diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index ecdd0eaf52f..bd342911e49 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,4 +1,5 @@ -- page_title "Contributor statistics" +- page_title "Contributors", "Graphs" += render "header_title" .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs' = render 'head' diff --git a/app/views/projects/issues/_header_title.html.haml b/app/views/projects/issues/_header_title.html.haml new file mode 100644 index 00000000000..99f03549c44 --- /dev/null +++ b/app/views/projects/issues/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project)) diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 24314d11404..d6260ab2900 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,4 +1,6 @@ - page_title "Issues" += render "header_title" + = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index da6edd5c2d2..153447baa1b 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,2 +1,4 @@ - page_title "New Issue" += render "header_title" + = render "form" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 09080642293..5cb814c9ea8 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,4 +1,6 @@ - page_title "#{@issue.title} (##{@issue.iid})", "Issues" += render "header_title" + .issue .issue-details.issuable-details .page-title diff --git a/app/views/projects/labels/_header_title.html.haml b/app/views/projects/labels/_header_title.html.haml new file mode 100644 index 00000000000..abe28da483b --- /dev/null +++ b/app/views/projects/labels/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Labels", namespace_project_labels_path(@project.namespace, @project)) diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 645402667fd..bc4ab0ca27c 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,4 +1,6 @@ - page_title "Edit", @label.name, "Labels" += render "header_title" + %h3 Edit label %span.light #{@label.name} diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 284adb40e97..97175f8232b 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,4 +1,5 @@ - page_title "Labels" += render "header_title" .gray-content-block.top-block - if can? current_user, :admin_label, @project diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index b3ef17025c3..342ad4f3f95 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,4 +1,6 @@ - page_title "New Label" += render "header_title" + %h3 New label .back-link = link_to namespace_project_labels_path(@project.namespace, @project) do diff --git a/app/views/projects/merge_requests/_header_title.html.haml b/app/views/projects/merge_requests/_header_title.html.haml new file mode 100644 index 00000000000..669a9b06bdf --- /dev/null +++ b/app/views/projects/merge_requests/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project)) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 61e04dce5ab..0b0f52c653c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,4 +1,6 @@ - page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" += render "header_title" + - if params[:view] == 'parallel' - fluid_layout true diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index 7e5cb07f249..303ca0a880b 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -1,4 +1,6 @@ - page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" += render "header_title" + %h3.page-title = "Edit merge request ##{@merge_request.iid}" %hr diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index d3a576977c2..086298e5af1 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,4 +1,6 @@ - page_title "Merge Requests" += render "header_title" + = render 'projects/last_push' .project-issuable-filter .controls diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml index 15bd4e2fafd..fc03ee73a3d 100644 --- a/app/views/projects/merge_requests/invalid.html.haml +++ b/app/views/projects/merge_requests/invalid.html.haml @@ -1,4 +1,6 @@ - page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" += render "header_title" + .merge-request = render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_box" diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml index b038a640f67..9fdde80c6d9 100644 --- a/app/views/projects/merge_requests/new.html.haml +++ b/app/views/projects/merge_requests/new.html.haml @@ -1,4 +1,6 @@ - page_title "New Merge Request" += render "header_title" + - if @merge_request.can_be_created = render 'new_submit' - else diff --git a/app/views/projects/milestones/_header_title.html.haml b/app/views/projects/milestones/_header_title.html.haml new file mode 100644 index 00000000000..5f4b6982a6d --- /dev/null +++ b/app/views/projects/milestones/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Milestones", namespace_project_milestones_path(@project.namespace, @project)) diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index c09815a212a..e9dc0b77462 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -1,2 +1,3 @@ - page_title "Edit", @milestone.title, "Milestones" += render "header_title" = render "form" diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index 2b8fd671587..a207385bd43 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,4 +1,5 @@ - page_title "Milestones" += render "header_title" = render 'shared/milestones_filter' .gray-content-block diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 47149dfea41..9ba9acb6f77 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,2 +1,3 @@ - page_title "New Milestone" += render "header_title" = render "form" diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 7b1681df336..4eeb0621e52 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,4 +1,6 @@ - page_title @milestone.title, "Milestones" += render "header_title" + %h4.page-title .issue-box{ class: issue_box_class(@milestone) } - if @milestone.closed? diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 52b5b8b877e..16005161df6 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,4 +1,5 @@ - page_title "Network", @ref += header_title project_title(@project, "Network", namespace_project_network_path(@project.namespace, @project, current_ref)) = render "head" .project-network .controls diff --git a/app/views/projects/project_members/_header_title.html.haml b/app/views/projects/project_members/_header_title.html.haml new file mode 100644 index 00000000000..a31f0a37fa2 --- /dev/null +++ b/app/views/projects/project_members/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Members", namespace_project_project_members_path(@project.namespace, @project)) diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml index 6914543f6da..189906498cb 100644 --- a/app/views/projects/project_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -1,4 +1,6 @@ - page_title "Import members" += render "header_title" + %h3.page-title Import members from another project %p.light diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index a40d1513671..9a0a824b811 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,4 +1,5 @@ - page_title "Members" += render "header_title" .gray-content-block.top-block .clearfix.js-toggle-container diff --git a/app/views/projects/snippets/_header_title.html.haml b/app/views/projects/snippets/_header_title.html.haml new file mode 100644 index 00000000000..04f0bbe9853 --- /dev/null +++ b/app/views/projects/snippets/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Snippets", namespace_project_snippets_path(@project.namespace, @project)) diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index 945f0084dff..e69f2d99709 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -1,4 +1,6 @@ - page_title "Edit", @snippet.title, "Snippets" += render "header_title" + %h3.page-title Edit snippet %hr diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 45d4de6a385..3fed2c9949d 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,4 +1,6 @@ - page_title "Snippets" += render "header_title" + %h3.page-title Snippets - if can? current_user, :create_project_snippet, @project diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index e38d95c45e7..67cd69fd215 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -1,4 +1,6 @@ - page_title "New Snippets" += render "header_title" + %h3.page-title New snippet %hr diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 8cbb813c758..be7d4d486fa 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,4 +1,6 @@ - page_title @snippet.title, "Snippets" += render "header_title" + %h3.page-title = @snippet.title diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 1503a4330f4..85d76eae3b5 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -1,4 +1,5 @@ - page_title "Tags" += render "projects/commits/header_title" = render "projects/commits/head" .gray-content-block diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 172fafdeeff..9f5c1be125c 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -1,4 +1,6 @@ - page_title "New Tag" += render "projects/commits/header_title" + - if @error .alert.alert-danger %button{ type: "button", class: "close", "data-dismiss" => "alert"} × diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index c9e59428e78..dec4677f830 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,4 +1,5 @@ - page_title @path.presence || "Files", @ref +- header_title project_title(@project, "Files", project_files_path(@project)) = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") diff --git a/app/views/projects/wikis/_header_title.html.haml b/app/views/projects/wikis/_header_title.html.haml new file mode 100644 index 00000000000..408adc36ca6 --- /dev/null +++ b/app/views/projects/wikis/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, 'Wiki', get_project_wiki_path(@project)) diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 3f1dce1050c..0b709c3695b 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,4 +1,6 @@ - page_title "Edit", @page.title, "Wiki" += render "header_title" + = render 'nav' .pull-right = render 'main_links' diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml index ead99412406..c7e490c3cd1 100644 --- a/app/views/projects/wikis/empty.html.haml +++ b/app/views/projects/wikis/empty.html.haml @@ -1,4 +1,6 @@ - page_title "Wiki" += render "header_title" + %h3.page-title Empty page %hr .error_message diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index 226fd3b2290..6417ef4a38b 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -1,4 +1,6 @@ - page_title "Git Access", "Wiki" += render "header_title" + = render 'nav' .gray-content-block .row diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index 7c81ad53d32..bfbef823b35 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -1,4 +1,6 @@ -- page_title "History", @page.title, "Wiki" +- page_title "History", @page.title.capitalize, "Wiki" += render "header_title" + = render 'nav' .gray-content-block %h3.page-title diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 7fb91507eb2..03e6a522b25 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,4 +1,6 @@ - page_title "All Pages", "Wiki" += render "header_title" + = render 'nav' .gray-content-block %h3.page-title diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 126810811ec..55fbf5a8b6e 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,4 +1,6 @@ -- page_title @page.title, "Wiki" +- page_title @page.title.capitalize, "Wiki" += render "header_title" + = render 'nav' .gray-content-block -- cgit v1.2.1 From 6043c6696640404f989b598188a6b78f7e574723 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Sep 2015 12:16:35 +0200 Subject: Rename "CI" in sidebar to "GitLab CI". --- app/views/layouts/nav/_dashboard.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 56283cba6bd..5381002ecf5 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -50,4 +50,4 @@ = link_to ci_root_path, title: 'Continuous Integration', data: {placement: 'right'} do = icon('building fw') %span - CI + GitLab CI -- cgit v1.2.1 From 89fee0e35e56ca0bc47877b567c4a6d39d060fd5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Sep 2015 12:16:49 +0200 Subject: Fix active state of CI Help nav link. --- app/views/layouts/ci/_nav_dashboard.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/ci/_nav_dashboard.html.haml b/app/views/layouts/ci/_nav_dashboard.html.haml index fcff405d19d..55bacb66061 100644 --- a/app/views/layouts/ci/_nav_dashboard.html.haml +++ b/app/views/layouts/ci/_nav_dashboard.html.haml @@ -16,7 +16,7 @@ %i.fa.fa-cogs %span Admin - %li + = nav_link path: "helps#show" do = link_to ci_help_path do %i.fa.fa-info %span -- cgit v1.2.1 From 7b10bd954f2fd593d66f3920988a312375d34eea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Sep 2015 12:17:13 +0200 Subject: "Back to x" links are lowercase. --- app/views/layouts/ci/_nav_admin.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index c987ab876a3..4e4c8ab1079 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -3,7 +3,7 @@ = 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 + Back to dashboard %li.separate-item = nav_link path: 'projects#index' do -- cgit v1.2.1 From 1ab80247bcccdab36447a56a3455fe6d828e5c07 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 17 Sep 2015 12:23:35 +0200 Subject: Remove CI cronjob during migration --- doc/migrate_ci_to_ce/README.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 13efc8442d2..1e45f29dbb2 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -52,7 +52,14 @@ This also breaks your database structure disallowing you to use it anymore. ALTER TABLE web_hooks RENAME TO ci_web_hooks; EOF -### 4. Dump GitLab CI database [CI] +### 4. Remove CI cronjob + +``` +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec whenever --clear-crontab +``` + +### 5. Dump GitLab CI database [CI] First check used database and credentials on GitLab CI and GitLab CE/EE: @@ -125,18 +132,18 @@ You will need to put these credentials into commands executed below.** # 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] +### 6. 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 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] +### 7. Stop GitLab CE/EE [CE] Before you can migrate data you need to stop GitLab CE/EE first. sudo service gitlab stop -### 7. Backup GitLab CE/EE [CE] +### 8. Backup GitLab CE/EE [CE] This migration poses a **significant risk** of breaking your GitLab CE/EE. **You should create the GitLab CI/EE backup before doing it.** @@ -144,7 +151,7 @@ This migration poses a **significant risk** of breaking your GitLab CE/EE. cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production -### 8. Copy secret tokens [CE] +### 9. Copy secret tokens [CE] The `secrets.yml` file stores encryption keys for secure variables. @@ -154,7 +161,7 @@ You need to copy the content of `config/secrets.yml` to the same file in GitLab 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] +### 10. 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`: @@ -165,7 +172,7 @@ git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/g The new options include configuration of GitLab CI that are now being part of GitLab CE and EE. -### 10. Copy build logs [CE] +### 11. Copy build logs [CE] You need to copy the contents of `builds/` to the same directory in GitLab CE/EE. @@ -174,7 +181,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. -### 11. Import GitLab CI database [CE] +### 12. Import GitLab CI database [CE] The one of the last steps is to import existing GitLab CI database. @@ -189,13 +196,13 @@ The task does: 1. Fix tags assigned to Builds and Runners 1. Fix services used by CI -### 12. Start GitLab [CE] +### 13. Start GitLab [CE] You can start GitLab CI/EE now and see if everything is working. sudo service gitlab start -### 13. Update nginx [CI] +### 14. 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 a migrated GitLab CI. @@ -263,7 +270,7 @@ You should also make sure that you can do: sudo /etc/init.d/nginx restart -### 14. Done! +### 15. 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 1247076b459ad2ccb529d7501063ed23901d5e15 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 17 Sep 2015 13:19:38 +0200 Subject: Remove double png. --- doc/integration/gmail_action_buttons_for_gitlab.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/integration/gmail_action_buttons_for_gitlab.md b/doc/integration/gmail_action_buttons_for_gitlab.md index 21f34db6ed5..de45f25ad62 100644 --- a/doc/integration/gmail_action_buttons_for_gitlab.md +++ b/doc/integration/gmail_action_buttons_for_gitlab.md @@ -4,7 +4,7 @@ GitLab supports [Google actions in email](https://developers.google.com/gmail/ma If correctly setup, emails that require an action will be marked in Gmail. -![gmail_actions_button.png](gmail_actions_button.png.png) +![gmail_actions_button.png](gmail_actions_button.png) To get this functioning, you need to be registered with Google. [See how to register with Google in this document.](https://developers.google.com/gmail/markup/registering-with-google) -- cgit v1.2.1 From da6d9607d1b997de19af94f9ff1bf58610f7f4ff Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Sep 2015 23:55:09 +0200 Subject: Integrate CI navigation more tightly with project --- app/views/layouts/ci/_nav_admin.html.haml | 4 ++-- app/views/layouts/ci/_nav_dashboard.html.haml | 24 ------------------------ app/views/layouts/ci/application.html.haml | 2 +- app/views/layouts/nav/_admin.html.haml | 7 ++++++- app/views/layouts/nav/_dashboard.html.haml | 12 ++++++------ app/views/layouts/nav/_project.html.haml | 7 +++++++ 6 files changed, 22 insertions(+), 34 deletions(-) delete mode 100644 app/views/layouts/ci/_nav_dashboard.html.haml diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index c987ab876a3..d0ca4c421ef 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-sidebar = nav_link do - = link_to ci_root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to admin_root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to Dashboard + Back to Admin %li.separate-item = nav_link path: 'projects#index' do diff --git a/app/views/layouts/ci/_nav_dashboard.html.haml b/app/views/layouts/ci/_nav_dashboard.html.haml deleted file mode 100644 index fcff405d19d..00000000000 --- a/app/views/layouts/ci/_nav_dashboard.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -%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#index' 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/application.html.haml b/app/views/layouts/ci/application.html.haml index b9f871d5447..9cc7fb85142 100644 --- a/app/views/layouts/ci/application.html.haml +++ b/app/views/layouts/ci/application.html.haml @@ -8,4 +8,4 @@ - else = render "layouts/header/public", title: header_title - = render 'layouts/ci/page', sidebar: 'nav_dashboard' + = render 'layouts/ci/page' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 3fe0127041e..2079feeeab6 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -4,7 +4,7 @@ = icon('dashboard fw') %span Overview - = nav_link(controller: :projects) do + = nav_link(controller: [:admin, :projects]) do = link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do = icon('cube fw') %span @@ -24,6 +24,11 @@ = icon('key fw') %span Deploy Keys + = nav_link do + = link_to ci_admin_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do + = icon('building fw') + %span + Continuous Integration = nav_link(controller: :logs) do = link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do = icon('file-text fw') diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 56283cba6bd..acb7b62f2a8 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,5 +1,5 @@ %ul.nav.nav-sidebar - = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do + = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = link_to root_path, title: 'Projects', data: {placement: 'right'} do = icon('home fw') %span @@ -31,6 +31,11 @@ %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count + = nav_link(controller: [:ci, :projects]) do + = link_to ci_root_path, title: 'Continuous Integration', data: {placement: 'right'} do + = icon('building fw') + %span + Continuous Integration = nav_link(controller: :snippets) do = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do = icon('clipboard fw') @@ -46,8 +51,3 @@ = 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/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 8ce46d4865b..a218ec7486c 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -76,6 +76,13 @@ Merge Requests %span.count.merge_counter= @project.merge_requests.opened.count + - if @project.gitlab_ci? + = nav_link(controller: [:ci, :project]) do + = link_to ci_project_path(@project.gitlab_ci_project), title: 'Continuous Integration', data: {placement: 'right'} do + = icon('building fw') + %span + Continuous Integration + - if project_nav_tab? :settings = nav_link(controller: [:project_members, :teams]) do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do -- cgit v1.2.1 From e7bca1c8535f0bdcde6293a560d87a452265d2c3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 14:22:43 +0200 Subject: Check current_controller against controller_path The controller_path includes namespaces, where controller_name only includes the class name without controller --- app/helpers/application_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b049bd9fcc2..39ab83ccf12 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -13,7 +13,9 @@ module ApplicationHelper # current_controller?(:commits) # => false # current_controller?(:commits, :tree) # => true def current_controller?(*args) - args.any? { |v| v.to_s.downcase == controller.controller_name } + args.any? do |v| + v.to_s.downcase == controller.controller_name || v.to_s.downcase == controller.controller_path + end end # Check if a particular action is the current one -- cgit v1.2.1 From d059d346222c2a32c1f761acb823fb0240da99fa Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 14:22:56 +0200 Subject: Remove duplicate code --- app/helpers/ci/application_helper.rb | 108 ----------------------------------- 1 file changed, 108 deletions(-) diff --git a/app/helpers/ci/application_helper.rb b/app/helpers/ci/application_helper.rb index 3198fe55f91..7e880b00b3a 100644 --- a/app/helpers/ci/application_helper.rb +++ b/app/helpers/ci/application_helper.rb @@ -4,118 +4,10 @@ module Ci 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 -- cgit v1.2.1 From e960b3ad2c44c49560cceeee98fe0ec9542d74a7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 14:23:46 +0200 Subject: Fix nav_link for dashboard --- app/views/layouts/nav/_dashboard.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index acb7b62f2a8..f172e9b4ae8 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -31,8 +31,8 @@ %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count - = nav_link(controller: [:ci, :projects]) do - = link_to ci_root_path, title: 'Continuous Integration', data: {placement: 'right'} do + = nav_link(path: 'ci/projects#index') do + = link_to ci_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do = icon('building fw') %span Continuous Integration -- cgit v1.2.1 From ac855e5ed39bf02b7314c97550d026a5c019fba6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Sep 2015 23:34:30 -0700 Subject: Move partial locals out of controller --- app/controllers/projects/blob_controller.rb | 11 ----------- app/views/projects/blob/_upload.html.haml | 10 +++++----- app/views/projects/blob/new.html.haml | 2 +- app/views/projects/blob/show.html.haml | 6 +++++- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index d7be212c33a..8776721d243 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -18,12 +18,6 @@ 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 @@ -46,11 +40,6 @@ 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/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 2cfb79486dc..1a1df127703 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -3,12 +3,12 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title #{@title} + %h3.page-title #{title} %p.light From branch %strong= @ref .modal-body - = form_tag @form_path, method: @method, 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 @@ -17,12 +17,12 @@ %br .dropzone-alerts{class: "alert alert-danger data", style: "display:none"} = render 'shared/commit_message_container', params: params, - placeholder: @placeholder + placeholder: placeholder .form-group .col-sm-offset-2.col-sm-10 - = button_tag @button_title, 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'), '#{@method}') + new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}') diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 68c9ec7f802..071c3bb7da8 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -4,7 +4,7 @@ { class: 'upload-link', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} an existing one -= render 'projects/blob/upload' += render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post .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 diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 4e66a43bbd5..a536d1cf272 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -10,4 +10,8 @@ - if allowed_tree_edit? = render 'projects/blob/remove' - = render 'projects/blob/upload' + + - title = "Replace #{@blob.name}" + = render 'projects/blob/upload', title: title, placeholder: title, + button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), + method: :put -- cgit v1.2.1 From 4e33b47a50b6fa2585ece0a3b199556623978e24 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 17 Sep 2015 17:08:10 +0200 Subject: Update the link in integration readme. --- doc/integration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/integration/README.md b/doc/integration/README.md index 6d856951d4e..eff39a626ae 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -10,7 +10,7 @@ See the documentation below for details on how to configure these services. - [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider - [Slack](slack.md) Integrate with the Slack chat service - [OAuth2 provider](oauth_provider.md) OAuth2 application creation -- [Gmail](gitlab_buttons_in_gmail.md) Adds GitLab actions to messages +- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html). -- cgit v1.2.1 From 7166f7b7cf18e1760e9d4ccfd9ed9622c0869b30 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 10:03:46 +0200 Subject: Remove CI help --- app/controllers/ci/helps_controller.rb | 16 -------------- app/views/ci/helps/oauth2.html.haml | 20 ----------------- app/views/ci/helps/show.html.haml | 40 ---------------------------------- config/routes.rb | 4 ---- 4 files changed, 80 deletions(-) delete mode 100644 app/controllers/ci/helps_controller.rb delete mode 100644 app/views/ci/helps/oauth2.html.haml delete mode 100644 app/views/ci/helps/show.html.haml diff --git a/app/controllers/ci/helps_controller.rb b/app/controllers/ci/helps_controller.rb deleted file mode 100644 index a1ee4111614..00000000000 --- a/app/controllers/ci/helps_controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -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/views/ci/helps/oauth2.html.haml b/app/views/ci/helps/oauth2.html.haml deleted file mode 100644 index 2031b7340d4..00000000000 --- a/app/views/ci/helps/oauth2.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -.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 deleted file mode 100644 index 9b32d529c60..00000000000 --- a/app/views/ci/helps/show.html.haml +++ /dev/null @@ -1,40 +0,0 @@ -.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.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.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.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.fa-thumbs-up - Feedback forum - %p Suggest improvements or new features for GitLab CI. diff --git a/config/routes.rb b/config/routes.rb index 41970d2af8a..b5a84c1f192 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,10 +9,6 @@ Gitlab::Application.routes.draw do resource :lint, only: [:show, :create] - resource :help do - get :oauth2 - end - resources :projects do collection do post :add -- cgit v1.2.1 From ca95842f9483b85a8c0e7735961a56136c783b1b Mon Sep 17 00:00:00 2001 From: karen Carias Date: Thu, 17 Sep 2015 09:40:46 -0700 Subject: fixed text --- doc/ssh/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index cb4c46edbf0..7b294a70fe7 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -72,8 +72,7 @@ access can happen through being a direct member of the project, or through a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more information. -Deploy keys are added by project. It is not possible yet to have default -or global deploy keys. +Deploy keys can be shared between projects, you just need to add them to each project. ## Applications -- cgit v1.2.1 From b7e49d8042629dd0e5b9feade78e058a737f96b2 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 17 Sep 2015 13:41:06 +0300 Subject: Fix: ci projects order --- app/controllers/ci/projects_controller.rb | 24 ++++------ app/views/ci/projects/_gl_projects.html.haml | 15 ------- app/views/ci/projects/_project.html.haml | 58 +++++++++++++++---------- app/views/ci/projects/_search.html.haml | 8 +--- app/views/ci/projects/gitlab.html.haml | 27 ------------ app/views/ci/projects/index.html.haml | 33 ++++++++++---- lib/ci/project_list_builder.rb | 21 +++++++++ spec/controllers/ci/projects_controller_spec.rb | 2 +- 8 files changed, 93 insertions(+), 95 deletions(-) delete mode 100644 app/views/ci/projects/_gl_projects.html.haml delete mode 100644 app/views/ci/projects/gitlab.html.haml create mode 100644 lib/ci/project_list_builder.rb diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 6483a84ee91..653384b7178 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -5,38 +5,32 @@ module Ci 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_access_project!, except: [:build, :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] + layout 'ci/project', except: :index 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) - @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) + if current_user + @projects = ProjectListBuilder.new.execute(current_user, params[:search]) - @projects = Ci::Project.where(gitlab_id: @gl_projects.map(&:id)).ordered_by_last_commit_date - @total_count = @gl_projects.size + @projects = @projects.page(@page).per(@limit) - @gl_projects = @gl_projects.where.not(id: @projects.map(&:gitlab_id)) + @total_count = @projects.size + end respond_to do |format| format.json do - pager_json("ci/projects/gitlab", @total_count) + pager_json("ci/projects/index", @total_count) end + format.html end - rescue - @error = 'Failed to fetch GitLab projects' end def show diff --git a/app/views/ci/projects/_gl_projects.html.haml b/app/views/ci/projects/_gl_projects.html.haml deleted file mode 100644 index 7bd30b37caf..00000000000 --- a/app/views/ci/projects/_gl_projects.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -- @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_json(methods: [:name_with_namespace, :path_with_namespace, :ssh_url_to_repo]) - = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm' diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index e4a811119e1..869747b6eb1 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -1,22 +1,36 @@ -- last_commit = project.last_commit -%tr.alert{class: commit_status_alert_class(last_commit) } - %td - = link_to [:ci, project] do - = 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.fa-globe - Public - - else - %i.fa.fa-lock - Private - %td - = project.commits.count +- if project.gitlab_ci_project + - ci_project = project.gitlab_ci_project + - last_commit = ci_project.last_commit + %tr.alert{class: commit_status_alert_class(last_commit) } + %td + = link_to [:ci, ci_project] do + = ci_project.name + %td + - if last_commit + #{last_commit.status} (#{commit_link(last_commit)}) + - if ci_project.last_commit_date + = time_ago_in_words ci_project.last_commit_date + ago + - else + No builds yet + %td + - if ci_project.public + %i.fa.fa-globe + Public + - else + %i.fa.fa-lock + Private + %td + = ci_project.commits.count +- else + %tr.light + %td + = project.name_with_namespace + %td + %small Not added to CI + %td + %td + = form_tag ci_projects_path do + = 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' + \ No newline at end of file diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml index 6d84b25a6af..4ab43a403f7 100644 --- a/app/views/ci/projects/_search.html.haml +++ b/app/views/ci/projects/_search.html.haml @@ -5,13 +5,7 @@ .input-group-addon %i.fa.fa-search - :coffeescript $('.ci-search-form').submit -> - NProgress.start() - query = $('.ci-search-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 + CiPager.init "#{ci_projects_path}" + "?search=" + query, #{Ci::ProjectsController::PROJECTS_BATCH}, false false diff --git a/app/views/ci/projects/gitlab.html.haml b/app/views/ci/projects/gitlab.html.haml deleted file mode 100644 index 2101aa932a4..00000000000 --- a/app/views/ci/projects/gitlab.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -- if @offset == 0 - .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 - - .wide-table-holder - %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.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 index 60ab29a66cf..8de205d57aa 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -1,13 +1,30 @@ - if current_user - .gray-content-block.top-block - = render "search" - .projects - %p.fetch-status.light - %i.fa.fa-refresh.fa-spin + - if @offset > 0 + = render @projects + - else + .gray-content-block.top-block + = render "search" + .projects + .gray-content-block.clearfix.light.second-block + .pull-left.fetch-status + - if params[:search].present? + by keyword: "#{params[:search]}", + #{@total_count} projects + + .wide-table-holder + %table.table.projects-table.content-list + %thead + %tr + %th Project Name + %th Last commit + %th Access + %th Commits + + = render @projects + %p.text-center.hide.loading + %i.fa.fa-refresh.fa-spin :coffeescript - $.get '#{gitlab_ci_projects_path}', (data) -> - $(".projects").html data.html - CiPager.init "#{gitlab_ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false + CiPager.init "#{ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false - else = render 'public' diff --git a/lib/ci/project_list_builder.rb b/lib/ci/project_list_builder.rb new file mode 100644 index 00000000000..da26f9a9f47 --- /dev/null +++ b/lib/ci/project_list_builder.rb @@ -0,0 +1,21 @@ +module Ci + class ProjectListBuilder + def execute(current_user, search = nil) + projects = current_user.authorized_projects + projects = projects.search(search) if search + + projects. + joins("LEFT JOIN ci_projects ON projects.id = ci_projects.gitlab_id + LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.id = last_commit.project_id"). + reorder("ci_projects.id is NULL ASC, + CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, + last_commit.committed_at DESC") + end + + private + + def last_commit_subquery + "(SELECT project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY project_id)" + end + end +end diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index 015788a05e1..c7a3cd50c20 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -84,7 +84,7 @@ describe Ci::ProjectsController do end it "searches projects" do - xhr :get, :gitlab, { search: "str", format: "js" }.with_indifferent_access + xhr :get, :index, { search: "str", format: "json" }.with_indifferent_access expect(response).to be_success expect(response.code).to eq('200') -- cgit v1.2.1 From 567eac0396cbf39b04320d0aec5a2e4c5afcd2bf Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Sep 2015 16:24:00 -0400 Subject: Reformat and copy edit the CI-to-CE migration guide --- doc/migrate_ci_to_ce/README.md | 418 +++++++++++++++++++++++------------------ 1 file changed, 231 insertions(+), 187 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 1e45f29dbb2..3930b165bbf 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -1,96 +1,105 @@ -## Migrate GitLab CI to GitLab CE/EE +## Migrate GitLab CI to GitLab CE or EE -## Notice +Beginning with version 8.0 of GitLab Community Edition (CE) and Enterprise +Edition (EE), GitLab CI is no longer its own application, but is instead built +into the CE and EE applications. -**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 guide will detail the process of migrating your CI installation and data +into your GitLab CE or EE installation. -This migration can't be done online and takes significant amount of time. -Make sure to plan it ahead. +### Before we begin -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 +**You need to have a working installation of GitLab CI version 7.14 to perform +this migration. The older versions are not supported and will most likely break +this migration procedure.** -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. +This migration cannot be performed online and takes a significant amount of +time. Make sure to plan ahead. -### 1. Stop CI server [CI] +If you are running a version of GitLab CI prior to 7.14 please follow the +appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/update/). + +The migration is divided into three parts: + +1. [GitLab CI](#part-i-gitlab-ci) +1. [Gitlab CE (or EE)](#part-ii-gitlab-ce-or-ee) +1. [Finishing Up](#part-iii-finishing-up) + +### Part I: GitLab CI + +#### 1. Stop GitLab 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 GitLab CI in case of failure.** +#### 2. Create a backup + +The migration procedure modifies the structure of the CI database. If something +goes wrong, you will not be able to revert to a previous version without a +backup: ```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 + ``` -#### a. Dump MySQL - - mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ - --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 - -#### 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 - -### 6. 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 the update guide: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md - -### 7. Stop GitLab CE/EE [CE] - -Before you can migrate data you need to stop GitLab CE/EE first. + - If both your CI and CE (or EE) installations use **postgresql** as the + `adapter`, use `pg_dump`: + + ```sh + pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ + --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql + ``` + + - If your CI installation uses **mysql2** as the `adapter` and your CE (or + EE) installation uses **postgresql**, use `mysqldump` to dump the database + and then convert it to PostgreSQL using [mysql-postgresql-converter]: + + ```sh + # 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 + ``` + +[mysql-postgresql-converter]: https://github.com/gitlabhq/mysql-postgresql-converter + +### Part II: GitLab CE (or EE) + +#### 1. Ensure GitLab is updated + +Your GitLab CE or EE installation **must be version 8.0**. If it's not, follow +the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md). + +#### 2. Stop GitLab + +Before you can migrate data you need to stop the GitLab service first: sudo service gitlab stop - -### 8. Backup GitLab CE/EE [CE] -This migration poses a **significant risk** of breaking your GitLab CE/EE. -**You should create the GitLab CI/EE backup before doing it.** +#### 3. Create a backup + +This migration poses a **significant risk** of breaking your GitLab +installation. Create a backup before proceeding: cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production -### 9. Copy secret tokens [CE] +#### 4. Copy secret tokens from CI 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. +You need to copy the contents of GitLab CI's `config/secrets.yml` file 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 - -### 10. 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`: +#### 5. New configuration options for `gitlab.yml` + +There are new configuration options available for `gitlab.yml`. 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 ``` -The new options include configuration of GitLab CI that are now being part of GitLab CE and EE. +The new options include configuration settings for GitLab CI. -### 11. Copy build logs [CE] +#### 6. Copy build logs -You need to copy the contents of `builds/` to the same directory in GitLab CE/EE. +You need to copy the contents of GitLab CI's `builds/` directory to the +corresponding directory in GitLab CE or 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. +The build logs are usually quite big so it may take a significant amount of +time. -### 12. Import GitLab CI database [CE] +#### 7. Import GitLab CI database -The one of the last steps is to import existing GitLab CI database. +Now you'll import the GitLab CI database dump that you [created +earlier](#create-a-database-dump) into the GitLab CE or EE 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 -The task does: +This task will: + 1. Delete data from all existing CI tables -1. Import database data -1. Fix database auto increments +1. Import data from database dump +1. Fix database auto-increments 1. Fix tags assigned to Builds and Runners 1. Fix services used by CI -### 13. Start GitLab [CE] +#### 8. Start GitLab -You can start GitLab CI/EE now and see if everything is working. +You can start GitLab CI (or EE) now and see if everything is working: sudo service gitlab start -### 14. 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 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: - - # 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; - } - - # 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; - } - - # 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.** - -You should also make sure that you can do: +### Part III: Finishing Up + +#### 1. Update Nginx configuration + +To ensure that your existing CI runners are able to communicate with the +migrated installation, and that existing build triggers still work, you'll need +to update your Nginx configuration to redirect requests for the old locations to +the new ones. + +Edit `/etc/nginx/sites-available/gitlab_ci` and paste: + +```nginx +# 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; + } + + # 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; + } + + # 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 you substitute these placeholder values with your real ones: + +1. `YOUR_CI_SERVER_FQDN`: The existing public-facing address of your GitLab CI + install (e.g., `ci.gitlab.com`). +1. `YOUR_GITLAB_SERVER_FQDN`: The current public-facing address of your GitLab + CE (or EE) install (e.g., `gitlab.com`). + +**Make sure not to remove the `/ci$request_uri` part. This is required to properly forward the requests.** + +You should also make sure that you can: + 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. +1. `curl https://YOUR_CI_SERVER_FQDN/` from your GitLab CE (or EE) server. -## Check your configuration +#### 2. Check Nginx configuration sudo nginx -t -## Restart nginx +#### 3. Restart Nginx sudo /etc/init.d/nginx restart -### 15. Done! +#### 4. 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/. +If everything went well you should be able to access your migrated CI install by +visiting `https://gitlab.example.com/ci/`. -The GitLab CI should also work when using the previous address, redirecting you to the GitLab CE/EE. +If you visit the old GitLab CI address, you should be redirected to the new one. **Enjoy!** -- cgit v1.2.1 From 8cc788be4ee4465db9fc42db0b4d8f219e0bc2e9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Sep 2015 16:29:06 -0400 Subject: Indent database dump steps one more level [ci skip] --- doc/migrate_ci_to_ce/README.md | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 3930b165bbf..412aded9ea6 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -118,45 +118,45 @@ each installation. values `IN_UPPERCASE` with the corresponding values from your **CI installation's** `config/database.yml` files above. - - If both your CI and CE (or EE) installations use **mysql2** as the `adapter`, use - `mysqldump`: - - ```sh - mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ - --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 - ``` - - - If both your CI and CE (or EE) installations use **postgresql** as the - `adapter`, use `pg_dump`: - - ```sh - pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ - --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql - ``` - - - If your CI installation uses **mysql2** as the `adapter` and your CE (or - EE) installation uses **postgresql**, use `mysqldump` to dump the database - and then convert it to PostgreSQL using [mysql-postgresql-converter]: - - ```sh - # 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 - ``` + - If both your CI and CE (or EE) installations use **mysql2** as the `adapter`, use + `mysqldump`: + + ```sh + mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ + --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 + ``` + + - If both your CI and CE (or EE) installations use **postgresql** as the + `adapter`, use `pg_dump`: + + ```sh + pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ + --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql + ``` + + - If your CI installation uses **mysql2** as the `adapter` and your CE (or + EE) installation uses **postgresql**, use `mysqldump` to dump the database + and then convert it to PostgreSQL using [mysql-postgresql-converter]: + + ```sh + # 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 + ``` [mysql-postgresql-converter]: https://github.com/gitlabhq/mysql-postgresql-converter -- cgit v1.2.1 From eae049505c3bb6a09e524949e81335d0aa3f8e6f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Sep 2015 16:35:59 -0400 Subject: Fix link to "create a database dump" header --- doc/migrate_ci_to_ce/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 412aded9ea6..2f59dc69943 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -217,7 +217,7 @@ time. #### 7. Import GitLab CI database Now you'll import the GitLab CI database dump that you [created -earlier](#create-a-database-dump) into the GitLab CE or EE database: +earlier](#5-create-a-database-dump) into the GitLab CE or EE 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 -- cgit v1.2.1 From 62c0e5ab6a8ff88510d9ed0e2731ae6fda8bbacd Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Sep 2015 16:38:31 -0400 Subject: Redcarpet didn't like unordered lists deeply nested in an ordered one I don't really like making these an ordered list since they're mutually exclusive, but I think the structure is what's important. [ci skip] --- doc/migrate_ci_to_ce/README.md | 82 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 2f59dc69943..34b70c7140c 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -111,52 +111,52 @@ each installation. # socket: /tmp/mysql.sock ``` -1. Depending on the values for `adapter`, you will have to use different - commands to perform the database dump. +1. Depending on the values for `adapter`, you will have to use one of three + different commands to perform the database dump. **NOTE:** For any of the commands below, you'll need to substitute the values `IN_UPPERCASE` with the corresponding values from your **CI installation's** `config/database.yml` files above. - - If both your CI and CE (or EE) installations use **mysql2** as the `adapter`, use - `mysqldump`: - - ```sh - mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ - --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 - ``` - - - If both your CI and CE (or EE) installations use **postgresql** as the - `adapter`, use `pg_dump`: - - ```sh - pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ - --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql - ``` - - - If your CI installation uses **mysql2** as the `adapter` and your CE (or - EE) installation uses **postgresql**, use `mysqldump` to dump the database - and then convert it to PostgreSQL using [mysql-postgresql-converter]: - - ```sh - # 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 - ``` + 1. If both your CI and CE (or EE) installations use **mysql2** as the `adapter`, use + `mysqldump`: + + ```sh + mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ + --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 + ``` + + 1. If both your CI and CE (or EE) installations use **postgresql** as the + `adapter`, use `pg_dump`: + + ```sh + pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ + --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql + ``` + + 1. If your CI installation uses **mysql2** as the `adapter` and your CE (or + EE) installation uses **postgresql**, use `mysqldump` to dump the + database and then convert it to PostgreSQL using [mysql-postgresql-converter]: + + ```sh + # 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 + ``` [mysql-postgresql-converter]: https://github.com/gitlabhq/mysql-postgresql-converter -- cgit v1.2.1 From d4be96747860ec0c5e9e64a1095a1dd95c48545c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 23:27:08 +0200 Subject: Fix 500 when accessing private project when not logged in --- Gemfile.lock | 3 --- app/controllers/ci/application_controller.rb | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index eeaddc95a8e..f43e885ce48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -928,6 +928,3 @@ DEPENDENCIES webmock (~> 1.21.0) whenever (~> 0.8.4) wikicloth (= 0.8.1) - -BUNDLED WITH - 1.10.6 diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index a5868da377f..d45c4e9caf1 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -10,9 +10,7 @@ module Ci def authenticate_public_page! unless project.public - unless current_user - redirect_to(new_user_sessions_path) and return - end + authenticate_user! return access_denied! unless can?(current_user, :read_project, gl_project) end -- cgit v1.2.1 From b53994bb9e4a8f3c2a18725ecbb152cc6463f5d8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 23:33:16 +0200 Subject: Remove Cancel button in Project settings --- app/views/ci/projects/_form.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/ci/projects/_form.html.haml b/app/views/ci/projects/_form.html.haml index d50e1a83b06..e782fd8a0f7 100644 --- a/app/views/ci/projects/_form.html.haml +++ b/app/views/ci/projects/_form.html.haml @@ -96,6 +96,5 @@ .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' -- cgit v1.2.1 From e86668984f4f7465ed39ffcf9ce09aabfa8f79da Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 18 Sep 2015 09:44:10 +0200 Subject: Style CI project/build list Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/projects.scss | 27 +------------------------- app/helpers/ci/builds_helper.rb | 10 +++++----- app/helpers/ci/commits_helper.rb | 12 ++++++------ app/views/ci/admin/builds/_build.html.haml | 2 +- app/views/ci/admin/projects/_project.html.haml | 2 +- app/views/ci/admin/runners/show.html.haml | 2 +- app/views/ci/builds/_build.html.haml | 2 +- app/views/ci/builds/show.html.haml | 4 ++-- app/views/ci/commits/_commit.html.haml | 2 +- app/views/ci/commits/show.html.haml | 2 +- app/views/ci/projects/_project.html.haml | 2 +- 11 files changed, 21 insertions(+), 46 deletions(-) diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index e5d69360c2c..3b8a29269ad 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -13,31 +13,6 @@ .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; } @@ -47,8 +22,8 @@ } td { + color: $gl-gray; vertical-align: middle !important; - border-color: inherit !important; a { font-weight: normal; diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb index cdabdad17d2..422994f5984 100644 --- a/app/helpers/ci/builds_helper.rb +++ b/app/helpers/ci/builds_helper.rb @@ -16,15 +16,15 @@ module Ci ci_project_build_url(build.project, build) end - def build_status_alert_class(build) + def build_status_css_class(build) if build.success? - 'alert-success' + 'build-success' elsif build.failed? - 'alert-danger' + 'build-danger' elsif build.canceled? - 'alert-disabled' + 'build-disabled' else - 'alert-warning' + 'build-warning' end end diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb index 74de30e006e..2c86bbffa36 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -1,17 +1,17 @@ module Ci module CommitsHelper - def commit_status_alert_class(commit) - return 'alert-info' unless commit + def commit_status_css_class(commit) + return 'build-info' unless commit case commit.status when 'success' - 'alert-success' + 'build-success' when 'failed', 'canceled' - 'alert-danger' + 'build-danger' when 'skipped' - 'alert-disabled' + 'build-disabled' else - 'alert-warning' + 'build-warning' end end diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml index 47f8df8f98e..923219a2e46 100644 --- a/app/views/ci/admin/builds/_build.html.haml +++ b/app/views/ci/admin/builds/_build.html.haml @@ -1,5 +1,5 @@ - if build.commit && build.project - %tr.build.alert{class: build_status_alert_class(build)} + %tr.build{class: build_status_css_class(build)} %td.build-link = link_to ci_project_build_url(build.project, build) do %strong #{build.id} diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml index 505dd4b3fdc..8017fb2086b 100644 --- a/app/views/ci/admin/projects/_project.html.haml +++ b/app/views/ci/admin/projects/_project.html.haml @@ -1,5 +1,5 @@ - last_commit = project.last_commit -%tr.alert{class: commit_status_alert_class(last_commit) } +%tr{class: commit_status_css_class(last_commit) } %td = project.id %td diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 24e0ad3b070..409db6ba82c 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -102,7 +102,7 @@ %th Finished at - @builds.each do |build| - %tr.build.alert{class: build_status_alert_class(build)} + %tr.build.alert{class: build_status_css_class(build)} %td.status = build.status diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml index da306c9f020..a53c64ba3f5 100644 --- a/app/views/ci/builds/_build.html.haml +++ b/app/views/ci/builds/_build.html.haml @@ -1,4 +1,4 @@ -%tr.build.alert{class: build_status_alert_class(build)} +%tr.build{class: build_status_css_class(build)} %td.status = build.status diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index d1e955b5012..2a08bf14f43 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -21,7 +21,7 @@ .row .col-md-9 - .build-head.alert{class: build_status_alert_class(@build)} + .build-head.alert{class: build_status_css_class(@build)} %h4 - if @build.commit.tag? Build for tag @@ -150,7 +150,7 @@ %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)} + %tr.build.alert{class: build_status_css_class(build)} %td = link_to ci_project_build_url(@project, build) do %span ##{build.id} diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index c1b1988d147..aa116fa12dc 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -1,4 +1,4 @@ -%tr.build.alert{class: commit_status_alert_class(commit)} +%tr.build{class: commit_status_css_class(commit)} %td.status = commit.status - if commit.running? diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 1aeb557314a..8f75d95b214 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -44,7 +44,7 @@ %h3 Status -.build.alert{class: commit_status_alert_class(@commit)} +.build.alert{class: commit_status_css_class(@commit)} .status = @commit.status.titleize diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index e4a811119e1..47415265b44 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -1,5 +1,5 @@ - last_commit = project.last_commit -%tr.alert{class: commit_status_alert_class(last_commit) } +%tr{class: commit_status_css_class(last_commit) } %td = link_to [:ci, project] do = project.name -- cgit v1.2.1 From fe17a236e37c2d7504925a474a716f714f160b2d Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Wed, 16 Sep 2015 13:51:42 +0200 Subject: Allow custom info to be added to Help page Fixes #2536 Signed-off-by: Jeroen van Baarsen --- CHANGELOG | 1 + app/controllers/admin/application_settings_controller.rb | 1 + app/views/admin/application_settings/_form.html.haml | 5 +++++ app/views/help/index.html.haml | 3 +++ .../20150916114643_add_help_page_text_to_application_settings.rb | 5 +++++ db/schema.rb | 1 + features/steps/admin/settings.rb | 1 + 7 files changed, 17 insertions(+) create mode 100644 db/migrate/20150916114643_add_help_page_text_to_application_settings.rb diff --git a/CHANGELOG b/CHANGELOG index aab2416a09b..811089bbc61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -53,6 +53,7 @@ v 8.0.0 (unreleased) - Add FogBugz project import (Jared Szechy) - Sort users autocomplete lists by user (Allister Antosik) - Webhook for issue now contains repository field (Jungkook Park) + - Add ability to add custom text to the help page (Jeroen van Baarsen) v 7.14.3 - No changes diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index f38e07af84b..7c134d2ec9b 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -46,6 +46,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :gravatar_enabled, :twitter_sharing_enabled, :sign_in_text, + :help_page_text, :home_page_url, :after_sign_out_path, :max_attachment_size, diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 330b8bbf9d2..a36ae0b766c 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -118,6 +118,11 @@ .col-sm-10 = f.text_area :sign_in_text, class: 'form-control', rows: 4 .help-block Markdown enabled + .form-group + = f.label :help_page_text, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :help_page_text, class: 'form-control', rows: 4 + .help-block Markdown enabled .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index bf4b7234b21..f492aaf4c0a 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -17,6 +17,9 @@ Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises. %br Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank'}. + - if current_application_settings.help_page_text.present? + %hr + = markdown(current_application_settings.help_page_text) %hr diff --git a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb new file mode 100644 index 00000000000..37a27f11935 --- /dev/null +++ b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :help_page_text, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 151f34e1965..bf5a88f10e8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -45,6 +45,7 @@ ActiveRecord::Schema.define(version: 20150916145038) do t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" + t.text "help_page_text" end create_table "audit_events", force: true do |t| diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb index 7a6aec23af8..6acbf46eb20 100644 --- a/features/steps/admin/settings.rb +++ b/features/steps/admin/settings.rb @@ -7,6 +7,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps step 'I modify settings and save form' do uncheck 'Gravatar enabled' fill_in 'Home page URL', with: 'https://about.gitlab.com/' + fill_in 'Help page text', with: 'Example text' click_button 'Save' end -- cgit v1.2.1 From 0f41417a8c88f11aedaeb2c59b524e47db7f40e0 Mon Sep 17 00:00:00 2001 From: Ben Hadley-Evans Date: Fri, 18 Sep 2015 09:21:09 +0100 Subject: Correct markdown.md docs RE use in commit messages. Remove the line from the markdown documentation which states GFM can be used in commit messages as this is currently not true. See issue gitlab-org/gitlab-ce#1582. [ci skip] --- doc/markdown/markdown.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index 6fdb2fe1491..d83838127c9 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -33,7 +33,6 @@ For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It e You can use GFM in -- commit messages - comments - issues - merge requests -- cgit v1.2.1 From b6de7ad49eb40a108f0776d727ae37adb6f4559e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 18 Sep 2015 10:45:42 +0200 Subject: Fix 500 on trending projects if isntance has 100k+ projects Signed-off-by: Dmitriy Zaporozhets --- app/finders/trending_projects_finder.rb | 17 ++++------------- app/views/shared/projects/_list.html.haml | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb index f3f4d461efa..9ea342cb26d 100644 --- a/app/finders/trending_projects_finder.rb +++ b/app/finders/trending_projects_finder.rb @@ -2,21 +2,12 @@ class TrendingProjectsFinder def execute(current_user, start_date = nil) start_date ||= Date.today - 1.month + projects = projects_for(current_user) + # Determine trending projects based on comments count # for period of time - ex. month - trending_project_ids = Note. - select("notes.project_id, count(notes.project_id) as pcount"). - where('notes.created_at > ?', start_date). - group("project_id"). - reorder("pcount DESC"). - map(&:project_id) - - sql_order_ids = trending_project_ids.reverse. - map { |project_id| "id = #{project_id}" }.join(", ") - - # Get list of projects that user allowed to see - projects = projects_for(current_user) - projects.where(id: trending_project_ids).reorder(sql_order_ids) + projects.joins(:notes).where('notes.created_at > ?', start_date). + group("projects.id").reorder("count(notes.id) DESC") end private diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 021e3b689a1..330b0626d63 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -8,7 +8,7 @@ = render "shared/projects/project", project: project, avatar: avatar, stars: stars, css_class: css_class - - if projects.count > projects_limit + - if projects.size > projects_limit %li.bottom.center .light #{projects_limit} of #{pluralize(projects.count, 'project')} displayed. -- cgit v1.2.1 From e41a29b0c381458f789d62cfeb9ad94920f78804 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 18 Sep 2015 10:19:08 +0200 Subject: Allow to disable GitLab CI --- app/controllers/admin/application_settings_controller.rb | 1 + app/controllers/ci/application_controller.rb | 9 +++++++++ app/controllers/ci/projects_controller.rb | 8 ++++++-- app/models/application_setting.rb | 3 ++- app/views/admin/application_settings/_form.html.haml | 9 +++++++++ app/views/ci/projects/disabled.html.haml | 1 + app/views/layouts/ci/application.html.haml | 2 +- app/views/layouts/nav/_dashboard.html.haml | 2 +- config/initializers/1_settings.rb | 1 + config/routes.rb | 2 +- .../20150918084513_add_ci_enabled_to_application_settings.rb | 5 +++++ db/schema.rb | 3 ++- lib/ci/api/api.rb | 4 ++++ lib/ci/api/helpers.rb | 4 ++++ 14 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 app/views/ci/projects/disabled.html.haml create mode 100644 db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 7c134d2ec9b..5f70582cbb7 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -56,6 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :restricted_signup_domains_raw, :version_check_enabled, :user_oauth_applications, + :ci_enabled, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index d45c4e9caf1..8d8ff75ff72 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -1,5 +1,7 @@ module Ci class ApplicationController < ::ApplicationController + before_action :check_enable_flag! + def self.railtie_helpers_paths "app/helpers/ci" end @@ -8,6 +10,13 @@ module Ci private + def check_enable_flag! + unless current_application_settings.ci_enabled + redirect_to(disabled_ci_projects_path) + return + end + end + def authenticate_public_page! unless project.public authenticate_user! diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 653384b7178..b1f1c087b9e 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -5,13 +5,17 @@ module Ci 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, :badge, :index, :show, :new, :create] + before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :create, :disabled] 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] + skip_before_action :check_enable_flag!, only: [:disabled] protect_from_forgery except: :build - layout 'ci/project', except: :index + layout 'ci/project', except: [:index, :disabled] + + def disabled + end def index @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c8841178e93..784f5c96a0a 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -83,7 +83,8 @@ class ApplicationSetting < ActiveRecord::Base default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], - import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] + import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], + ci_enabled: Settings.gitlab_ci['enabled'] ) end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index a36ae0b766c..1476e29524c 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -124,5 +124,14 @@ = f.text_area :help_page_text, class: 'form-control', rows: 4 .help-block Markdown enabled + %fieldset + %legend Continuous Integration + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :ci_enabled do + = f.check_box :ci_enabled + Enable Continuous Integration + .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/ci/projects/disabled.html.haml b/app/views/ci/projects/disabled.html.haml new file mode 100644 index 00000000000..95276d894ed --- /dev/null +++ b/app/views/ci/projects/disabled.html.haml @@ -0,0 +1 @@ +Continuous Integration has been disabled. Please ask your administrator to enable it. diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml index 9cc7fb85142..38023468d0b 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 = "CI Projects" + - header_title = "Continuous Integration" - if current_user = render "layouts/header/default", title: header_title - else diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 3bda7c46959..b94165aac39 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -31,7 +31,7 @@ %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count - = nav_link(path: 'ci/projects#index') do + = nav_link(path: ['ci/projects#index', 'ci/projects#disabled']) do = link_to ci_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do = icon('building fw') %span diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index fe81ffd4205..274e3d409fb 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -178,6 +178,7 @@ Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious' # CI # Settings['gitlab_ci'] ||= Settingslogic.new({}) +Settings.gitlab_ci['enabled'] = true if Settings.gitlab_ci['enabled'].nil? 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) diff --git a/config/routes.rb b/config/routes.rb index b5a84c1f192..c3b9475146d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,7 @@ Gitlab::Application.routes.draw do resources :projects do collection do post :add - get :gitlab + get :disabled end member do diff --git a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb new file mode 100644 index 00000000000..6cf668a170e --- /dev/null +++ b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddCiEnabledToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :ci_enabled, :boolean, null: false, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index bf5a88f10e8..d70c4b58e93 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: 20150916145038) do +ActiveRecord::Schema.define(version: 20150918084513) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -46,6 +46,7 @@ ActiveRecord::Schema.define(version: 20150916145038) do t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" t.text "help_page_text" + t.boolean "ci_enabled", default: true, null: false end create_table "audit_events", force: true do |t| diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 172c6f22164..7bb8869d61a 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -23,6 +23,10 @@ module Ci rack_response({ 'message' => '500 Internal Server Error' }, 500) end + before do + check_enable_flag! + end + format :json helpers Helpers diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index e602cda81d6..13aa71f4630 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -3,6 +3,10 @@ module Ci module Helpers UPDATE_RUNNER_EVERY = 60 + def check_enable_flag! + not_found! unless current_application_settings.ci_enabled + end + def authenticate_runners! forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN end -- cgit v1.2.1 From 97fa9904064b6799be7f329651387c5df2fd0387 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 18 Sep 2015 12:46:04 +0200 Subject: Use 400 to notify that CI API is disabled --- lib/ci/api/helpers.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 13aa71f4630..8e893aa5cc6 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -4,7 +4,9 @@ module Ci UPDATE_RUNNER_EVERY = 60 def check_enable_flag! - not_found! unless current_application_settings.ci_enabled + unless current_application_settings.ci_enabled + render_api_error!('400 (Bad request) CI is disabled', 400) + end end def authenticate_runners! -- cgit v1.2.1 From 87db46e2d8aecfb93c3183991efbd582cc4165dc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 18 Sep 2015 12:11:49 +0000 Subject: Fix typo in Reply by email docs --- doc/reply_by_email/postfix.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md index b8ab07d9fe1..c0ac59bb922 100644 --- a/doc/reply_by_email/postfix.md +++ b/doc/reply_by_email/postfix.md @@ -303,8 +303,8 @@ Courier, which we will install later to add IMAP authentication, requires mailbo ## Done! -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. +If all the tests were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) guide to configure GitLab. --------- -_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._ +_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._ \ No newline at end of file -- cgit v1.2.1 From d7242054bafc49d66b0102f12706ac71a9e5d04c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 18 Sep 2015 12:47:25 +0300 Subject: add pg_schema to backup config --- CHANGELOG | 1 + config/gitlab.yml.example | 1 + config/initializers/1_settings.rb | 1 + lib/backup/database.rb | 8 ++++++-- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 811089bbc61..7a54700af04 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -54,6 +54,7 @@ v 8.0.0 (unreleased) - Sort users autocomplete lists by user (Allister Antosik) - Webhook for issue now contains repository field (Jungkook Park) - Add ability to add custom text to the help page (Jeroen van Baarsen) + - Add pg_schema to backup config v 7.14.3 - No changes diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 0005d44e0f2..eada70faebc 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -306,6 +306,7 @@ production: &base path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) # archive_permissions: 0640 # Permissions for the resulting backup.tar file (default: 0600) # keep_time: 604800 # default: 0 (forever) (in seconds) + # pg_schema: public # default: nil, it means that all schemas will be backed up # upload: # # Fog storage connection settings, see http://fog.io/storage/ . # connection: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index fe81ffd4205..37c76df12c4 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -219,6 +219,7 @@ Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_s # Settings['backup'] ||= Settingslogic.new({}) Settings.backup['keep_time'] ||= 0 +Settings.backup['pg_schema'] = nil Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root) Settings.backup['archive_permissions'] ||= 0600 Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) diff --git a/lib/backup/database.rb b/lib/backup/database.rb index ce75476a09b..959ac4b7868 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -25,8 +25,12 @@ module Backup when "postgresql" then $progress.print "Dumping PostgreSQL database #{config['database']} ... " pg_env - # Pass '--clean' to include 'DROP TABLE' statements in the DB dump. - system('pg_dump', '--clean', config['database'], out: db_file_name) + pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump. + if Gitlab.config.backup.pg_schema + pgsql_args << "-n" + pgsql_args << Gitlab.config.backup.pg_schema + end + system('pg_dump', *pgsql_args, config['database'], out: db_file_name) end report_success(success) abort 'Backup failed' unless success -- cgit v1.2.1 From 600d6eeff326fe42e4074b4bb963fc10e401c6f5 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 18 Sep 2015 16:20:32 +0300 Subject: Fix CI status in the MR page --- app/models/project_services/buildkite_service.rb | 8 ----- app/models/project_services/drone_ci_service.rb | 14 -------- app/models/project_services/gitlab_ci_service.rb | 39 +++++----------------- .../project_services/buildkite_service_spec.rb | 14 -------- .../project_services/gitlab_ci_service_spec.rb | 13 +++----- 5 files changed, 13 insertions(+), 75 deletions(-) diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 9e5da6f45d2..40058b53df5 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -69,14 +69,6 @@ class BuildkiteService < CiService "#{project_url}/builds?commit=#{sha}" end - def builds_path - "#{project_url}/builds?branch=#{project.default_branch}" - end - - def status_img_path - "#{buildkite_endpoint('badge')}/#{status_token}.svg" - end - def title 'Buildkite' end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 3e2b7faecdb..b13fa3e9ecb 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -135,20 +135,6 @@ class DroneCiService < CiService commit_page(sha, ref) end - def builds_path - url = [drone_url, "#{project.namespace.path}/#{project.path}"] - - URI.join(*url).to_s - end - - def status_img_path - url = [drone_url, - "api/badges/#{project.namespace.path}/#{project.path}/status.svg", - "?branch=#{URI::encode(project.default_branch)}"] - - URI.join(*url).to_s - end - def title 'Drone CI' end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index acbbc9935b6..1bba6326949 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -58,24 +58,13 @@ class GitlabCiService < CiService service_hook.execute(data) end - def commit_status_path(sha, ref) - URI::encode(project_url + "/refs/#{ref}/commits/#{sha}/status.json?token=#{token}") - end - - def get_ci_build(sha, ref) - @ci_builds ||= {} - @ci_builds[sha] ||= HTTParty.get(commit_status_path(sha, ref), verify: false) + def get_ci_commit(sha, ref) + Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref) end def commit_status(sha, ref) - response = get_ci_build(sha, ref) - - if response.code == 200 and response["status"] - response["status"] - else - :error - end - rescue Errno::ECONNREFUSED + get_ci_commit(sha, ref).status + rescue ActiveRecord::RecordNotFound :error end @@ -101,25 +90,13 @@ class GitlabCiService < CiService end def commit_coverage(sha, ref) - response = get_ci_build(sha, ref) - - if response.code == 200 and response["coverage"] - response["coverage"] - end - rescue Errno::ECONNREFUSED - nil + get_ci_commit(sha, ref).coverage + rescue ActiveRecord::RecordNotFound + :error end def build_page(sha, ref) - URI::encode(project_url + "/refs/#{ref}/commits/#{sha}") - end - - def builds_path - project_url + "?ref=" + project.default_branch - end - - def status_img_path - project_url + "/status.png?ref=" + project.default_branch + Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) end def title diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 9445d96c337..230807ea672 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -63,19 +63,5 @@ describe BuildkiteService do ) end end - - describe :builds_page do - it 'returns the correct path to the builds page' do - expect(@service.builds_path).to eq( - 'https://buildkite.com/account-name/example-project/builds?branch=default-brancho' - ) - end - end - - describe :status_img_path do - it 'returns the correct path to the status image' do - expect(@service.status_img_path).to eq('https://badge.buildkite.com/secret-sauce-status-token.svg') - end - end end end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index a14384c87b4..516d1d4fc1d 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -55,22 +55,19 @@ describe GitlabCiService do describe 'commits methods' do before do + @ci_project = create(:ci_project) @service = GitlabCiService.new allow(@service).to receive_messages( service_hook: true, project_url: 'http://ci.gitlab.org/projects/2', - token: 'verySecret' + token: 'verySecret', + project: @ci_project.gl_project ) end - describe :commit_status_path do - it { expect(@service.commit_status_path("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c/status.json?token=verySecret")} - it { expect(@service.commit_status_path("issue#2", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/issue%232/status.json?token=verySecret")} - end - describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c")} - it { expect(@service.build_page("issue#2", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/issue%232")} + it { expect(@service.build_page("2ab7834c", 'master')).to eq("/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")} + it { expect(@service.build_page("issue#2", 'master')).to eq("/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")} end describe "execute" do -- cgit v1.2.1 From a0c1a12dee276f5d587514d24432e7647e07aeec Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 16 Sep 2015 12:43:16 +0300 Subject: remove API calls from CE to CI --- CHANGELOG | 1 + app/controllers/ci/projects_controller.rb | 12 +---- app/models/ci/project.rb | 15 ------ app/models/project_services/gitlab_ci_service.rb | 47 +++++------------ app/services/ci/create_project_service.rb | 9 +--- app/services/projects/fork_service.rb | 2 +- app/workers/fork_registration_worker.rb | 12 ----- config/routes.rb | 1 - doc/ci/api/README.md | 1 - doc/ci/api/forks.md | 23 --------- features/steps/project/services.rb | 4 +- lib/ci/api/api.rb | 1 - lib/ci/api/forks.rb | 37 -------------- spec/controllers/ci/projects_controller_spec.rb | 43 ---------------- spec/lib/gitlab/backend/grack_auth_spec.rb | 1 - .../project_services/gitlab_ci_service_spec.rb | 37 ++------------ spec/requests/api/projects_spec.rb | 2 +- spec/requests/api/services_spec.rb | 4 +- spec/requests/ci/api/forks_spec.rb | 59 ---------------------- spec/services/ci/create_project_service_spec.rb | 4 +- spec/services/projects/fork_service_spec.rb | 3 +- spec/workers/fork_registration_worker_spec.rb | 10 ---- 22 files changed, 31 insertions(+), 297 deletions(-) delete mode 100644 app/workers/fork_registration_worker.rb delete mode 100644 doc/ci/api/forks.md delete mode 100644 lib/ci/api/forks.rb delete mode 100644 spec/requests/ci/api/forks_spec.rb delete mode 100644 spec/workers/fork_registration_worker_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 7a54700af04..a1b17e58156 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -55,6 +55,7 @@ v 8.0.0 (unreleased) - Webhook for issue now contains repository field (Jungkook Park) - Add ability to add custom text to the help page (Jeroen van Baarsen) - Add pg_schema to backup config + - Removed API calls from CE to CI v 7.14.3 - No changes diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index b1f1c087b9e..40b61edb0a9 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -55,7 +55,7 @@ module Ci 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")) + @project = Ci::CreateProjectService.new.execute(current_user, project_data) if @project.persisted? redirect_to ci_project_path(@project, show_guide: true), notice: 'Project was successfully created.' @@ -86,16 +86,6 @@ module Ci 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 diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index ae901d4ccd0..37fbcc287bb 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -92,21 +92,6 @@ module Ci project end - # TODO: remove - def from_gitlab(user, scope = :owned, options) - opts = user.authenticate_options - opts.merge! options - - raise 'Implement me of fix' - #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 diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 1bba6326949..820dd3f567c 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -19,22 +19,12 @@ # class GitlabCiService < CiService - API_PREFIX = "api/v1" - - prop_accessor :project_url, :token, :enable_ssl_verification - validates :project_url, - presence: true, - format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated? - validates :token, - presence: true, - format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated? + prop_accessor :token after_save :compose_service_hook, if: :activated? def compose_service_hook hook = service_hook || build_service_hook - hook.url = [project_url, "/build", "?token=#{token}"].join("") - hook.enable_ssl_verification = enable_ssl_verification hook.save end @@ -55,7 +45,8 @@ class GitlabCiService < CiService end end - service_hook.execute(data) + ci_project = Ci::Project.find_by(gitlab_id: project.id) + Ci::CreateCommitService.new.execute(ci_project, data) end def get_ci_commit(sha, ref) @@ -68,24 +59,22 @@ class GitlabCiService < CiService :error end - def fork_registration(new_project, private_token) - params = { + def fork_registration(new_project, current_user) + params = OpenStruct.new({ id: new_project.id, name_with_namespace: new_project.name_with_namespace, path_with_namespace: new_project.path_with_namespace, web_url: new_project.web_url, default_branch: new_project.default_branch, ssh_url_to_repo: new_project.ssh_url_to_repo - } - - HTTParty.post( - fork_registration_path, - body: { - project_id: project.id, - project_token: token, - private_token: private_token, - data: params }, - verify: false + }) + + ci_project = Ci::Project.find_by!(gitlab_id: project.id) + + Ci::CreateProjectService.new.execute( + current_user, + params, + ci_project ) end @@ -112,11 +101,7 @@ class GitlabCiService < CiService end def fields - [ - { type: 'text', name: 'token', placeholder: 'GitLab CI project specific token' }, - { type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3' }, - { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" } - ] + [] end private @@ -125,10 +110,6 @@ class GitlabCiService < CiService repository.blob_at(sha, '.gitlab-ci.yml') end - def fork_registration_path - project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks") - end - def repository project.repository end diff --git a/app/services/ci/create_project_service.rb b/app/services/ci/create_project_service.rb index 0419612d521..839d3f6b444 100644 --- a/app/services/ci/create_project_service.rb +++ b/app/services/ci/create_project_service.rb @@ -2,20 +2,15 @@ module Ci class CreateProjectService include Gitlab::Application.routes.url_helpers - def execute(current_user, params, project_route, forked_project = nil) + def execute(current_user, params, 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), - } - gl_project = ::Project.find(@project.gitlab_id) gl_project.build_missing_services - gl_project.gitlab_ci_service.update_attributes(data.merge(active: true)) + gl_project.gitlab_ci_service.update_attributes(active: true, token: @project.token) end if forked_project diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 50f208b11d1..2e995d6fd51 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -18,7 +18,7 @@ module Projects if new_project.persisted? if @project.gitlab_ci? - ForkRegistrationWorker.perform_async(@project.id, new_project.id, @current_user.private_token) + @project.gitlab_ci_service.fork_registration(new_project, @current_user) end end diff --git a/app/workers/fork_registration_worker.rb b/app/workers/fork_registration_worker.rb deleted file mode 100644 index fffa8b3a659..00000000000 --- a/app/workers/fork_registration_worker.rb +++ /dev/null @@ -1,12 +0,0 @@ -class ForkRegistrationWorker - include Sidekiq::Worker - - sidekiq_options queue: :default - - def perform(from_project_id, to_project_id, private_token) - from_project = Project.find(from_project_id) - to_project = Project.find(to_project_id) - - from_project.gitlab_ci_service.fork_registration(to_project, private_token) - end -end diff --git a/config/routes.rb b/config/routes.rb index c3b9475146d..512dda7b547 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,7 +18,6 @@ Gitlab::Application.routes.draw do member do get :status, to: 'projects#badge' get :integration - post :build post :toggle_shared_runners get :dumped_yaml end diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index e47e5c46732..33c5b172e98 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -6,7 +6,6 @@ - [Runners](runners.md) - [Commits](commits.md) - [Builds](builds.md) -- [Forks](forks.md) ## Authentication diff --git a/doc/ci/api/forks.md b/doc/ci/api/forks.md deleted file mode 100644 index 8f32e2d3b40..00000000000 --- a/doc/ci/api/forks.md +++ /dev/null @@ -1,23 +0,0 @@ -# Forks API - -This API is intended to aid in the setup and configuration of -forked projects on Gitlab CI. - -__Authentication is done by GitLab user token & GitLab project token__ - -## Forks - -### Create fork for project - - - -``` -POST /ci/forks -``` - -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) diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 0327fd61981..d3b462bfd31 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -26,13 +26,11 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps step 'I fill gitlab-ci settings' do check 'Active' - fill_in 'Project url', with: 'http://ci.gitlab.org/projects/3' - fill_in 'Token', with: 'verySecret' click_button 'Save' end step 'I should see service settings saved' do - expect(find_field('Project url').value).to eq 'http://ci.gitlab.org/projects/3' + expect(find_field('Active').value).to eq '1' end step 'I click hipchat service link' do diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 7bb8869d61a..5109c84e0ea 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -36,7 +36,6 @@ module Ci mount Commits mount Runners mount Projects - mount Forks mount Triggers end end diff --git a/lib/ci/api/forks.rb b/lib/ci/api/forks.rb deleted file mode 100644 index 152883a599f..00000000000 --- a/lib/ci/api/forks.rb +++ /dev/null @@ -1,37 +0,0 @@ -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) - - fork = Ci::CreateProjectService.new.execute( - current_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/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index c7a3cd50c20..3e579f9a7d6 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -5,49 +5,6 @@ describe Ci::ProjectsController do @project = FactoryGirl.create :ci_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) { OpenStruct.new({ id: @project.gitlab_id }) } diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index d9676445908..9bed8f8ee5c 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -180,7 +180,6 @@ describe Grack::Auth do gitlab_ci_service = project.build_gitlab_ci_service gitlab_ci_service.active = true gitlab_ci_service.token = token - gitlab_ci_service.project_url = "http://google.com" gitlab_ci_service.save env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 516d1d4fc1d..e0da04a3f40 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -26,33 +26,6 @@ describe GitlabCiService do it { is_expected.to have_one(:service_hook) } end - describe 'validations' do - context 'active' do - before { allow(subject).to receive(:activated?).and_return(true) } - - it { is_expected.to validate_presence_of(:token) } - it { is_expected.to validate_presence_of(:project_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://ci.example.com/project/1').for(:project_url) } - it { is_expected.not_to allow_value('token with spaces').for(:token) } - it { is_expected.not_to allow_value('token/with%spaces').for(:token) } - it { is_expected.not_to allow_value('this is not url').for(:project_url) } - it { is_expected.not_to allow_value('http//noturl').for(:project_url) } - it { is_expected.not_to allow_value('ftp://ci.example.com/projects/3').for(:project_url) } - end - - context 'inactive' do - before { allow(subject).to receive(:activated?).and_return(false) } - - it { is_expected.not_to validate_presence_of(:token) } - it { is_expected.not_to validate_presence_of(:project_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://ci.example.com/project/1').for(:project_url) } - it { is_expected.to allow_value('token with spaces').for(:token) } - it { is_expected.to allow_value('ftp://ci.example.com/projects/3').for(:project_url) } - end - end - describe 'commits methods' do before do @ci_project = create(:ci_project) @@ -77,8 +50,6 @@ describe GitlabCiService do it "calls ci_yaml_file" do service_hook = double - expect(service_hook).to receive(:execute) - expect(@service).to receive(:service_hook).and_return(service_hook) expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha]) @service.execute(push_sample_data) @@ -88,7 +59,7 @@ describe GitlabCiService do describe "Fork registration" do before do - @old_project = create(:empty_project) + @old_project = create(:ci_project).gl_project @project = create(:empty_project) @user = create(:user) @@ -101,9 +72,9 @@ describe GitlabCiService do ) end - it "performs http reuquest to ci" do - stub_request(:post, "http://ci.gitlab.org/api/v1/forks") - @service.fork_registration(@project, @user.private_token) + it "creates fork on CI" do + expect_any_instance_of(Ci::CreateProjectService).to receive(:execute) + @service.fork_registration(@project, @user) end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 5bd8206b890..3007a15b0b1 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -89,7 +89,7 @@ describe API::API, api: true do it 'returns projects in the correct order when ci_enabled_first parameter is passed' do [project, project2, project3].each{ |project| project.build_missing_services } - project2.gitlab_ci_service.update(active: true, token: "token", project_url: "http://ci.example.com/projects/1") + project2.gitlab_ci_service.update(active: true, token: "token") get api('/projects', user), { ci_enabled_first: 'true' } expect(response.status).to eq(200) expect(json_response).to be_an Array diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index fb3b235446f..9aa60826f21 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -17,9 +17,9 @@ describe API::API, api: true do it "should return if required fields missing" do attrs = service_attrs - + required_attributes = service_attrs_list.select do |attr| - service_klass.validators_on(attr).any? do |v| + service_klass.validators_on(attr).any? do |v| v.class == ActiveRecord::Validations::PresenceValidator end end diff --git a/spec/requests/ci/api/forks_spec.rb b/spec/requests/ci/api/forks_spec.rb deleted file mode 100644 index 37fa1e82f25..00000000000 --- a/spec/requests/ci/api/forks_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'spec_helper' - -describe Ci::API::API do - include ApiHelpers - - let(:project) { FactoryGirl.create(:ci_project) } - let(:private_token) { create(:user).private_token } - - let(:options) do - { - private_token: private_token, - url: GitlabCi.config.gitlab_ci.url - } - end - - before do - stub_gitlab_calls - end - - - describe "POST /forks" do - let(:project_info) do - { - project_id: project.gitlab_id, - project_token: project.token, - data: { - id: create(:empty_project).id, - 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" - } - } - end - - context "with valid info" do - before do - options.merge!(project_info) - end - - it "should create a project with valid data" do - post ci_api("/forks"), options - expect(response.status).to eq(201) - expect(json_response['name']).to eq("Gitlab.org / Underscore") - end - end - - context "with invalid project info" do - before do - options.merge!({}) - end - - it "should error with invalid data" do - post ci_api("/forks"), options - expect(response.status).to eq(400) - end - end - end -end diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index 64041b8d5a2..c0af515aa8f 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -7,7 +7,7 @@ describe Ci::CreateProjectService do describe :execute do context 'valid params' do - subject { service.execute(current_user, project, 'http://localhost/projects/:project_id') } + subject { service.execute(current_user, project) } it { is_expected.to be_kind_of(Ci::Project) } it { is_expected.to be_persisted } @@ -24,7 +24,7 @@ describe Ci::CreateProjectService 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) } + subject { service.execute(current_user, project, ci_origin_project) } it "uses project as a template for settings and jobs" do expect(subject.shared_runners_enabled).to be_truthy diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 7c4bb74b77f..18ab333c1d1 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -44,10 +44,11 @@ describe Projects::ForkService do context 'GitLab CI is enabled' do it "calls fork registrator for CI" do + create(:ci_project, gl_project: @from_project) @from_project.build_missing_services @from_project.gitlab_ci_service.update_attributes(active: true) - expect(ForkRegistrationWorker).to receive(:perform_async) + expect_any_instance_of(Ci::CreateProjectService).to receive(:execute) fork_project(@from_project, @to_user) end diff --git a/spec/workers/fork_registration_worker_spec.rb b/spec/workers/fork_registration_worker_spec.rb deleted file mode 100644 index cc6f574b29c..00000000000 --- a/spec/workers/fork_registration_worker_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ - -require 'spec_helper' - -describe ForkRegistrationWorker do - context "as a resque worker" do - it "reponds to #perform" do - expect(ForkRegistrationWorker.new).to respond_to(:perform) - end - end -end -- cgit v1.2.1 From 6776dcf90b9e101b8c9341ea83621e48f87584d6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 18 Sep 2015 18:24:33 +0200 Subject: Add colors to build labels Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/projects.scss | 42 +++++++++++++++++++++++++++++++++ app/views/ci/builds/_build.html.haml | 3 ++- app/views/ci/commits/_commit.html.haml | 3 ++- app/views/ci/commits/show.html.haml | 4 ++-- app/views/ci/projects/index.html.haml | 18 +++++++------- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index 3b8a29269ad..43cf025c9af 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -13,6 +13,48 @@ .builds, .projects-table { + .build-danger { + .ci-status { + color: $gl-danger; + border-color: $gl-danger; + } + } + + .build-success { + .ci-status { + color: $gl-success; + border-color: $gl-success; + } + } + + .build-info { + .ci-status { + color: $gl-info; + border-color: $gl-info; + } + } + + .build-disabled { + .ci-status { + color: $gl-gray; + border-color: $gl-gray; + } + } + + .build-warning { + .ci-status { + color: $gl-warning; + border-color: $gl-warning; + } + } + + .ci-status { + padding: 2px 5px; + margin-right: 5px; + border: 1px solid #EEE; + @include border-radius(4px); + } + .light { border-color: $border-color; } diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml index a53c64ba3f5..b31acec460d 100644 --- a/app/views/ci/builds/_build.html.haml +++ b/app/views/ci/builds/_build.html.haml @@ -1,6 +1,7 @@ %tr.build{class: build_status_css_class(build)} %td.status - = build.status + %span.ci-status< + = build.status %td.build-link = link_to ci_project_build_path(build.project, build) do diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index aa116fa12dc..4b2f5e28a4d 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -1,6 +1,7 @@ %tr.build{class: commit_status_css_class(commit)} %td.status - = commit.status + %span.ci-status< + = commit.status - if commit.running? · = commit.stage diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 8f75d95b214..477382bb6c1 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -44,8 +44,8 @@ %h3 Status -.build.alert{class: commit_status_css_class(@commit)} - .status +.build{class: commit_status_css_class(@commit)} + .ci-status = @commit.status.titleize %h3 diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 8de205d57aa..2b618d61f79 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -11,16 +11,16 @@ by keyword: "#{params[:search]}", #{@total_count} projects - .wide-table-holder - %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 %p.text-center.hide.loading %i.fa.fa-refresh.fa-spin :coffeescript -- cgit v1.2.1 From b2c06bf707c6951c5c99c189327aec785b2c79ea Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 18 Sep 2015 18:32:09 +0200 Subject: Fix gitlab ci projects page UI Signed-off-by: Dmitriy Zaporozhets --- app/views/ci/projects/index.html.haml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 8de205d57aa..2b618d61f79 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -11,16 +11,16 @@ by keyword: "#{params[:search]}", #{@total_count} projects - .wide-table-holder - %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 %p.text-center.hide.loading %i.fa.fa-refresh.fa-spin :coffeescript -- cgit v1.2.1 From a22b6c591f2c383468d1a6ec54208e407c0ca032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Fri, 18 Sep 2015 19:49:04 +0200 Subject: fix failing migration on mysql We didn't specify that the db user needs the right to create temporary tables before. That's why the migration will fail on most installations. This commits removes the "TEMPORARY" from the migration and changes the documentation to include the necessary rights for the future. --- db/migrate/20150817163600_deduplicate_user_identities.rb | 2 +- doc/install/database_mysql.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20150817163600_deduplicate_user_identities.rb b/db/migrate/20150817163600_deduplicate_user_identities.rb index fab669c2905..fceffc48018 100644 --- a/db/migrate/20150817163600_deduplicate_user_identities.rb +++ b/db/migrate/20150817163600_deduplicate_user_identities.rb @@ -1,7 +1,7 @@ class DeduplicateUserIdentities < ActiveRecord::Migration def change execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;' - execute 'CREATE TEMPORARY TABLE tt_migration_DeduplicateUserIdentities AS SELECT id,provider,user_id FROM identities;' + execute 'CREATE TABLE tt_migration_DeduplicateUserIdentities AS SELECT id,provider,user_id FROM identities;' execute 'DELETE FROM identities WHERE id NOT IN ( SELECT MIN(id) FROM tt_migration_DeduplicateUserIdentities GROUP BY user_id, provider);' execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;' end diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 362c492d0ac..c565e90da2f 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -36,7 +36,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; # Grant the GitLab user necessary permissions on the database - mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlabhq_production`.* TO 'git'@'localhost'; + mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `gitlabhq_production`.* TO 'git'@'localhost'; # Quit the database session mysql> \q -- cgit v1.2.1 From 619f04c196731b2a274802dfb4b4d3c3664e5465 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 18 Sep 2015 14:03:42 -0400 Subject: Allow relative links to go up one directory level --- lib/gitlab/markdown/relative_link_filter.rb | 41 ++++++++++++++++------ .../gitlab/markdown/relative_link_filter_spec.rb | 18 +++++++--- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index 8c5cf51bfe1..d613c205509 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -59,25 +59,44 @@ module Gitlab end def relative_file_path(path) - nested_path = build_nested_path(path, context[:requested_path]) + nested_path = build_relative_path(path, context[:requested_path]) file_exists?(nested_path) ? nested_path : path end - # Covering a special case, when the link is referencing file in the same - # directory. - # If we are at doc/api/README.md and the README.md contains relative - # links like [Users](users.md), this takes the request - # path(doc/api/README.md) and replaces the README.md with users.md so the - # path looks like doc/api/users.md. - # If we are at doc/api and the README.md shown in below the tree view - # this takes the request path(doc/api) and adds users.md so the path - # looks like doc/api/users.md - def build_nested_path(path, request_path) + # Convert a relative path into its correct location based on the currently + # requested path + # + # path - Relative path String + # request_path - Currently-requested path String + # + # Examples: + # + # # File in the same directory as the current path + # build_relative_path("users.md", "doc/api/README.md") + # # => "doc/api/users.md" + # + # # File in the same directory, which is also the current path + # build_relative_path("users.md", "doc/api") + # # => "doc/api/users.md" + # + # # Going up one level to a different directory + # build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md") + # # => "doc/update/7.14-to-8.0.md" + # + # Returns a String + def build_relative_path(path, request_path) return request_path if path.empty? return path unless request_path parts = request_path.split('/') parts.pop if path_type(request_path) != 'tree' + + # Allow for going up one directory + if parts.length > 1 && path.start_with?('../') + parts.pop + path.sub!('../', '') + end + parts.push(path).join('/') end diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb index 7f4d67e403f..ab9c2008ea7 100644 --- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb +++ b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb @@ -4,14 +4,16 @@ require 'spec_helper' module Gitlab::Markdown describe RelativeLinkFilter do - def filter(doc) - described_class.call(doc, { + def filter(doc, contexts = {}) + contexts.reverse_merge!({ commit: project.commit, project: project, project_wiki: project_wiki, ref: ref, requested_path: requested_path }) + + described_class.call(doc, contexts) end def image(path) @@ -75,6 +77,14 @@ module Gitlab::Markdown to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" end + it 'rebuilds relative URL for a file in the repo up one directory' do + relative_link = link('../api/README.md') + doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md') + + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" + end + it 'rebuilds relative URL for a file in the repo with an anchor' do doc = filter(link('README.md#section')) expect(doc.at_css('a')['href']). @@ -108,8 +118,8 @@ module Gitlab::Markdown escaped = Addressable::URI.escape(path) # Stub these methods so the file doesn't actually need to be in the repo - allow_any_instance_of(described_class).to receive(:file_exists?). - and_return(true) + allow_any_instance_of(described_class). + to receive(:file_exists?).and_return(true) allow_any_instance_of(described_class). to receive(:image?).with(path).and_return(true) -- cgit v1.2.1 From 52eac60405d523c4016f41526890b102f40d3afa Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Sep 2015 11:33:16 -0700 Subject: Make Markdown area formatting consistent --- app/assets/stylesheets/generic/typography.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 73034c84f9a..41189432bf6 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -89,6 +89,10 @@ a > code { } } +.md-area { + @include md-typography; +} + .md { @include md-typography; } -- cgit v1.2.1 From de8497ca56a20f8c9362fdb8a9a8629a551f0a4d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Sep 2015 12:02:01 -0700 Subject: Fix Error 500 when comparing non-existing branches Closes #2593 --- app/controllers/projects/compare_controller.rb | 10 +++++---- app/services/compare_service.rb | 5 ++++- .../projects/compare_controller_spec.rb | 26 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index d9b3adae95b..d15004f93a6 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -16,10 +16,12 @@ class Projects::CompareController < Projects::ApplicationController compare_result = CompareService.new. execute(@project, head_ref, @project, base_ref) - @commits = compare_result.commits - @diffs = compare_result.diffs - @commit = @commits.last - @line_notes = [] + if compare_result + @commits = compare_result.commits + @diffs = compare_result.diffs + @commit = @commits.last + @line_notes = [] + end end def create diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb index 70f642baaaa..bfe6a3dc4be 100644 --- a/app/services/compare_service.rb +++ b/app/services/compare_service.rb @@ -4,7 +4,10 @@ require 'securerandom' # and return Gitlab::CompareResult object that responds to commits and diffs class CompareService def execute(source_project, source_branch, target_project, target_branch) - source_sha = source_project.commit(source_branch).sha + source_commit = source_project.commit(source_branch) + return unless source_commit + + source_sha = source_commit.sha # If compare with other project we need to fetch ref first unless target_project == source_project diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index b643b354073..2a447248b70 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -22,4 +22,30 @@ describe Projects::CompareController do expect(assigns(:diffs).length).to be >= 1 expect(assigns(:commits).length).to be >= 1 end + + describe 'non-existent refs' do + it 'invalid source ref' do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + from: 'non-existent', + to: ref_to) + + expect(response).to be_success + expect(assigns(:diffs)).to eq([]) + expect(assigns(:commits)).to eq([]) + end + + it 'invalid target ref' do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + from: ref_from, + to: 'non-existent') + + expect(response).to be_success + expect(assigns(:diffs)).to eq(nil) + expect(assigns(:commits)).to eq(nil) + end + end end -- cgit v1.2.1 From a19a0b431c05d7e17aca61f2e04abfe6fb5ac5b2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Sep 2015 23:54:52 +0200 Subject: Fix 500 when showing project page and there's no CI project present --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 6e2f9645661..1a5c1c978c9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -428,7 +428,7 @@ class Project < ActiveRecord::Base end def gitlab_ci? - gitlab_ci_service && gitlab_ci_service.active + gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present? end def ci_services -- cgit v1.2.1 From 68b15af46c1ddc44552252bd097d9748693dda23 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 18 Sep 2015 16:35:38 -0400 Subject: Fix IssuableContext javascript We were calling `affix` before the events were bound so they never triggered correctly. Closes #2597 --- app/assets/javascripts/issuable_context.js.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index 176d9cabefa..c4d3e619f5e 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -11,12 +11,13 @@ class @IssuableContext $(this).submit() $('.issuable-details').waitForImages -> + $('.issuable-affix').on 'affix.bs.affix', -> + $(@).width($(@).outerWidth()) + .on 'affixed-top.bs.affix affixed-bottom.bs.affix', -> + $(@).width('') + $('.issuable-affix').affix offset: top: -> @top = ($('.issuable-affix').offset().top - 70) bottom: -> @bottom = $('.footer').outerHeight(true) - $('.issuable-affix').on 'affix.bs.affix', -> - $(@).width($(@).outerWidth()) - .on 'affixed-top.bs.affix affixed-bottom.bs.affix', -> - $(@).width('') -- cgit v1.2.1 From 34da6f00f6a1513703232bd071237c1ed402b6d1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 19 Sep 2015 00:21:24 +0200 Subject: Return callout css for CI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/callout.scss | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/assets/stylesheets/generic/callout.scss diff --git a/app/assets/stylesheets/generic/callout.scss b/app/assets/stylesheets/generic/callout.scss new file mode 100644 index 00000000000..f1699d21c9b --- /dev/null +++ b/app/assets/stylesheets/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; +} + -- cgit v1.2.1 From 4daffbe4064e0f39e401c962bb0c0f2d8a092810 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 19 Sep 2015 01:40:44 +0200 Subject: Cleanup GitLab CI UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/projects.scss | 83 +++++++++++--------------- app/helpers/ci/application_helper.rb | 22 +++++++ app/helpers/ci/builds_helper.rb | 22 ------- app/helpers/ci/commits_helper.rb | 15 ----- app/views/ci/admin/builds/_build.html.haml | 4 +- app/views/ci/admin/projects/_project.html.haml | 5 +- app/views/ci/admin/runners/show.html.haml | 4 +- app/views/ci/builds/_build.html.haml | 5 +- app/views/ci/builds/show.html.haml | 69 +++++++++++---------- app/views/ci/commits/_commit.html.haml | 5 +- app/views/ci/commits/show.html.haml | 55 +++++++++-------- app/views/ci/projects/_project.html.haml | 5 +- 12 files changed, 132 insertions(+), 162 deletions(-) diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index 43cf025c9af..c63a67ab720 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -13,48 +13,6 @@ .builds, .projects-table { - .build-danger { - .ci-status { - color: $gl-danger; - border-color: $gl-danger; - } - } - - .build-success { - .ci-status { - color: $gl-success; - border-color: $gl-success; - } - } - - .build-info { - .ci-status { - color: $gl-info; - border-color: $gl-info; - } - } - - .build-disabled { - .ci-status { - color: $gl-gray; - border-color: $gl-gray; - } - } - - .build-warning { - .ci-status { - color: $gl-warning; - border-color: $gl-warning; - } - } - - .ci-status { - padding: 2px 5px; - margin-right: 5px; - border: 1px solid #EEE; - @include border-radius(4px); - } - .light { border-color: $border-color; } @@ -75,23 +33,16 @@ } .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; } } @@ -105,4 +56,38 @@ margin-bottom: 16px; } } + + .ci-status { + padding: 2px 7px; + margin-right: 5px; + border: 1px solid #EEE; + white-space: nowrap; + @include border-radius(4px); + + &.ci-failed { + color: $gl-danger; + border-color: $gl-danger; + } + + &.ci-success { + color: $gl-success; + border-color: $gl-success; + } + + &.ci-info { + color: $gl-info; + border-color: $gl-info; + } + + &.ci-disabled { + color: $gl-gray; + border-color: $gl-gray; + } + + &.ci-pending, + &.ci-running { + color: $gl-warning; + border-color: $gl-warning; + } + } } diff --git a/app/helpers/ci/application_helper.rb b/app/helpers/ci/application_helper.rb index 7e880b00b3a..9fe6282bb81 100644 --- a/app/helpers/ci/application_helper.rb +++ b/app/helpers/ci/application_helper.rb @@ -28,5 +28,27 @@ module Ci "#{pluralize(seconds, "second")}" end end + + def ci_icon_for_status(status) + icon_name = + case status + when 'success' + 'check-square' + when 'failed' + 'close' + when 'running', 'pending' + 'clock-o' + else + 'circle' + end + + icon(icon_name) + end + + def ci_status_with_icon(status) + content_tag :span, class: "ci-status ci-#{status}" do + ci_icon_for_status(status) + ' '.html_safe + status + end + end end end diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb index 422994f5984..5d6e785d951 100644 --- a/app/helpers/ci/builds_helper.rb +++ b/app/helpers/ci/builds_helper.rb @@ -15,27 +15,5 @@ module Ci def build_url(build) ci_project_build_url(build.project, build) end - - def build_status_css_class(build) - if build.success? - 'build-success' - elsif build.failed? - 'build-danger' - elsif build.canceled? - 'build-disabled' - else - 'build-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 index 2c86bbffa36..9069aed5b4d 100644 --- a/app/helpers/ci/commits_helper.rb +++ b/app/helpers/ci/commits_helper.rb @@ -1,20 +1,5 @@ module Ci module CommitsHelper - def commit_status_css_class(commit) - return 'build-info' unless commit - - case commit.status - when 'success' - 'build-success' - when 'failed', 'canceled' - 'build-danger' - when 'skipped' - 'build-disabled' - else - 'build-warning' - end - end - def ci_commit_path(commit) ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) end diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml index 923219a2e46..778d51d03be 100644 --- a/app/views/ci/admin/builds/_build.html.haml +++ b/app/views/ci/admin/builds/_build.html.haml @@ -1,11 +1,11 @@ - if build.commit && build.project - %tr.build{class: build_status_css_class(build)} + %tr.build %td.build-link = link_to ci_project_build_url(build.project, build) do %strong #{build.id} %td.status - = build.status + = ci_status_with_icon(build.status) %td.commit-link = commit_link(build.commit) diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml index 8017fb2086b..c461206c72a 100644 --- a/app/views/ci/admin/projects/_project.html.haml +++ b/app/views/ci/admin/projects/_project.html.haml @@ -1,5 +1,5 @@ - last_commit = project.last_commit -%tr{class: commit_status_css_class(last_commit) } +%tr %td = project.id %td @@ -7,8 +7,9 @@ %strong= project.name %td - if last_commit - #{last_commit.status} (#{commit_link(last_commit)}) + = ci_status_with_icon(last_commit.status) - if project.last_commit_date + · = time_ago_in_words project.last_commit_date ago - else diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 409db6ba82c..09905e0eb47 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -102,9 +102,9 @@ %th Finished at - @builds.each do |build| - %tr.build.alert{class: build_status_css_class(build)} + %tr.build %td.status - = build.status + = ci_status_with_icon(build.status) %td.status = build.project.name diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml index b31acec460d..515b862e992 100644 --- a/app/views/ci/builds/_build.html.haml +++ b/app/views/ci/builds/_build.html.haml @@ -1,7 +1,6 @@ -%tr.build{class: build_status_css_class(build)} +%tr.build %td.status - %span.ci-status< - = build.status + = ci_status_with_icon(build.status) %td.build-link = link_to ci_project_build_path(build.project, build) do diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml index 2a08bf14f43..839dbf5c554 100644 --- a/app/views/ci/builds/show.html.haml +++ b/app/views/ci/builds/show.html.haml @@ -1,15 +1,16 @@ #up-build-trace - if @commit.matrix? - %ul.nav.nav-tabs.append-bottom-10 + %ul.center-top-menu - @commit.builds_without_retry_sorted.each do |build| %li{class: ('active' if build == @build) } = link_to ci_project_build_url(@project, build) do - %i{class: build_icon_css_class(build)} + = ci_icon_for_status(build.status) %span - Build ##{build.id} - if build.name - · = build.name + - else + = build.id + - unless @commit.builds_without_retry.include?(@build) %li.active @@ -19,34 +20,33 @@ %i.fa.fa-warning-sign This build was retried. -.row - .col-md-9 - .build-head.alert{class: build_status_css_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.fa-time - #{duration_in_words(@build.finished_at, @build.started_at)} +.gray-content-block + .build-head + %h4 + - if @build.commit.tag? + Build for tag + %code #{@build.ref} + - else + Build for commit + %strong.monospace= commit_link(@build.commit) + from - .clearfix - = @build.status - .pull-right - = @build.updated_at.stamp('19:00 Aug 27') + = link_to ci_project_path(@build.project, ref: @build.ref) do + %strong.monospace= "#{@build.ref}" + - if @build.duration + .pull-right + %span + %i.fa.fa-time + #{duration_in_words(@build.finished_at, @build.started_at)} + .clearfix + = ci_status_with_icon(@build.status) + .pull-right + = @build.updated_at.stamp('19:00 Aug 27') +.row.prepend-top-default + .col-md-9 .clearfix - if @build.active? .autoscroll-container @@ -150,13 +150,16 @@ %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_css_class(build)} + %tr.build %td - = link_to ci_project_build_url(@project, build) do - %span ##{build.id} + = ci_icon_for_status(build.status) %td - - if build.name - = build.name + = link_to ci_project_build_url(@project, build) do + - if build.name + = build.name + - else + %span ##{build.id} + %td.status= build.status diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index 4b2f5e28a4d..1eacfca944f 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -1,7 +1,6 @@ -%tr.build{class: commit_status_css_class(commit)} +%tr.build %td.status - %span.ci-status< - = commit.status + = ci_status_with_icon(commit.status) - if commit.running? · = commit.stage diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml index 477382bb6c1..8f38aa84676 100644 --- a/app/views/ci/commits/show.html.haml +++ b/app/views/ci/commits/show.html.haml @@ -1,29 +1,34 @@ .commit-info - %pre.commit-message - #{@commit.git_commit_message} + .append-bottom-20 + = ci_status_with_icon(@commit.status) - .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)} + .gray-content-block.middle-block + %pre.commit-message + #{@commit.git_commit_message} + + .gray-content-block.second-block + .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)} + %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 && can?(current_user, :manage_builds, gl_project) .pull-right @@ -42,12 +47,6 @@ .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit -%h3 Status - -.build{class: commit_status_css_class(@commit)} - .ci-status - = @commit.status.titleize - %h3 Builds - if @commit.duration > 0 diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index 29e9f023dda..844b6677b3d 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -1,14 +1,13 @@ - if project.gitlab_ci_project - ci_project = project.gitlab_ci_project - last_commit = ci_project.last_commit - %tr{class: commit_status_css_class(last_commit) } + %tr %td = link_to [:ci, ci_project] do = ci_project.name %td - if last_commit - %span.ci-status< - = last_commit.status + = ci_status_with_icon(last_commit.status) = commit_link(last_commit) · - if ci_project.last_commit_date -- cgit v1.2.1 From 9021200ad3312666bbcd6e0b4a876023d72283ab Mon Sep 17 00:00:00 2001 From: Kirilll Zaitsev Date: Sat, 19 Sep 2015 04:18:10 +0300 Subject: Removed format validation --- app/models/project_services/drone_ci_service.rb | 2 +- spec/models/project_services/drone_ci_service_spec.rb | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index b13fa3e9ecb..c73c4b058a1 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -26,7 +26,7 @@ class DroneCiService < CiService format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated? validates :token, presence: true, - format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated? + if: :activated? after_save :compose_service_hook, if: :activated? diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index bad9a9e6e1a..e9967f5fe0b 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -34,8 +34,6 @@ describe DroneCiService do it { is_expected.to validate_presence_of(:drone_url) } it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } it { is_expected.to allow_value('http://ci.example.com').for(:drone_url) } - it { is_expected.not_to allow_value('token with spaces').for(:token) } - it { is_expected.not_to allow_value('token/with%spaces').for(:token) } it { is_expected.not_to allow_value('this is not url').for(:drone_url) } it { is_expected.not_to allow_value('http//noturl').for(:drone_url) } it { is_expected.not_to allow_value('ftp://ci.example.com').for(:drone_url) } @@ -48,7 +46,6 @@ describe DroneCiService do it { is_expected.not_to validate_presence_of(:drone_url) } it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } it { is_expected.to allow_value('http://drone.example.com').for(:drone_url) } - it { is_expected.to allow_value('token with spaces').for(:token) } it { is_expected.to allow_value('ftp://drone.example.com').for(:drone_url) } end end -- cgit v1.2.1 From 9fb3ef8888cc49619e259ddb5e9c559e9724be86 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 19 Sep 2015 16:22:04 +0200 Subject: Apply new design to pagination Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/gl_variables.scss | 12 +++++----- app/assets/stylesheets/generic/blocks.scss | 7 +++--- app/assets/stylesheets/generic/pagination.scss | 32 ++++++++++++++++++++++++++ app/views/kaminari/gitlab/_paginator.html.haml | 2 +- 4 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 app/assets/stylesheets/generic/pagination.scss diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss index bfef5f78f83..7378d404008 100644 --- a/app/assets/stylesheets/base/gl_variables.scss +++ b/app/assets/stylesheets/base/gl_variables.scss @@ -65,20 +65,20 @@ $legend-color: $text-color; // //## -$pagination-color: #fff; -$pagination-bg: $brand-success; +$pagination-color: $gl-gray; +$pagination-bg: $background-color; $pagination-border: transparent; $pagination-hover-color: #fff; -$pagination-hover-bg: darken($brand-success, 15%); +$pagination-hover-bg: $brand-info; $pagination-hover-border: transparent; $pagination-active-color: #fff; -$pagination-active-bg: darken($brand-success, 15%); +$pagination-active-bg: $brand-info; $pagination-active-border: transparent; -$pagination-disabled-color: #b4bcc2; -$pagination-disabled-bg: lighten($brand-success, 15%); +$pagination-disabled-color: #fff; +$pagination-disabled-bg: lighten($brand-info, 15%); $pagination-disabled-border: transparent; diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss index ce024272a30..6ce34b5c3e8 100644 --- a/app/assets/stylesheets/generic/blocks.scss +++ b/app/assets/stylesheets/generic/blocks.scss @@ -20,11 +20,11 @@ .gray-content-block { margin: -$gl-padding; - background-color: #f8fafc; + background-color: $background-color; padding: $gl-padding; margin-bottom: 0px; - border-top: 1px solid #e7e9ed; - border-bottom: 1px solid #e7e9ed; + border-top: 1px solid $border-color; + border-bottom: 1px solid $border-color; color: $gl-gray; &.top-block { @@ -48,6 +48,7 @@ &.footer-block { margin-top: 0; + border-bottom: none; margin-bottom: -$gl-padding; } diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/generic/pagination.scss new file mode 100644 index 00000000000..a937677ebdc --- /dev/null +++ b/app/assets/stylesheets/generic/pagination.scss @@ -0,0 +1,32 @@ +.gl-pagination { + border-top: 1px solid $border-color; + background-color: $background-color; + margin: -$gl-padding; + margin-top: 0; + + .pagination { + padding: 0; + margin: 0; + display: block; + + li.next, + li.prev { + > a { + color: $link-color; + + &:hover { + color: #fff; + } + } + } + + li > a, + li > span { + border: none; + margin: 0; + @include border-radius(0 !important); + padding: 13px 19px; + border-right: 1px solid $border-color; + } + } +} diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml index 4f7996e4996..b8d419b5894 100644 --- a/app/views/kaminari/gitlab/_paginator.html.haml +++ b/app/views/kaminari/gitlab/_paginator.html.haml @@ -7,7 +7,7 @@ -# paginator: the paginator that renders the pagination tags inside = paginator.render do %div.gl-pagination - %ul.pagination + %ul.pagination.clearfix = prev_page_tag unless current_page.first? - each_page do |page| - if page.left_outer? || page.right_outer? || page.inside_window? -- cgit v1.2.1 From ce8f78a57168bbacb1320a4dc4e9e79b878221d4 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 19 Sep 2015 21:12:32 -0400 Subject: Bump devise to 3.5.2 --- Gemfile | 2 +- Gemfile.lock | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 1903d66e6ab..426e67e7e3f 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries -gem "devise", '~> 3.2.4' +gem "devise", '~> 3.5.2' gem "devise-async", '~> 0.9.0' gem 'omniauth', "~> 1.2.2" gem 'omniauth-google-oauth2', '~> 0.2.5' diff --git a/Gemfile.lock b/Gemfile.lock index f43e885ce48..a8a067eb785 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,10 +136,11 @@ GEM activerecord (>= 3.2.0, < 5.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (3.2.4) + devise (3.5.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) + responders thread_safe (~> 0.1) warden (~> 1.2.3) devise-async (0.9.0) @@ -148,7 +149,7 @@ GEM activemodel activesupport attr_encrypted (~> 1.3.2) - devise (>= 3.2.4, < 3.5) + devise (~> 3.5.0) railties rotp (< 2) diff-lcs (1.2.5) @@ -558,6 +559,8 @@ GEM request_store (1.2.0) rerun (0.10.0) listen (~> 2.7, >= 2.7.3) + responders (1.1.2) + railties (>= 3.2, < 4.2) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) @@ -806,7 +809,7 @@ DEPENDENCIES d3_rails (~> 3.5.5) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) - devise (~> 3.2.4) + devise (~> 3.5.2) devise-async (~> 0.9.0) devise-two-factor (~> 1.0.1) diffy (~> 3.0.3) @@ -928,3 +931,6 @@ DEPENDENCIES webmock (~> 1.21.0) whenever (~> 0.8.4) wikicloth (= 0.8.1) + +BUNDLED WITH + 1.10.6 -- cgit v1.2.1 From 69723d20024821fb4206e899ffa3acd2da690315 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 19 Sep 2015 21:15:13 -0400 Subject: Devise 3.5.x deprecates `confirm!` in favor of `confirm` --- app/controllers/admin/users_controller.rb | 2 +- db/fixtures/production/001_admin.rb | 2 +- spec/mailers/notify_spec.rb | 6 +++--- spec/models/user_spec.rb | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 6092c79c254..a19b1abee27 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -56,7 +56,7 @@ class Admin::UsersController < Admin::ApplicationController end def confirm - if user.confirm! + if user.confirm redirect_to :back, notice: "Successfully confirmed" else redirect_to :back, alert: "Error occurred. User was not confirmed" diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index 1c8740f6ba9..b0c0b6450f6 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -19,7 +19,7 @@ admin = User.create( admin.projects_limit = 10000 admin.admin = true admin.save! -admin.confirm! +admin.confirm if admin.valid? puts %Q[ diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 97c07ad7d55..2c97a521d96 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -712,7 +712,7 @@ describe Notify do before do user.update_attribute(:email, "user@company.com") - user.confirm! + user.confirm end it "is sent from the committer email" do @@ -730,7 +730,7 @@ describe Notify do before do user.update_attribute(:email, "user@something.company.com") - user.confirm! + user.confirm end it "is sent from the default email" do @@ -748,7 +748,7 @@ describe Notify do before do user.update_attribute(:email, "user@mpany.com") - user.confirm! + user.confirm end it "is sent from the default email" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index eeb9069aa17..480950859a2 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -188,7 +188,7 @@ describe User do end it 'confirms a user' do - user.confirm! + user.confirm expect(user.confirmed?).to be_truthy end end -- cgit v1.2.1 From 259851c0d601632ad02a9f6ac0d59276552570b9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 19 Sep 2015 21:16:18 -0400 Subject: Bump devise-two-factor to 2.0.0 Addresses internal https://dev.gitlab.org/gitlab/gitlabhq/issues/2605 See https://github.com/tinfoil/devise-two-factor/pull/43 --- Gemfile | 2 +- Gemfile.lock | 9 ++++----- app/controllers/profiles/two_factor_auths_controller.rb | 2 +- app/controllers/sessions_controller.rb | 2 +- db/migrate/20150920010715_add_consumed_timestep_to_users.rb | 5 +++++ db/schema.rb | 3 ++- spec/controllers/profiles/two_factor_auths_controller_spec.rb | 4 ++-- 7 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20150920010715_add_consumed_timestep_to_users.rb diff --git a/Gemfile b/Gemfile index 426e67e7e3f..924ee382f4d 100644 --- a/Gemfile +++ b/Gemfile @@ -38,7 +38,7 @@ gem 'omniauth_crowd' gem "rack-oauth2", "~> 1.0.5" # Two-factor authentication -gem 'devise-two-factor', '~> 1.0.1' +gem 'devise-two-factor', '~> 2.0.0' gem 'rqrcode-rails3', '~> 0.1.7' gem 'attr_encrypted', '~> 1.3.4' diff --git a/Gemfile.lock b/Gemfile.lock index a8a067eb785..320f7629fb6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -145,13 +145,12 @@ GEM warden (~> 1.2.3) devise-async (0.9.0) devise (~> 3.2) - devise-two-factor (1.0.2) - activemodel + devise-two-factor (2.0.0) activesupport attr_encrypted (~> 1.3.2) devise (~> 3.5.0) railties - rotp (< 2) + rotp (~> 2) diff-lcs (1.2.5) diffy (3.0.7) docile (1.1.5) @@ -566,7 +565,7 @@ GEM mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rinku (1.7.3) - rotp (1.6.1) + rotp (2.1.1) rouge (1.10.1) rqrcode (0.7.0) chunky_png @@ -811,7 +810,7 @@ DEPENDENCIES default_value_for (~> 3.0.0) devise (~> 3.5.2) devise-async (~> 0.9.0) - devise-two-factor (~> 1.0.1) + devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) doorkeeper (~> 2.1.3) dropzonejs-rails (~> 0.7.1) diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index f9af0871cf1..e6b99be37fb 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -9,7 +9,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController end def create - if current_user.valid_otp?(params[:pin_code]) + if current_user.validate_and_consume_otp!(params[:pin_code]) current_user.two_factor_enabled = true @codes = current_user.generate_otp_backup_codes! current_user.save! diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index cfa565cd03e..1b60d3e27d0 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -99,7 +99,7 @@ class SessionsController < Devise::SessionsController end def valid_otp_attempt?(user) - user.valid_otp?(user_params[:otp_attempt]) || + user.validate_and_consume_otp!(user_params[:otp_attempt]) || user.invalidate_otp_backup_code!(user_params[:otp_attempt]) end diff --git a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb new file mode 100644 index 00000000000..c8438b3f6aa --- /dev/null +++ b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb @@ -0,0 +1,5 @@ +class AddConsumedTimestepToUsers < ActiveRecord::Migration + def change + add_column :users, :consumed_timestep, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index d70c4b58e93..b8eb9d26779 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: 20150918084513) do +ActiveRecord::Schema.define(version: 20150920010715) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -749,6 +749,7 @@ ActiveRecord::Schema.define(version: 20150918084513) do t.string "public_email", default: "", null: false t.integer "dashboard", default: 0 t.integer "project_view", default: 0 + t.integer "consumed_timestep" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb index f54706e3aa3..4fb1473c2d2 100644 --- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb +++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb @@ -37,7 +37,7 @@ describe Profiles::TwoFactorAuthsController do context 'with valid pin' do before do - expect(user).to receive(:valid_otp?).with(pin).and_return(true) + expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true) end it 'sets two_factor_enabled' do @@ -63,7 +63,7 @@ describe Profiles::TwoFactorAuthsController do context 'with invalid pin' do before do - expect(user).to receive(:valid_otp?).with(pin).and_return(false) + expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(false) end it 'assigns error' do -- cgit v1.2.1 From 57b009829d3139b2c42d516dc0645fe6bde0294c Mon Sep 17 00:00:00 2001 From: karen Carias Date: Sat, 19 Sep 2015 19:28:18 -0700 Subject: note about EE git hooks --- doc/hooks/custom_hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md index f7d4f3de68b..dd3be70c900 100644 --- a/doc/hooks/custom_hooks.md +++ b/doc/hooks/custom_hooks.md @@ -2,7 +2,7 @@ **Note: Custom git hooks must be configured on the filesystem of the GitLab server. Only GitLab server administrators will be able to complete these tasks. -Please explore webhooks as an option if you do not have filesystem access.** +Please explore webhooks as an option if you do not have filesystem access. For a user-friendly Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** Git natively supports hooks that are executed on different actions. Examples of server-side git hooks include pre-receive, post-receive, and update. -- cgit v1.2.1 From 01c0d14e41627a3977121b4d3a2bd466687887da Mon Sep 17 00:00:00 2001 From: Sameer Naik Date: Sun, 20 Sep 2015 11:25:09 +0530 Subject: fixes link to reply_by_email docs --- config/gitlab.yml.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index eada70faebc..69cdf497a84 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -96,7 +96,7 @@ production: &base ## Reply by email # Allow users to comment on issues and merge requests by replying to notification emails. - # For documentation on how to set this up, see http://doc.gitlab.com/ce/reply_by_email/README.md + # For documentation on how to set this up, see http://doc.gitlab.com/ce/reply_by_email/README.html reply_by_email: enabled: false address: "replies+%{reply_key}@gitlab.example.com" -- cgit v1.2.1 From 3377808193e8571b028fd05f009a7d1089dcc916 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Sep 2015 18:18:39 +0200 Subject: Fix reply by email for comments on a specific line in a diff/commit. --- app/mailers/emails/notes.rb | 6 +++--- app/models/sent_notification.rb | 14 ++++++++++++-- .../20150920161119_add_line_code_to_sent_notification.rb | 5 +++++ db/schema.rb | 3 ++- lib/gitlab/email/receiver.rb | 3 ++- 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20150920161119_add_line_code_to_sent_notification.rb diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 63d4aca61af..87ba94a583d 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -12,7 +12,7 @@ module Emails to: recipient(recipient_id), subject: subject("#{@commit.title} (#{@commit.short_id})")) - SentNotification.record(@commit, recipient_id, reply_key) + SentNotification.record_note(@note, recipient_id, reply_key) end def note_issue_email(recipient_id, note_id) @@ -27,7 +27,7 @@ module Emails to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) - SentNotification.record(@issue, recipient_id, reply_key) + SentNotification.record_note(@note, recipient_id, reply_key) end def note_merge_request_email(recipient_id, note_id) @@ -43,7 +43,7 @@ module Emails to: recipient(recipient_id), subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) - SentNotification.record(@merge_request, recipient_id, reply_key) + SentNotification.record_note(@note, recipient_id, reply_key) end end end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 33b113a2a27..03425389dd3 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -8,6 +8,7 @@ # noteable_type :string(255) # recipient_id :integer # commit_id :string(255) +# line_code :string(255) # reply_key :string(255) not null # @@ -21,13 +22,14 @@ class SentNotification < ActiveRecord::Base validates :noteable_id, presence: true, unless: :for_commit? validates :commit_id, presence: true, if: :for_commit? + validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true class << self def for(reply_key) find_by(reply_key: reply_key) end - def record(noteable, recipient_id, reply_key) + def record(noteable, recipient_id, reply_key, params = {}) return unless reply_key noteable_id = nil @@ -38,7 +40,7 @@ class SentNotification < ActiveRecord::Base noteable_id = noteable.id end - create( + params.reverse_merge!( project: noteable.project, noteable_type: noteable.class.name, noteable_id: noteable_id, @@ -46,6 +48,14 @@ class SentNotification < ActiveRecord::Base recipient_id: recipient_id, reply_key: reply_key ) + + create(params) + end + + def record_note(note, recipient_id, reply_key, params = {}) + params[:line_code] = note.line_code + + record(note.noteable, recipient_id, reply_key, params) end end diff --git a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb new file mode 100644 index 00000000000..d9af4e71751 --- /dev/null +++ b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb @@ -0,0 +1,5 @@ +class AddLineCodeToSentNotification < ActiveRecord::Migration + def change + add_column :sent_notifications, :line_code, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index b8eb9d26779..01ccda7a75e 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: 20150920010715) do +ActiveRecord::Schema.define(version: 20150920161119) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -623,6 +623,7 @@ ActiveRecord::Schema.define(version: 20150920010715) do t.integer "recipient_id" t.string "commit_id" t.string "reply_key", null: false + t.string "line_code" end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index 355fbd27898..341b557858f 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -98,7 +98,8 @@ module Gitlab note: reply, noteable_type: sent_notification.noteable_type, noteable_id: sent_notification.noteable_id, - commit_id: sent_notification.commit_id + commit_id: sent_notification.commit_id, + line_code: sent_notification.line_code ).execute end end -- cgit v1.2.1 From 3b39b648d2f0545f9d3dbd1680f8e9e977893792 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Sep 2015 11:05:27 -0700 Subject: Fix Markdown links not showing up in dashboard activity feed Closes #2586 --- CHANGELOG | 1 + app/helpers/gitlab_markdown_helper.rb | 4 ++-- spec/helpers/gitlab_markdown_helper_spec.rb | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a1b17e58156..cb3794f7cb6 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 Markdown links not showing up in dashboard activity feed (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - 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) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 1ebfd92f119..78bf25f55e7 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -45,7 +45,7 @@ module GitlabMarkdownHelper end def markdown(text, context = {}) - context.merge!( + context.reverse_merge!( current_user: current_user, path: @path, project: @project, @@ -59,7 +59,7 @@ module GitlabMarkdownHelper # TODO (rspeicher): Remove all usages of this helper and just call `markdown` # with a custom pipeline depending on the content being rendered def gfm(text, options = {}) - options.merge!( + options.reverse_merge!( current_user: current_user, path: @path, project: @project, diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 5639b3db913..b8101ae77ec 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -38,6 +38,17 @@ describe GitlabMarkdownHelper do expect(markdown(actual)).to match(expected) end end + + describe "override default project" do + let(:actual) { issue.to_reference } + let(:second_project) { create(:project) } + let(:second_issue) { create(:issue, project: second_project) } + + it 'should link to the issue' do + expected = namespace_project_issue_path(second_project.namespace, second_project, second_issue) + expect(markdown(actual, project: second_project)).to match(expected) + end + end end describe '#link_to_gfm' do -- cgit v1.2.1 From 47e926bec0a9da28daaca7dc92415b25f2301daf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 20 Sep 2015 18:28:14 +0200 Subject: Minor code cleanup --- app/controllers/projects_controller.rb | 5 ++-- app/helpers/notifications_helper.rb | 15 +++++------- .../projects/buttons/_notifications.html.haml | 27 +++++++++++----------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 9aed6a19e54..213c2a7173b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -86,9 +86,10 @@ class ProjectsController < ApplicationController if @project.empty_repo? render 'projects/empty' else - unless current_user.nil? - @membership = @project.project_members.find_by_user_id(current_user.id) + if current_user + @membership = @project.project_member_by_id(current_user.id) end + render :show end else diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 4fd06bebc2a..cf11f8e5320 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -16,31 +16,31 @@ module NotificationsHelper def notification_list_item(notification_level, user_membership) case notification_level when Notification::N_DISABLED - content_tag(:li, class: active_level_for(user_membership, 'disabled?')) do + content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do icon('microphone-slash fw', text: 'Disabled') end end when Notification::N_PARTICIPATING - content_tag(:li, class: active_level_for(user_membership, 'participating?')) do + content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do icon('volume-up fw', text: 'Participate') end end when Notification::N_WATCH - content_tag(:li, class: active_level_for(user_membership, 'watch?')) do + content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do icon('eye fw', text: 'Watch') end end when Notification::N_MENTION - content_tag(:li, class: active_level_for(user_membership, 'mention?')) do + content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do icon('at fw', text: 'On mention') end end when Notification::N_GLOBAL - content_tag(:li, class: active_level_for(user_membership, 'global?')) do + content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do icon('globe fw', text: 'Global') end @@ -55,9 +55,6 @@ module NotificationsHelper end def active_level_for(user_membership, level) - value = Notification.new(user_membership) - if value.send(level) - 'active' - end + 'active' if user_membership.notification_level == level end end diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 9bb46157229..57f764178d5 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,13 +1,14 @@ -- if current_user and !@membership.nil? - = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do - = hidden_field_tag :notification_type, 'project' - = hidden_field_tag :notification_id, @membership.id - = hidden_field_tag :notification_level - %span.dropdown - %a.dropdown-toggle.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} - = icon('bell') - = notification_label(@membership) - = icon('angle-down') - %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - - Notification.project_notification_levels.each do |level| - = notification_list_item(level, @membership) \ No newline at end of file +- return unless @membership + += form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do + = hidden_field_tag :notification_type, 'project' + = hidden_field_tag :notification_id, @membership.id + = hidden_field_tag :notification_level + %span.dropdown + %a.dropdown-toggle.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} + = icon('bell') + = notification_label(@membership) + = icon('angle-down') + %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown + - Notification.project_notification_levels.each do |level| + = notification_list_item(level, @membership) -- cgit v1.2.1 From 71a5d5a036a34a9b2cd257e6e6a105095ec89241 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 18 Sep 2015 17:28:09 +0200 Subject: Fail builds if no .gitlab-ci.yml is found --- CHANGELOG | 1 + app/models/ci/commit.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index a1b17e58156..4733b28e50d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -42,6 +42,7 @@ v 8.0.0 (unreleased) - Retrieving oauth token with LDAP credentials - Load Application settings from running database unless env var USE_DB=false - Added Drone CI integration (Kirill Zaitsev) + - Fail builds if no .gitlab-ci.yml is found - Refactored service API and added automatically service docs generator (Kirill Zaitsev) - Added web_url key project hook_attrs (Kirill Zaitsev) - Add ability to get user information by ID of an SSH key via the API diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 23cd47dfe37..f102d0a7679 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -236,7 +236,7 @@ module Ci end def config_processor - @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file] || project.generated_yaml_config) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file]) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 38d9943765a..981caaa51e1 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -50,6 +50,19 @@ module Ci end end + it 'fails commits without .gitlab-ci.yml' do + 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 + expect(result.builds.any?).to be_falsey + expect(result.status).to eq('failed') + 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]" }] -- cgit v1.2.1 From 8671343b65810b983d2c8ea37b3123abd620c093 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 18 Sep 2015 17:51:56 +0200 Subject: Rubocop fix --- spec/services/ci/create_commit_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 981caaa51e1..84ab0a615dd 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -56,7 +56,7 @@ module Ci before: '00000000', after: '31das312', ci_yaml_file: config, - commits: [ { message: 'Message'} ] + commits: [ { message: 'Message' } ] ) expect(result).to be_persisted expect(result.builds.any?).to be_falsey -- cgit v1.2.1 From 45a105b127eb5ee4f9ebc99f3cf46d582de3601c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 20 Sep 2015 22:12:45 +0200 Subject: Use pure SQL queries to migrate CI tags --- lib/ci/migrate/tags.rb | 58 ++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb index 125a535e9a9..2bbd253f8de 100644 --- a/lib/ci/migrate/tags.rb +++ b/lib/ci/migrate/tags.rb @@ -4,45 +4,37 @@ 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 '' + ActiveRecord::Base.transaction do + puts 'Inserting tags...' + connection.execute( + 'INSERT INTO tags (name) ' + + 'SELECT ci_tags.name FROM ci_tags ' + + 'WHERE (SELECT COUNT(*) FROM tags WHERE tags.name = ci_tags.name)=0' + ) + + puts 'Deleting old records' + connection.execute "DELETE FROM taggings WHERE context = 'tags' AND taggable_type LIKE 'Ci::%'" + + puts 'Inserting tags...' + connection.execute( + 'INSERT INTO taggings (taggable_type, taggable_id, tag_id, context) ' + + "SELECT CONCAT('Ci::', ci_taggings.taggable_type), ci_taggings.taggable_id, tags.id, 'tags' FROM ci_taggings " + + 'JOIN ci_tags ON ci_tags.id = ci_taggings.tag_id ' + + 'JOIN tags ON tags.name = ci_tags.name ' + ) - 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 + puts 'Resetting counters... ' + connection.execute( + 'UPDATE tags SET ' + + 'taggings_count = (SELECT COUNT(*) FROM taggings WHERE tags.id = taggings.tag_id)' + ) 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'] } + def connection + ActiveRecord::Base.connection end end end -- cgit v1.2.1 From 265ad515c697b9d50c4fd3e4bb360689a369a190 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 20 Sep 2015 23:20:51 +0200 Subject: Create CI migration task on GitLab side --- lib/ci/migrate/builds.rb | 29 +++++++++++++++++++ lib/ci/migrate/database.rb | 42 +++++++++++++-------------- lib/ci/migrate/manager.rb | 72 ++++++++++++++++++++++++++++++++++++++++++++++ lib/tasks/ci/migrate.rake | 49 +++++++++++++++++++------------ 4 files changed, 152 insertions(+), 40 deletions(-) create mode 100644 lib/ci/migrate/builds.rb create mode 100644 lib/ci/migrate/manager.rb diff --git a/lib/ci/migrate/builds.rb b/lib/ci/migrate/builds.rb new file mode 100644 index 00000000000..fdc143cfad5 --- /dev/null +++ b/lib/ci/migrate/builds.rb @@ -0,0 +1,29 @@ +module Ci + module Migrate + class Builds + attr_reader :app_builds_dir, :backup_builds_tarball, :backup_dir + + def initialize + @app_builds_dir = Settings.gitlab_ci.builds_path + @backup_dir = Gitlab.config.backup.path + @backup_builds_tarball = File.join(backup_dir, 'builds/builds.tar.gz') + end + + def restore + backup_existing_builds_dir + + FileUtils.mkdir_p(app_builds_dir, mode: 0700) + unless system('tar', '-C', app_builds_dir, '-zxvf', backup_builds_tarball) + abort 'Restore failed'.red + end + 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/migrate/database.rb b/lib/ci/migrate/database.rb index 74f592dcaea..bf9b80f1f62 100644 --- a/lib/ci/migrate/database.rb +++ b/lib/ci/migrate/database.rb @@ -9,32 +9,32 @@ module Ci @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 + def restore + decompress_rd, decompress_wr = IO.pipe + decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name) + decompress_wr.close - 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 + restore_pid = case config["adapter"] + when /^mysql/ then + $progress.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"] + spawn('mysql', *mysql_args, config['database'], in: decompress_rd) + when "postgresql" then + $progress.print "Restoring PostgreSQL database #{config['database']} ... " + pg_env + spawn('psql', config['database'], in: decompress_rd) + end + decompress_rd.close + + success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? } + abort 'Restore failed' unless success 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 + def db_file_name + File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz') end def mysql_args diff --git a/lib/ci/migrate/manager.rb b/lib/ci/migrate/manager.rb new file mode 100644 index 00000000000..9405397031c --- /dev/null +++ b/lib/ci/migrate/manager.rb @@ -0,0 +1,72 @@ +module Ci + module Migrate + class Manager + VERSION = '8.0.0.pre' + + def cleanup + $progress.print "Deleting tmp directories ... " + + backup_contents.each do |dir| + next unless File.exist?(File.join(Gitlab.config.backup.path, dir)) + + if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir)) + $progress.puts "done".green + else + puts "deleting tmp directory '#{dir}' failed".red + abort 'Backup failed' + end + end + end + + def unpack + Dir.chdir(Gitlab.config.backup.path) + + # check for existing backups in the backup dir + file_list = Dir.glob("*_gitlab_ci_backup.tar").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") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar") + + unless File.exists?(tar_file) + puts "The specified CI backup doesn't exist!" + exit 1 + end + + $progress.print "Unpacking backup ... " + + unless Kernel.system(*%W(tar -xf #{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] != VERSION + puts "GitLab CI version mismatch:".red + puts " Your current GitLab CI version (#{VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red + exit 1 + end + 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/migrate.rake b/lib/tasks/ci/migrate.rake index e7d41874a11..3507871fcb0 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -1,40 +1,49 @@ namespace :ci do desc 'GitLab | Import and migrate CI database' task migrate: :environment do + warn_user_is_not_gitlab + configure_cron_mode + 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." + puts 'This will remove all CI related data and restore it from the provided backup.' ask_to_continue - puts "" + puts '' end - Rake::Task["ci:migrate:db"].invoke - Rake::Task["ci:migrate:autoincrements"].invoke - Rake::Task["ci:migrate:tags"].invoke - Rake::Task["ci:migrate:services"].invoke + migrate = Ci::Migrate::Manager.new + migrate.unpack + + Rake::Task['ci:migrate:db'].invoke + Rake::Task['ci:migrate:builds'].invoke + Rake::Task['ci:migrate:tags'].invoke + Rake::Task['ci:migrate:services'].invoke + + migrate.cleanup 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 + configure_cron_mode + $progress.puts 'Restoring database ... '.blue + Ci::Migrate::Database.new.restore + $progress.puts 'done'.green + end - ::Ci::Migrate::Database.new.restore(ci_dump) + desc 'GitLab | Import CI builds' + task builds: :environment do + configure_cron_mode + $progress.puts 'Restoring builds ... '.blue + Ci::Migrate::Builds.new.restore + $progress.puts 'done'.green end desc 'GitLab | Migrate CI tags' task tags: :environment do + configure_cron_mode + $progress.puts 'Migrating tags ... '.blue ::Ci::Migrate::Tags.new.restore + $progress.puts 'done'.green end desc 'GitLab | Migrate CI auto-increments' @@ -56,8 +65,10 @@ namespace :ci do desc 'GitLab | Migrate CI services' task services: :environment do + $progress.puts 'Migrating services ... '.blue c = ActiveRecord::Base.connection c.execute("UPDATE ci_services SET type=CONCAT('Ci::', type) WHERE type NOT LIKE 'Ci::%'") + $progress.puts 'done'.green end end end -- cgit v1.2.1 From fc8ae32bcefdeb3fb629d932ba1ac5cc2f31f38d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 20 Sep 2015 23:45:35 +0200 Subject: Update migration guide --- doc/migrate_ci_to_ce/README.md | 152 ++++++----------------------------------- 1 file changed, 22 insertions(+), 130 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 34b70c7140c..ac6bb630b08 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -9,14 +9,14 @@ into your GitLab CE or EE installation. ### Before we begin -**You need to have a working installation of GitLab CI version 7.14 to perform +**You need to have a working installation of GitLab CI version 8.0 to perform this migration. The older versions are not supported and will most likely break this migration procedure.** This migration cannot be performed online and takes a significant amount of time. Make sure to plan ahead. -If you are running a version of GitLab CI prior to 7.14 please follow the +If you are running a version of GitLab CI prior to 8.0 please follow the appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/update/). The migration is divided into three parts: @@ -42,123 +42,29 @@ cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production ``` -#### 3. Rename database tables +If your GitLab CI installation uses **MySQL** and your GitLab CE uses **PostgreSQL** +you need to convert database data with **MYSQL_TO_POSTGRESQL**. -To prevent naming conflicts with database tables in GitLab CE or EE, we need to -rename CI's tables to begin with a `ci_` prefix: - -```sh -cat < PostgreSQL) execute: - **NOTE:** For any of the commands below, you'll need to substitute the - values `IN_UPPERCASE` with the corresponding values from your **CI - installation's** `config/database.yml` files above. - - 1. If both your CI and CE (or EE) installations use **mysql2** as the `adapter`, use - `mysqldump`: - - ```sh - mysqldump --default-character-set=utf8 --complete-insert --no-create-info \ - --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 - ``` - - 1. If both your CI and CE (or EE) installations use **postgresql** as the - `adapter`, use `pg_dump`: - - ```sh - pg_dump -h DB_HOSTNAME -U DB_USERNAME -p DB_PORT \ - --data-only GITLAB_CI_DATABASE -t "ci_*" > gitlab_ci.sql - ``` - - 1. If your CI installation uses **mysql2** as the `adapter` and your CE (or - EE) installation uses **postgresql**, use `mysqldump` to dump the - database and then convert it to PostgreSQL using [mysql-postgresql-converter]: - - ```sh - # 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 +```bash +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 +``` - # Filter to only include INSERT statements - grep "^\(START\|SET\|INSERT\|COMMIT\)" gitlab_ci.sql.tmp2 > gitlab_ci.sql - ``` +#### 3. Remove cronjob -[mysql-postgresql-converter]: https://github.com/gitlabhq/mysql-postgresql-converter +``` +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec whenever --clear-crontab +``` ### Part II: GitLab CE (or EE) @@ -203,33 +109,19 @@ git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/g The new options include configuration settings for GitLab CI. -#### 6. Copy build logs +#### 6. Copy backup from GitLab CI -You need to copy the contents of GitLab CI's `builds/` directory to the -corresponding directory in GitLab CE or EE: + sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups + sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar - sudo rsync -av /home/gitlab_ci/gitlab-ci/builds /home/git/gitlab/builds - sudo chown -R git:git /home/git/gitlab/builds - -The build logs are usually quite big so it may take a significant amount of -time. - -#### 7. Import GitLab CI database +#### 7. Import GitLab CI backup Now you'll import the GitLab CI database dump that you [created earlier](#5-create-a-database-dump) into the GitLab CE or EE 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 task will: - -1. Delete data from all existing CI tables -1. Import data from database dump -1. Fix database auto-increments -1. Fix tags assigned to Builds and Runners -1. Fix services used by CI + sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production + +This task will take some time. #### 8. Start GitLab -- cgit v1.2.1 From 500e277a8326b345854a5fc3114c1163826c1b4a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 00:13:58 +0200 Subject: Change notices during migrating --- lib/ci/migrate/tags.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb index 2bbd253f8de..2e4872f0716 100644 --- a/lib/ci/migrate/tags.rb +++ b/lib/ci/migrate/tags.rb @@ -12,10 +12,10 @@ module Ci 'WHERE (SELECT COUNT(*) FROM tags WHERE tags.name = ci_tags.name)=0' ) - puts 'Deleting old records' + puts 'Deleting old taggings...' connection.execute "DELETE FROM taggings WHERE context = 'tags' AND taggable_type LIKE 'Ci::%'" - puts 'Inserting tags...' + puts 'Inserting taggings...' connection.execute( 'INSERT INTO taggings (taggable_type, taggable_id, tag_id, context) ' + "SELECT CONCAT('Ci::', ci_taggings.taggable_type), ci_taggings.taggable_id, tags.id, 'tags' FROM ci_taggings " + -- cgit v1.2.1 From 098c1982713f3a4075efd6f27ab6326c75fc20ea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 00:14:07 +0200 Subject: Disable CI for time of migration --- lib/tasks/ci/migrate.rake | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake index 3507871fcb0..1de664c85e1 100644 --- a/lib/tasks/ci/migrate.rake +++ b/lib/tasks/ci/migrate.rake @@ -10,6 +10,10 @@ namespace :ci do puts '' end + # disable CI for time of migration + enable_ci(false) + + # unpack archives migrate = Ci::Migrate::Manager.new migrate.unpack @@ -18,6 +22,9 @@ namespace :ci do Rake::Task['ci:migrate:tags'].invoke Rake::Task['ci:migrate:services'].invoke + # enable CI for time of migration + enable_ci(true) + migrate.cleanup end @@ -71,4 +78,10 @@ namespace :ci do $progress.puts 'done'.green end end + + def enable_ci(enabled) + settings = ApplicationSetting.current || ApplicationSetting.create_from_defaults + settings.ci_enabled = enabled + settings.save! + end end -- cgit v1.2.1 From b2a90216858160840fdfbe7a221c317a6c073f22 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 00:16:51 +0200 Subject: Change wording --- app/views/admin/application_settings/_form.html.haml | 2 +- app/views/ci/projects/disabled.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 1476e29524c..143cd10c543 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -131,7 +131,7 @@ .checkbox = f.label :ci_enabled do = f.check_box :ci_enabled - Enable Continuous Integration + Disable to prevent CI usage until rake ci:migrate is run (8.0 only) .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/ci/projects/disabled.html.haml b/app/views/ci/projects/disabled.html.haml index 95276d894ed..83b0d8329e1 100644 --- a/app/views/ci/projects/disabled.html.haml +++ b/app/views/ci/projects/disabled.html.haml @@ -1 +1 @@ -Continuous Integration has been disabled. Please ask your administrator to enable it. +Continuous Integration has been disabled for time of the migration. -- cgit v1.2.1 From d511edc952574d0e5f51180648cbfbd2061bfe47 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 20 Sep 2015 18:55:47 -0400 Subject: More migration guide updates Fixes a few links and typos, and reorganizes the CI "Create a backup" section. --- doc/migrate_ci_to_ce/README.md | 61 +++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index ac6bb630b08..8525fb3847d 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -17,7 +17,7 @@ This migration cannot be performed online and takes a significant amount of time. Make sure to plan ahead. If you are running a version of GitLab CI prior to 8.0 please follow the -appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/update/). +appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update/). The migration is divided into three parts: @@ -35,29 +35,36 @@ The migration is divided into three parts: The migration procedure modifies the structure of the CI database. If something goes wrong, you will not be able to revert to a previous version without a -backup: +backup. -```bash -cd /home/gitlab_ci/gitlab-ci -sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production -``` +If your GitLab CI installation uses **MySQL** and your GitLab CE (or EE) +installation uses **PostgreSQL** you'll need to convert the CI database by +setting a `MYSQL_TO_POSTGRESQL` flag. -If your GitLab CI installation uses **MySQL** and your GitLab CE uses **PostgreSQL** -you need to convert database data with **MYSQL_TO_POSTGRESQL**. +You can check which database each install is using by viewing their +database configuration files: -You can check that by looking into GitLab CI and GitLab CE (or EE) database configuration file: +```sh +cat /home/gitlab_ci/gitlab-ci/config/database.yml +cat /home/git/gitlab/config/database.yml +``` - ```sh - cat /home/gitlab_ci/gitlab-ci/config/database.yml - cat /home/git/gitlab/config/database.yml +- If both applications use the same database `adapter`, create the backup with + this command: + + ```bash + cd /home/gitlab_ci/gitlab-ci + sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production ``` -To create backup with database conversion (MySQL -> PostgreSQL) execute: +- If CI uses MySQL, and CE (or EE) uses PostgreSQL, create the backup with this + command (note the `MYSQL_TO_POSTGRESQL` flag): -```bash -cd /home/gitlab_ci/gitlab-ci -sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 -``` + + ```bash + cd /home/gitlab_ci/gitlab-ci + sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 + ``` #### 3. Remove cronjob @@ -103,9 +110,7 @@ same file in GitLab CE: There are new configuration options available for `gitlab.yml`. 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 -``` + git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example The new options include configuration settings for GitLab CI. @@ -116,16 +121,16 @@ The new options include configuration settings for GitLab CI. #### 7. Import GitLab CI backup -Now you'll import the GitLab CI database dump that you [created -earlier](#5-create-a-database-dump) into the GitLab CE or EE database: +Now you'll import the GitLab CI database dump that you created earlier into the +GitLab CE or EE database: sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production - + This task will take some time. #### 8. Start GitLab -You can start GitLab CI (or EE) now and see if everything is working: +You can start GitLab CE (or EE) now and see if everything is working: sudo service gitlab start @@ -191,7 +196,8 @@ Make sure you substitute these placeholder values with your real ones: 1. `YOUR_GITLAB_SERVER_FQDN`: The current public-facing address of your GitLab CE (or EE) install (e.g., `gitlab.com`). -**Make sure not to remove the `/ci$request_uri` part. This is required to properly forward the requests.** +**Make sure not to remove the `/ci$request_uri` part. This is required to +properly forward the requests.** You should also make sure that you can: @@ -209,8 +215,7 @@ You should also make sure that you can: #### 4. Done! If everything went well you should be able to access your migrated CI install by -visiting `https://gitlab.example.com/ci/`. - -If you visit the old GitLab CI address, you should be redirected to the new one. +visiting `https://gitlab.example.com/ci/`. If you visit the old GitLab CI +address, you should be redirected to the new one. **Enjoy!** -- cgit v1.2.1 From 2b94f5fb06ef061fb49a7dadcbb95c0954bc9860 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 20 Sep 2015 19:21:33 -0400 Subject: Allow RelativeLinkFilter to go up multiple directories --- lib/gitlab/markdown/relative_link_filter.rb | 3 +-- spec/lib/gitlab/markdown/relative_link_filter_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index d613c205509..6ee3d1ce039 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -91,8 +91,7 @@ module Gitlab parts = request_path.split('/') parts.pop if path_type(request_path) != 'tree' - # Allow for going up one directory - if parts.length > 1 && path.start_with?('../') + while parts.length > 1 && path.start_with?('../') parts.pop path.sub!('../', '') end diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb index ab9c2008ea7..027336ceb73 100644 --- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb +++ b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb @@ -85,6 +85,14 @@ module Gitlab::Markdown to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" end + it 'rebuilds relative URL for a file in the repo up multiple directories' do + relative_link = link('../../../api/README.md') + doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md') + + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" + end + it 'rebuilds relative URL for a file in the repo with an anchor' do doc = filter(link('README.md#section')) expect(doc.at_css('a')['href']). -- cgit v1.2.1 From a37e591e7414f51ca9076e966c724030225528db Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 10:07:53 +0200 Subject: Use INSERT INTO to insert tags --- lib/ci/migrate/tags.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb index 2e4872f0716..97e043ece27 100644 --- a/lib/ci/migrate/tags.rb +++ b/lib/ci/migrate/tags.rb @@ -4,14 +4,15 @@ module Ci module Migrate class Tags def restore - ActiveRecord::Base.transaction do - puts 'Inserting tags...' - connection.execute( - 'INSERT INTO tags (name) ' + - 'SELECT ci_tags.name FROM ci_tags ' + - 'WHERE (SELECT COUNT(*) FROM tags WHERE tags.name = ci_tags.name)=0' - ) + puts 'Inserting tags...' + connection.select_all('SELECT ci_tags.name FROM ci_tags').each do |tag| + begin + connection.execute("INSERT INTO tags (name) VALUES(#{ActiveRecord::Base::sanitize(tag['name'])})") + rescue ActiveRecord::RecordNotUnique + end + end + ActiveRecord::Base.transaction do puts 'Deleting old taggings...' connection.execute "DELETE FROM taggings WHERE context = 'tags' AND taggable_type LIKE 'Ci::%'" -- cgit v1.2.1 From ee0f1c00b1b07383a1322c7b86440d5f9dd62df1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 10:33:02 +0200 Subject: Use GitlabCi::VERSION when checking migration --- lib/ci/migrate/manager.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/ci/migrate/manager.rb b/lib/ci/migrate/manager.rb index 9405397031c..4205809368d 100644 --- a/lib/ci/migrate/manager.rb +++ b/lib/ci/migrate/manager.rb @@ -1,8 +1,6 @@ module Ci module Migrate class Manager - VERSION = '8.0.0.pre' - def cleanup $progress.print "Deleting tmp directories ... " @@ -50,9 +48,9 @@ module Ci ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 # restoring mismatching backups can lead to unexpected problems - if settings[:gitlab_version] != VERSION + if settings[:gitlab_version] != GitlabCi::VERSION puts "GitLab CI version mismatch:".red - puts " Your current GitLab CI version (#{VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red + puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red exit 1 end end -- cgit v1.2.1 From ee028d9d60522f8993a0b2429ac8a0631d59229a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 09:46:47 +0200 Subject: Rename reply_by_email to incoming_email to prepare for the future. --- app/mailers/notify.rb | 4 +- app/models/sent_notification.rb | 6 + app/views/admin/dashboard/index.html.haml | 2 +- app/workers/email_receiver_worker.rb | 2 +- config/gitlab.yml.example | 6 +- config/initializers/1_settings.rb | 4 +- doc/README.md | 2 +- doc/incoming_email/README.md | 203 +++++++++++++++++++ doc/incoming_email/postfix.md | 310 +++++++++++++++++++++++++++++ doc/reply_by_email/README.md | 203 ------------------- doc/reply_by_email/postfix.md | 310 ----------------------------- lib/gitlab/email/receiver.rb | 5 +- lib/gitlab/incoming_email.rb | 43 ++++ lib/gitlab/reply_by_email.rb | 49 ----- lib/tasks/gitlab/check.rake | 18 +- spec/lib/gitlab/email/receiver_spec.rb | 2 +- spec/lib/gitlab/incoming_email_spec.rb | 61 ++++++ spec/lib/gitlab/reply_by_email_spec.rb | 86 -------- spec/support/stub_configuration.rb | 4 +- spec/workers/email_receiver_worker_spec.rb | 4 +- 20 files changed, 650 insertions(+), 674 deletions(-) create mode 100644 doc/incoming_email/README.md create mode 100644 doc/incoming_email/postfix.md delete mode 100644 doc/reply_by_email/README.md delete mode 100644 doc/reply_by_email/postfix.md create mode 100644 lib/gitlab/incoming_email.rb delete mode 100644 lib/gitlab/reply_by_email.rb create mode 100644 spec/lib/gitlab/incoming_email_spec.rb delete mode 100644 spec/lib/gitlab/reply_by_email_spec.rb diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index f196ffd53f3..db2f9654e14 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -110,7 +110,7 @@ class Notify < BaseMailer if reply_key headers['X-GitLab-Reply-Key'] = reply_key - address = Mail::Address.new(Gitlab::ReplyByEmail.reply_address(reply_key)) + address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key)) address.display_name = @project.name_with_namespace headers['Reply-To'] = address @@ -150,6 +150,6 @@ class Notify < BaseMailer end def reply_key - @reply_key ||= Gitlab::ReplyByEmail.reply_key + @reply_key ||= SentNotification.reply_key end end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 33b113a2a27..2724af8e613 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -23,6 +23,12 @@ class SentNotification < ActiveRecord::Base validates :commit_id, presence: true, if: :for_commit? class << self + def reply_key + return nil unless Gitlab::IncomingEmail.enabled? + + SecureRandom.hex(16) + end + def for(reply_key) find_by(reply_key: reply_key) end diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 54191aadda6..8657d2c71fe 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -58,7 +58,7 @@ %p Reply by email %span.light.pull-right - = boolean_to_icon Gitlab::ReplyByEmail.enabled? + = boolean_to_icon Gitlab::IncomingEmail.enabled? .col-md-4 %h4 Components diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 8cfb96ef376..5a921a73fe9 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -4,7 +4,7 @@ class EmailReceiverWorker sidekiq_options queue: :incoming_email def perform(raw) - return unless Gitlab::ReplyByEmail.enabled? + return unless Gitlab::IncomingEmail.enabled? begin Gitlab::Email::Receiver.new(raw).execute diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 69cdf497a84..7304dd6f6b2 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -96,10 +96,10 @@ production: &base ## Reply by email # Allow users to comment on issues and merge requests by replying to notification emails. - # For documentation on how to set this up, see http://doc.gitlab.com/ce/reply_by_email/README.html - reply_by_email: + # For documentation on how to set this up, see http://doc.gitlab.com/ce/incoming_email/README.html + incoming_email: enabled: false - address: "replies+%{reply_key}@gitlab.example.com" + address: "incoming+%{key}@gitlab.example.com" ## Gravatar ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index ddc9bbf5dfd..48601b67335 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -187,8 +187,8 @@ Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci[ # # Reply by email # -Settings['reply_by_email'] ||= Settingslogic.new({}) -Settings.reply_by_email['enabled'] = false if Settings.reply_by_email['enabled'].nil? +Settings['incoming_email'] ||= Settingslogic.new({}) +Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled'].nil? # # Gravatar diff --git a/doc/README.md b/doc/README.md index f5f1f56b1e2..5f38086b8e3 100644 --- a/doc/README.md +++ b/doc/README.md @@ -46,7 +46,7 @@ - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [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. +- [Reply by email](incoming_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/incoming_email/README.md b/doc/incoming_email/README.md new file mode 100644 index 00000000000..2a94bc07f2e --- /dev/null +++ b/doc/incoming_email/README.md @@ -0,0 +1,203 @@ +# Reply by email + +GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails. + +## Get a mailbox + +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. + +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 + +In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. + +### Omnibus package installations + +1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` and fill in the details for your specific IMAP server and email account: + + ```ruby + gitlab_rails['incoming_email_enabled'] = true + gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com" + gitlab_rails['incoming_email_host'] = "imap.gmail.com" # IMAP server host + gitlab_rails['incoming_email_port'] = 993 # IMAP server port + gitlab_rails['incoming_email_ssl'] = true # Whether the IMAP server uses SSL + gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com" # Email account username. Usually the full email address. + gitlab_rails['incoming_email_password'] = "password" # Email account password + gitlab_rails['incoming_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-incoming@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:incoming_email:check + ``` + +1. Reply by email should now be working. + +### Installations from source + +1. Go to the GitLab installation directory: + + ```sh + cd /home/git/gitlab + ``` + +1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key`: + + ```sh + sudo editor config/gitlab.yml + ``` + + ```yaml + incoming_email: + enabled: true + address: "gitlab-incoming+%{key}@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-incoming@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 + ``` + +3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account: + + ```sh + sudo editor config/mail_room.yml + ``` + + ```yaml + :mailboxes: + - + # IMAP server host + :host: "imap.gmail.com" + # IMAP server port + :port: 993 + # Whether the IMAP server uses SSL + :ssl: true + # Email account username. Usually the full email address. + :email: "gitlab-incoming@gmail.com" + # Email account password + :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". + :name: "inbox" + # Always "sidekiq". + :delivery_method: sidekiq + # Always true. + :delete_after_delivery: true + :delivery_options: + # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "resque:gitlab". + :namespace: resque:gitlab + # Always "incoming_email". + :queue: incoming_email + # Always "EmailReceiverWorker" + :worker: EmailReceiverWorker + ``` + +5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`: + + ```sh + sudo mkdir -p /etc/default + echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab + ``` + +6. Restart GitLab: + + ```sh + sudo service gitlab restart + ``` + +7. Verify that everything is configured correctly: + + ```sh + sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production + ``` + +8. Reply by email should now be working. + +### Development + +1. Go to the GitLab installation directory. + +1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key`: + + ```yaml + incoming_email: + enabled: true + address: "gitlab-incoming+%{key}@gmail.com" + ``` + + As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@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 + ``` + +3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account: + + ```yaml + :mailboxes: + - + # IMAP server host + :host: "imap.gmail.com" + # IMAP server port + :port: 993 + # Whether the IMAP server uses SSL + :ssl: true + # Email account username. Usually the full email address. + :email: "gitlab-incoming@gmail.com" + # Email account password + :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". + :name: "inbox" + # Always "sidekiq". + :delivery_method: sidekiq + # Always true. + :delete_after_delivery: true + :delivery_options: + # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "resque:gitlab". + :namespace: resque:gitlab + # Always "incoming_email". + :queue: incoming_email + # Always "EmailReceiverWorker" + :worker: EmailReceiverWorker + ``` + +4. Uncomment the `mail_room` line in your `Procfile`: + + ```yaml + mail_room: bundle exec mail_room -q -c config/mail_room.yml + ``` + +6. Restart GitLab: + + ```sh + bundle exec foreman start + ``` + +7. Verify that everything is configured correctly: + + ```sh + bundle exec rake gitlab:incoming_email:check RAILS_ENV=development + ``` + +8. Reply by email should now be working. diff --git a/doc/incoming_email/postfix.md b/doc/incoming_email/postfix.md new file mode 100644 index 00000000000..09c5804cb77 --- /dev/null +++ b/doc/incoming_email/postfix.md @@ -0,0 +1,310 @@ +# 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 `incoming@gitlab.example.com`, that is, username `incoming` on host `gitlab.example.com`. Don't forget to change it to your actual host when executing the example code snippets. + +## Configure your server firewall + +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 + +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 incoming. + + ```sh + sudo useradd -m -s /bin/bash incoming + ``` + +1. Set a password for this user. + + ```sh + sudo passwd incoming + ``` + + 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, verify that `postfix` is running: + + ```sh + sudo postfix status + ``` + + If it is not, start it: + + ```sh + sudo postfix start + ``` + +1. Send the new `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt: + + ``` + ehlo localhost + mail from: root@localhost + rcpt to: incoming@localhost + data + Subject: Re: Some issue + + Sounds good! + . + quit + ``` + + (Note: The `.` is a literal period on its own line) + +1. Check if the `incoming` user received the email: + + ```sh + su - incoming + mail + ``` + + You should see output like this: + + ``` + "/var/mail/incoming": 1 message 1 unread + >U 1 root@localhost 59/2842 Re: Some issue + ``` + + Quit the mail app: + + ```sh + q + ``` + +1. Log out of the `incoming` 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, requires mailboxes to have the Maildir format, rather than mbox. + +1. Configure Postfix to use Maildir-style mailboxes: + + ```sh + sudo postconf -e "home_mailbox = Maildir/" + ``` + +1. Restart Postfix: + + ```sh + sudo /etc/init.d/postfix restart + ``` + +1. Test the new 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 `incoming` user received the email: + + ```sh + su - incoming + MAIL=/home/incoming/Maildir + mail + ``` + + You should see output like this: + + ``` + "/home/incoming/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 `incoming` 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 = gitlab.example.com, localhost.localdomain, localhost" + ``` + +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 "inet_interfaces = all" + ``` + +1. Configure Postfix to use the `+` delimiter for sub-addressing: + + ```sh + sudo postconf -e "recipient_delimiter = +" + ``` + +1. Restart Postfix: + + ```sh + sudo service postfix restart + ``` + +## Test the final setup + +1. Test SMTP under the new setup: + + 1. Connect to the SMTP server: + + ```sh + telnet gitlab.example.com 25 + ``` + + You should see a prompt like this: + + ```sh + Trying 123.123.123.123... + Connected to 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 `incoming` 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: incoming@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 `incoming` user received the email: + + ```sh + su - incoming + MAIL=/home/incoming/Maildir + mail + ``` + + You should see output like this: + + ``` + "/home/incoming/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 `incoming` account and go back to being `root`: + + ```sh + logout + ``` + +1. Test IMAP under the new setup: + + 1. Connect to the IMAP server: + + ```sh + telnet 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 `incoming` user to test IMAP, by entering the following into the IMAP prompt: + + ``` + a login incoming PASSWORD + ``` + + Replace PASSWORD with the password you set on the `incoming` 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 were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) guide to configure GitLab. + +--------- + +_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._ diff --git a/doc/reply_by_email/README.md b/doc/reply_by_email/README.md deleted file mode 100644 index e9187298d79..00000000000 --- a/doc/reply_by_email/README.md +++ /dev/null @@ -1,203 +0,0 @@ -# Reply by email - -GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails. - -## Get a mailbox - -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. - -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 - -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: - - ```sh - cd /home/git/gitlab - ``` - -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`: - - ```sh - sudo editor config/gitlab.yml - ``` - - ```yaml - reply_by_email: - enabled: true - address: "gitlab-replies+%{reply_key}@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`: - - ```sh - sudo cp config/mail_room.yml.example config/mail_room.yml - ``` - -3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account: - - ```sh - sudo editor config/mail_room.yml - ``` - - ```yaml - :mailboxes: - - - # IMAP server host - :host: "imap.gmail.com" - # IMAP server port - :port: 993 - # Whether the IMAP server uses SSL - :ssl: true - # Email account username. Usually the full email address. - :email: "gitlab-replies@gmail.com" - # Email account password - :password: "[REDACTED]" - # The name of the mailbox where incoming mail will end up. Usually "inbox". - :name: "inbox" - # Always "sidekiq". - :delivery_method: sidekiq - # Always true. - :delete_after_delivery: true - :delivery_options: - # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml. - :redis_url: redis://localhost:6379 - # Always "resque:gitlab". - :namespace: resque:gitlab - # Always "incoming_email". - :queue: incoming_email - # Always "EmailReceiverWorker" - :worker: EmailReceiverWorker - ``` - -5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`: - - ```sh - sudo mkdir -p /etc/default - echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab - ``` - -6. Restart GitLab: - - ```sh - sudo service gitlab restart - ``` - -7. Verify that everything is configured correctly: - - ```sh - sudo -u git -H bundle exec rake gitlab:reply_by_email:check RAILS_ENV=production - ``` - -8. Reply by email should now be working. - -### 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 - 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`. - -2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: - - ```sh - sudo cp config/mail_room.yml.example config/mail_room.yml - ``` - -3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account: - - ```yaml - :mailboxes: - - - # IMAP server host - :host: "imap.gmail.com" - # IMAP server port - :port: 993 - # Whether the IMAP server uses SSL - :ssl: true - # Email account username. Usually the full email address. - :email: "gitlab-replies@gmail.com" - # Email account password - :password: "[REDACTED]" - # The name of the mailbox where incoming mail will end up. Usually "inbox". - :name: "inbox" - # Always "sidekiq". - :delivery_method: sidekiq - # Always true. - :delete_after_delivery: true - :delivery_options: - # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml. - :redis_url: redis://localhost:6379 - # Always "resque:gitlab". - :namespace: resque:gitlab - # Always "incoming_email". - :queue: incoming_email - # Always "EmailReceiverWorker" - :worker: EmailReceiverWorker - ``` - -4. Uncomment the `mail_room` line in your `Procfile`: - - ```yaml - mail_room: bundle exec mail_room -q -c config/mail_room.yml - ``` - -6. Restart GitLab: - - ```sh - bundle exec foreman start - ``` - -7. Verify that everything is configured correctly: - - ```sh - bundle exec rake gitlab:reply_by_email:check RAILS_ENV=development - ``` - -8. Reply by email should now be working. diff --git a/doc/reply_by_email/postfix.md b/doc/reply_by_email/postfix.md deleted file mode 100644 index c0ac59bb922..00000000000 --- a/doc/reply_by_email/postfix.md +++ /dev/null @@ -1,310 +0,0 @@ -# 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 firewall - -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 - -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, verify that `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 - su - 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, requires mailboxes to have the Maildir format, rather than mbox. - -1. Configure Postfix to use Maildir-style mailboxes: - - ```sh - sudo postconf -e "home_mailbox = Maildir/" - ``` - -1. Restart Postfix: - - ```sh - sudo /etc/init.d/postfix restart - ``` - -1. Test the new 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 - su - 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 = gitlab.example.com, localhost.localdomain, localhost" - ``` - -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 "inet_interfaces = all" - ``` - -1. Configure Postfix to use the `+` delimiter for sub-addressing: - - ```sh - sudo postconf -e "recipient_delimiter = +" - ``` - -1. Restart Postfix: - - ```sh - sudo service postfix restart - ``` - -## Test the final setup - -1. Test SMTP under the new setup: - - 1. Connect to the SMTP server: - - ```sh - telnet gitlab.example.com 25 - ``` - - You should see a prompt like this: - - ```sh - Trying 123.123.123.123... - Connected to 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 - su - 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 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 with 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 were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) guide to configure GitLab. - ---------- - -_This document was adapted from https://help.ubuntu.com/community/PostfixBasicSetupHowto, by contributors to the Ubuntu documentation wiki._ \ No newline at end of file diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index 355fbd27898..2b252b32887 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -65,7 +65,7 @@ module Gitlab def reply_key reply_key = nil message.to.each do |address| - reply_key = Gitlab::ReplyByEmail.reply_key_from_address(address) + reply_key = Gitlab::IncomingEmail.key_from_address(address) break if reply_key end @@ -98,7 +98,8 @@ module Gitlab note: reply, noteable_type: sent_notification.noteable_type, noteable_id: sent_notification.noteable_id, - commit_id: sent_notification.commit_id + commit_id: sent_notification.commit_id, + line_code: sent_notification.line_code ).execute end end diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb new file mode 100644 index 00000000000..856ccc71084 --- /dev/null +++ b/lib/gitlab/incoming_email.rb @@ -0,0 +1,43 @@ +module Gitlab + module IncomingEmail + class << self + def enabled? + config.enabled && address_formatted_correctly? + end + + def address_formatted_correctly? + config.address && + config.address.include?("%{key}") + end + + def reply_address(key) + config.address.gsub('%{key}', key) + end + + def key_from_address(address) + regex = address_regex + return unless regex + + match = address.match(regex) + return unless match + + match[1] + end + + private + + def config + Gitlab.config.incoming_email + end + + def address_regex + wildcard_address = config.address + return nil unless wildcard_address + + regex = Regexp.escape(wildcard_address) + regex = regex.gsub(Regexp.escape('%{key}'), "(.+)") + Regexp.new(regex).freeze + end + end + end +end diff --git a/lib/gitlab/reply_by_email.rb b/lib/gitlab/reply_by_email.rb deleted file mode 100644 index c3fe6778f06..00000000000 --- a/lib/gitlab/reply_by_email.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Gitlab - module ReplyByEmail - class << self - def enabled? - config.enabled && address_formatted_correctly? - end - - def address_formatted_correctly? - config.address && - config.address.include?("%{reply_key}") - end - - def reply_key - return nil unless enabled? - - SecureRandom.hex(16) - end - - def reply_address(reply_key) - config.address.gsub('%{reply_key}', reply_key) - end - - def reply_key_from_address(address) - regex = address_regex - return unless regex - - match = address.match(regex) - return unless match - - match[1] - end - - private - - def config - Gitlab.config.reply_by_email - end - - def address_regex - wildcard_address = config.address - return nil unless wildcard_address - - regex = Regexp.escape(wildcard_address) - regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.+)") - Regexp.new(regex).freeze - end - end - end -end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index b8eb13a4fea..c8222f8ce11 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -2,7 +2,7 @@ namespace :gitlab do desc "GitLab | Check the configuration of GitLab and its environment" task check: %w{gitlab:gitlab_shell:check gitlab:sidekiq:check - gitlab:reply_by_email:check + gitlab:incoming_email:check gitlab:ldap:check gitlab:app:check} @@ -634,13 +634,13 @@ namespace :gitlab do end - namespace :reply_by_email do + namespace :incoming_email do desc "GitLab | Check the configuration of Reply by email" task check: :environment do warn_user_is_not_gitlab start_checking "Reply by email" - if Gitlab.config.reply_by_email.enabled + if Gitlab.config.incoming_email.enabled check_address_formatted_correctly check_mail_room_config_exists check_imap_authentication @@ -665,12 +665,12 @@ namespace :gitlab do def check_address_formatted_correctly print "Address formatted correctly? ... " - if Gitlab::ReplyByEmail.address_formatted_correctly? + if Gitlab::IncomingEmail.address_formatted_correctly? puts "yes".green else puts "no".red try_fixing_it( - "Make sure that the address in config/gitlab.yml includes the '%{reply_key}' placeholder." + "Make sure that the address in config/gitlab.yml includes the '%{key}' placeholder." ) fix_and_rerun end @@ -689,7 +689,7 @@ namespace :gitlab do "Enable mail_room in the init.d configuration." ) for_more_information( - "doc/reply_by_email/README.md" + "doc/incoming_email/README.md" ) fix_and_rerun end @@ -708,7 +708,7 @@ namespace :gitlab do "Enable mail_room in your Procfile." ) for_more_information( - "doc/reply_by_email/README.md" + "doc/incoming_email/README.md" ) fix_and_rerun end @@ -753,7 +753,7 @@ namespace :gitlab do "Check that the information in config/mail_room.yml is correct" ) for_more_information( - "doc/reply_by_email/README.md" + "doc/incoming_email/README.md" ) fix_and_rerun end @@ -789,7 +789,7 @@ namespace :gitlab do "Check that the information in config/mail_room.yml is correct" ) for_more_information( - "doc/reply_by_email/README.md" + "doc/incoming_email/README.md" ) fix_and_rerun end diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index 1cc80f35f98..e470b7cd5f5 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" describe Gitlab::Email::Receiver do before do - stub_reply_by_email_setting(enabled: true, address: "reply+%{reply_key}@appmail.adventuretime.ooo") + stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo") end let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb new file mode 100644 index 00000000000..5fdb9c723b1 --- /dev/null +++ b/spec/lib/gitlab/incoming_email_spec.rb @@ -0,0 +1,61 @@ +require "spec_helper" + +describe Gitlab::IncomingEmail do + describe "self.enabled?" do + context "when reply by email is enabled" do + before do + stub_incoming_email_setting(enabled: true) + end + + context "when the address is valid" do + before do + stub_incoming_email_setting(address: "replies+%{key}@example.com") + end + + it "returns true" do + expect(described_class.enabled?).to be_truthy + end + end + + context "when the address is invalid" do + before do + stub_incoming_email_setting(address: "replies@example.com") + end + + it "returns false" do + expect(described_class.enabled?).to be_falsey + end + end + end + + context "when reply by email is disabled" do + before do + stub_incoming_email_setting(enabled: false) + end + + it "returns false" do + expect(described_class.enabled?).to be_falsey + end + end + end + + context "self.reply_address" do + before do + stub_incoming_email_setting(address: "replies+%{key}@example.com") + end + + it "returns the address with an interpolated reply key" do + expect(described_class.reply_address("key")).to eq("replies+key@example.com") + end + end + + context "self.key_from_address" do + before do + stub_incoming_email_setting(address: "replies+%{key}@example.com") + end + + it "returns reply key" do + expect(described_class.key_from_address("replies+key@example.com")).to eq("key") + end + end +end diff --git a/spec/lib/gitlab/reply_by_email_spec.rb b/spec/lib/gitlab/reply_by_email_spec.rb deleted file mode 100644 index a678c7e1a76..00000000000 --- a/spec/lib/gitlab/reply_by_email_spec.rb +++ /dev/null @@ -1,86 +0,0 @@ -require "spec_helper" - -describe Gitlab::ReplyByEmail do - describe "self.enabled?" do - context "when reply by email is enabled" do - before do - stub_reply_by_email_setting(enabled: true) - end - - context "when the address is valid" do - before do - stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") - end - - it "returns true" do - expect(described_class.enabled?).to be_truthy - end - end - - context "when the address is invalid" do - before do - stub_reply_by_email_setting(address: "replies@example.com") - end - - it "returns false" do - expect(described_class.enabled?).to be_falsey - end - end - end - - context "when reply by email is disabled" do - before do - stub_reply_by_email_setting(enabled: false) - end - - it "returns false" do - expect(described_class.enabled?).to be_falsey - end - end - end - - describe "self.reply_key" do - context "when enabled" do - before do - allow(described_class).to receive(:enabled?).and_return(true) - end - - it "returns a random hex" do - key = described_class.reply_key - key2 = described_class.reply_key - - expect(key).not_to eq(key2) - end - end - - context "when disabled" do - before do - allow(described_class).to receive(:enabled?).and_return(false) - end - - it "returns nil" do - expect(described_class.reply_key).to be_nil - end - end - end - - context "self.reply_address" do - before do - stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") - end - - it "returns the address with an interpolated reply key" do - expect(described_class.reply_address("key")).to eq("replies+key@example.com") - end - end - - context "self.reply_key_from_address" do - before do - stub_reply_by_email_setting(address: "replies+%{reply_key}@example.com") - end - - it "returns reply key" do - expect(described_class.reply_key_from_address("replies+key@example.com")).to eq("key") - end - end -end diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index ef3a120d44a..f40ee862df8 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -17,8 +17,8 @@ module StubConfiguration allow(Gitlab.config.gravatar).to receive_messages(messages) end - def stub_reply_by_email_setting(messages) - allow(Gitlab.config.reply_by_email).to receive_messages(messages) + def stub_incoming_email_setting(messages) + allow(Gitlab.config.incoming_email).to receive_messages(messages) end private diff --git a/spec/workers/email_receiver_worker_spec.rb b/spec/workers/email_receiver_worker_spec.rb index e8f1bd2fa2f..65a8d7d9197 100644 --- a/spec/workers/email_receiver_worker_spec.rb +++ b/spec/workers/email_receiver_worker_spec.rb @@ -5,7 +5,7 @@ describe EmailReceiverWorker do context "when reply by email is enabled" do before do - allow(Gitlab::ReplyByEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) end it "calls the email receiver" do @@ -33,7 +33,7 @@ describe EmailReceiverWorker do context "when reply by email is disabled" do before do - allow(Gitlab::ReplyByEmail).to receive(:enabled?).and_return(false) + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(false) end it "doesn't call the email receiver" do -- cgit v1.2.1 From 017219e7f548936ce348eeb445dc4717896902ec Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 11:42:19 +0200 Subject: Tweak text [ci skip] --- doc/incoming_email/README.md | 6 +++--- doc/incoming_email/postfix.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md index 2a94bc07f2e..c94e25bd4cc 100644 --- a/doc/incoming_email/README.md +++ b/doc/incoming_email/README.md @@ -16,7 +16,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. ### Omnibus package installations -1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` and fill in the details for your specific IMAP server and email account: +1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` that references the item being replied to and fill in the details for your specific IMAP server and email account: ```ruby gitlab_rails['incoming_email_enabled'] = true @@ -53,7 +53,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. cd /home/git/gitlab ``` -1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key`: +1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to: ```sh sudo editor config/gitlab.yml @@ -134,7 +134,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. 1. Go to the GitLab installation directory. -1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key`: +1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to: ```yaml incoming_email: diff --git a/doc/incoming_email/postfix.md b/doc/incoming_email/postfix.md index 09c5804cb77..18bf3db1744 100644 --- a/doc/incoming_email/postfix.md +++ b/doc/incoming_email/postfix.md @@ -27,7 +27,7 @@ The instructions make the assumption that you will be using the email address `i ## Create user -1. Create a user for incoming. +1. Create a user for incoming email. ```sh sudo useradd -m -s /bin/bash incoming -- cgit v1.2.1 From 25c7467ce1d2146f358ce416a2a41cfd76db5f9a Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 21 Sep 2015 11:42:59 +0200 Subject: Make importing builds less noisy --- lib/ci/migrate/builds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ci/migrate/builds.rb b/lib/ci/migrate/builds.rb index fdc143cfad5..c4f62e55295 100644 --- a/lib/ci/migrate/builds.rb +++ b/lib/ci/migrate/builds.rb @@ -13,7 +13,7 @@ module Ci backup_existing_builds_dir FileUtils.mkdir_p(app_builds_dir, mode: 0700) - unless system('tar', '-C', app_builds_dir, '-zxvf', backup_builds_tarball) + unless system('tar', '-C', app_builds_dir, '-zxf', backup_builds_tarball) abort 'Restore failed'.red end end -- cgit v1.2.1 From e0823d189f54ab10244afb8efb8aae22fa2dbbdf Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 21 Sep 2015 11:46:12 +0200 Subject: Relax CI version check during import --- lib/ci/migrate/manager.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ci/migrate/manager.rb b/lib/ci/migrate/manager.rb index 4205809368d..e5e4fb784eb 100644 --- a/lib/ci/migrate/manager.rb +++ b/lib/ci/migrate/manager.rb @@ -1,6 +1,8 @@ module Ci module Migrate class Manager + CI_IMPORT_PREFIX = '8.0' # Only allow imports from CI 8.0.x + def cleanup $progress.print "Deleting tmp directories ... " @@ -48,7 +50,7 @@ module Ci 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 + if !settings[:gitlab_version].start_with?(CI_IMPORT_PREFIX) puts "GitLab CI version mismatch:".red puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red exit 1 -- cgit v1.2.1 From 3abcab779ec3015914eda5d715b4a9588445d815 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 13:53:03 +0200 Subject: Fixed MR handling when GitLab CI project is not present --- app/models/project_services/gitlab_ci_service.rb | 8 ++++++-- app/views/projects/merge_requests/widget/_heading.html.haml | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 820dd3f567c..9e2b3bcd873 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -46,7 +46,9 @@ class GitlabCiService < CiService end ci_project = Ci::Project.find_by(gitlab_id: project.id) - Ci::CreateCommitService.new.execute(ci_project, data) + if ci_project + Ci::CreateCommitService.new.execute(ci_project, data) + end end def get_ci_commit(sha, ref) @@ -85,7 +87,9 @@ class GitlabCiService < CiService end def build_page(sha, ref) - Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) + if project.gitlab_ci_project.present? + Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) + end end def title diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 4d4e2f68f61..10640f746f0 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -10,7 +10,8 @@ %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + - if ci_build_details_path(@merge_request) + = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget = icon("spinner spin") -- cgit v1.2.1 From abaa65efc1f912ffef05ccaf1e92dc009f828278 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 14:52:51 +0200 Subject: Revert "Fixed MR handling when GitLab CI project is not present" This reverts commit 3abcab779ec3015914eda5d715b4a9588445d815. --- app/models/project_services/gitlab_ci_service.rb | 8 ++------ app/views/projects/merge_requests/widget/_heading.html.haml | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 9e2b3bcd873..820dd3f567c 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -46,9 +46,7 @@ class GitlabCiService < CiService end ci_project = Ci::Project.find_by(gitlab_id: project.id) - if ci_project - Ci::CreateCommitService.new.execute(ci_project, data) - end + Ci::CreateCommitService.new.execute(ci_project, data) end def get_ci_commit(sha, ref) @@ -87,9 +85,7 @@ class GitlabCiService < CiService end def build_page(sha, ref) - if project.gitlab_ci_project.present? - Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) - end + Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) end def title diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 10640f746f0..4d4e2f68f61 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -10,8 +10,7 @@ %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - - if ci_build_details_path(@merge_request) - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget = icon("spinner spin") -- cgit v1.2.1 From 82edd68e55c1983ec514fd7944c8f9b956333cd4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 14:53:58 +0200 Subject: Remove CI button from project home panel --- app/views/projects/_home_panel.html.haml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index c1d5428f676..6e53f55b0ab 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -30,8 +30,4 @@ = 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 ca6fd0a27da17e927b4db7136ead01f7564baf2b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 13:53:03 +0200 Subject: Fixed MR handling when GitLab CI project is not present --- app/models/project_services/gitlab_ci_service.rb | 8 ++++++-- app/views/projects/merge_requests/widget/_heading.html.haml | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 820dd3f567c..9e2b3bcd873 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -46,7 +46,9 @@ class GitlabCiService < CiService end ci_project = Ci::Project.find_by(gitlab_id: project.id) - Ci::CreateCommitService.new.execute(ci_project, data) + if ci_project + Ci::CreateCommitService.new.execute(ci_project, data) + end end def get_ci_commit(sha, ref) @@ -85,7 +87,9 @@ class GitlabCiService < CiService end def build_page(sha, ref) - Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) + if project.gitlab_ci_project.present? + Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) + end end def title diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 4d4e2f68f61..10640f746f0 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -10,7 +10,8 @@ %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + - if ci_build_details_path(@merge_request) + = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget = icon("spinner spin") -- cgit v1.2.1 From a9d686b2fbc1b6e158cd2a3ed5f0f3092611f6ce Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 14:57:33 +0200 Subject: Link project repo size to files --- app/helpers/projects_helper.rb | 4 ++-- app/views/projects/show.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index a2b83c50c2e..7b4747ce3d7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -156,8 +156,8 @@ module ProjectsHelper end end - def repository_size(project = nil) - "#{(project || @project).repository_size} MB" + def repository_size(project = @project) + "#{project.repository_size} MB" rescue # In order to prevent 500 error # when application cannot allocate memory diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index aa5914e3ed9..6a5fc689803 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -24,7 +24,7 @@ = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag') %li - = link_to namespace_project_path(@project.namespace, @project) do + = link_to project_files_path(@project) do = repository_size - if !prefer_readme? && @repository.readme -- cgit v1.2.1 From f935259260c49faaab945fbf7b300c1addae2a44 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 14:59:50 +0200 Subject: Use consistent styling for number of commits in CI nav sidebar --- 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 d747679c8cf..0f155c7dfef 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -9,7 +9,7 @@ %i.fa.fa-list-alt %span Commits - %small.pull-right= @project.commits.count + %span.count= @project.commits.count = nav_link path: 'charts#show' do = link_to ci_project_charts_path(@project) do %i.fa.fa-bar-chart -- cgit v1.2.1 From 09e0f8abb4f9241b6cbb01624666313caaced184 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 21 Sep 2015 15:04:30 +0200 Subject: Use fixed-width icons in CI nav sidebar. --- app/views/layouts/ci/_nav_admin.html.haml | 10 +++++----- app/views/layouts/ci/_nav_project.html.haml | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index e9974c85733..af2545a22d8 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -8,26 +8,26 @@ %li.separate-item = nav_link path: 'projects#index' do = link_to ci_admin_projects_path do - %i.fa.fa-list-alt + = icon('list-alt fw') Projects = nav_link path: 'events#index' do = link_to ci_admin_events_path do - %i.fa.fa-book + = icon('book fw') Events = nav_link path: ['runners#index', 'runners#show'] do = link_to ci_admin_runners_path do - %i.fa.fa-cog + = icon('cog fw') Runners %small.pull-right = Ci::Runner.count(:all) = nav_link path: 'builds#index' do = link_to ci_admin_builds_path do - %i.fa.fa-link + = icon('link fw') Builds %small.pull-right = Ci::Build.count(:all) = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to ci_admin_application_settings_path do - %i.fa.fa-cogs + = icon('cogs fw') %span Settings diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index d747679c8cf..301a92518c0 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -6,48 +6,48 @@ %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 + = icon('list-alt fw') %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 + = icon('bar-chart fw') %span Charts = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do = link_to ci_project_runners_path(@project) do - %i.fa.fa-cog + = icon('cog fw') %span Runners = nav_link path: 'variables#show' do = link_to ci_project_variables_path(@project) do - %i.fa.fa-code + = icon('code fw') %span Variables = nav_link path: 'web_hooks#index' do = link_to ci_project_web_hooks_path(@project) do - %i.fa.fa-link + = icon('link fw') %span Web Hooks = nav_link path: 'triggers#index' do = link_to ci_project_triggers_path(@project) do - %i.fa.fa-retweet + = icon('retweet fw') %span Triggers = nav_link path: ['services#index', 'services#edit'] do = link_to ci_project_services_path(@project) do - %i.fa.fa-share + = icon('share fw') %span Services = nav_link path: 'events#index' do = link_to ci_project_events_path(@project) do - %i.fa.fa-book + = icon('book fw') %span Events %li.separate-item = nav_link path: 'projects#edit' do = link_to edit_ci_project_path(@project) do - %i.fa.fa-cogs + = icon('cogs fw') %span Settings -- cgit v1.2.1 From ea5c25bb4ed46decde33c86e618f0b18dd55dae3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Sep 2015 08:39:19 -0700 Subject: Remove milestones from merge requests when milestones are deleted Reported by https://github.com/gitlabhq/gitlabhq/issues/9643 --- CHANGELOG | 1 + app/services/milestones/destroy_service.rb | 5 +++++ spec/controllers/projects/milestones_controller_spec.rb | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 31b2a759d40..ceebdc12d0f 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) - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) + - Remove milestones from merge requests when milestones are deleted (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - 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) diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb index 7ce7d259d0b..2414966505b 100644 --- a/app/services/milestones/destroy_service.rb +++ b/app/services/milestones/destroy_service.rb @@ -4,10 +4,15 @@ module Milestones Milestone.transaction do update_params = { milestone: nil } + milestone.issues.each do |issue| Issues::UpdateService.new(project, current_user, update_params).execute(issue) end + milestone.merge_requests.each do |merge_request| + MergeRequests::UpdateService.new(project, current_user, update_params).execute(merge_request) + end + event_service.destroy_milestone(milestone, current_user) Event.for_milestone_id(milestone.id).each do |event| diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 35446640929..8127efabe6e 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -5,6 +5,7 @@ describe Projects::MilestonesController do let(:user) { create(:user) } let(:milestone) { create(:milestone, project: project) } let(:issue) { create(:issue, project: project, milestone: milestone) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) } before do sign_in(user) @@ -14,6 +15,7 @@ describe Projects::MilestonesController do describe "#destroy" do it "should remove milestone" do + merge_request.reload expect(issue.milestone_id).to eq(milestone.id) delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js @@ -24,6 +26,10 @@ describe Projects::MilestonesController do expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound) issue.reload expect(issue.milestone_id).to eq(nil) + + merge_request.reload + expect(merge_request.milestone_id).to eq(nil) + # Check system note left for milestone removal last_note = project.issues.find(issue.id).notes[-1].note expect(last_note).to eq('Milestone removed') -- cgit v1.2.1 From 3a001a929064f615eda8fe935c6359381db21ddd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 21 Sep 2015 16:25:59 +0200 Subject: Fetch merge request ref if it is missing when visit MR page This will fix merge problem for merge requests between forks created before 8.0 Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/merge_requests_controller.rb | 7 +++++++ app/models/merge_request.rb | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index f3054881daf..c1231994f25 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] before_action :validates_merge_request, only: [:show, :diffs, :commits] before_action :define_show_vars, only: [:show, :diffs, :commits] + before_action :ensure_ref_fetched, only: [:show, :commits, :diffs] # Allow read any merge_request before_action :authorize_read_merge_request! @@ -277,4 +278,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController :state_event, :description, :task_num, label_ids: [] ) end + + # Make sure merge requests created before 8.0 + # have head file in refs/merge-requests/ + def ensure_ref_fetched + @merge_request.ensure_ref_fetched + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 93faa133875..eb468c6cd53 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -433,10 +433,22 @@ class MergeRequest < ActiveRecord::Base target_project.repository.fetch_ref( source_project.repository.path_to_repo, "refs/heads/#{source_branch}", - "refs/merge-requests/#{iid}/head" + ref_path ) end + def ref_path + "refs/merge-requests/#{iid}/head" + end + + def ref_is_fetched? + File.exists?(File.join(project.repository.path_to_repo, ref_path)) + end + + def ensure_ref_fetched + fetch_ref unless ref_is_fetched? + end + def in_locked_state begin lock_mr -- cgit v1.2.1 From 4e2bb80fb3ba73b11b3ab81f760434de95e3464e Mon Sep 17 00:00:00 2001 From: Jared Szechy Date: Mon, 21 Sep 2015 10:49:44 -0400 Subject: FogBugz Import: Closed comments may contain user data --- lib/gitlab/fogbugz_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 61e08b23543..496256700b8 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -154,7 +154,7 @@ module Gitlab while comment = comments.shift verb = comment['sVerb'] - next if verb == 'Opened' || verb === 'Closed' + next if verb == 'Opened' content = format_content(comment['s']) attachments = format_attachments(comment['rgAttachments']) -- cgit v1.2.1 From 534bd0f87d6354a115a0fe7ee2a608b9f1d14a83 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Mon, 21 Sep 2015 16:43:43 +0000 Subject: Fix grammar --- config/gitlab.yml.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 69cdf497a84..856e75dc829 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -4,7 +4,7 @@ # ########################### NOTE ##################################### # This file should not receive new settings. All configuration options # -# that do not require application restart are being moved to # +# that do not require an application restart are being moved to # # ApplicationSetting model! # # If you change this file in a Merge Request, please also create # # a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests # @@ -425,4 +425,4 @@ test: sync_ssh_keys: false staging: - <<: *base + <<: *base \ No newline at end of file -- cgit v1.2.1 From ef2fb20135242f9ee453fae18b1e7d11b6774406 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Sep 2015 07:30:19 -0700 Subject: More tweaks to make note edit/preview CSS consistent Closes #2585 --- app/assets/stylesheets/generic/typography.scss | 4 ++++ app/assets/stylesheets/pages/note_form.scss | 2 +- app/views/projects/notes/_edit_form.html.haml | 2 +- app/views/projects/notes/_form.html.haml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 41189432bf6..d5f0d86a307 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -105,6 +105,10 @@ textarea.js-gfm-input { font-family: $monospace_font; } +.md-preview { + font-family: $monospace_font; +} + .strikethrough { text-decoration: line-through; } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index b311d26d675..fdc2c3332df 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -109,7 +109,7 @@ .note-edit-form { display: none; - font-size: 13px; + font-size: 15px; .form-actions { padding-left: 20px; diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index 8f7d2e84c70..a0e26f9827e 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -1,7 +1,7 @@ .note-edit-form = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f| = note_target_fields(note) - = render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do + = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/notes/hints' diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 3be8f44b282..d99445da59a 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -7,7 +7,7 @@ = f.hidden_field :noteable_id = f.hidden_field :noteable_type - = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' = render 'projects/notes/hints' .error-alert -- cgit v1.2.1 From 68cdeb71d56f0a7c60efed0d70636409b9937eb8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 20:25:02 +0200 Subject: Fix permissions problems - Allow developers to retry builds - Hide advanced project options from CI page for non-admin users --- app/controllers/ci/application_controller.rb | 2 +- app/views/layouts/ci/_nav_project.html.haml | 83 ++++++++++++++-------------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 8d8ff75ff72..d8227e632e4 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -38,7 +38,7 @@ module Ci end def authorize_manage_builds! - unless can?(current_user, :admin_project, gl_project) + unless can?(current_user, :manage_builds, gl_project) return page_404 end end diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index b7e997be108..cb1dece073c 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -10,44 +10,45 @@ %span Commits %span.count= @project.commits.count - = nav_link path: 'charts#show' do - = link_to ci_project_charts_path(@project) do - = icon('bar-chart fw') - %span - Charts - = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do - = link_to ci_project_runners_path(@project) do - = icon('cog fw') - %span - Runners - = nav_link path: 'variables#show' do - = link_to ci_project_variables_path(@project) do - = icon('code fw') - %span - Variables - = nav_link path: 'web_hooks#index' do - = link_to ci_project_web_hooks_path(@project) do - = icon('link fw') - %span - Web Hooks - = nav_link path: 'triggers#index' do - = link_to ci_project_triggers_path(@project) do - = icon('retweet fw') - %span - Triggers - = nav_link path: ['services#index', 'services#edit'] do - = link_to ci_project_services_path(@project) do - = icon('share fw') - %span - Services - = nav_link path: 'events#index' do - = link_to ci_project_events_path(@project) do - = icon('book fw') - %span - Events - %li.separate-item - = nav_link path: 'projects#edit' do - = link_to edit_ci_project_path(@project) do - = icon('cogs fw') - %span - Settings + - if can?(current_user, :admin_project, gl_project) + = nav_link path: 'charts#show' do + = link_to ci_project_charts_path(@project) do + = icon('bar-chart fw') + %span + Charts + = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do + = link_to ci_project_runners_path(@project) do + = icon('cog fw') + %span + Runners + = nav_link path: 'variables#show' do + = link_to ci_project_variables_path(@project) do + = icon('code fw') + %span + Variables + = nav_link path: 'web_hooks#index' do + = link_to ci_project_web_hooks_path(@project) do + = icon('link fw') + %span + Web Hooks + = nav_link path: 'triggers#index' do + = link_to ci_project_triggers_path(@project) do + = icon('retweet fw') + %span + Triggers + = nav_link path: ['services#index', 'services#edit'] do + = link_to ci_project_services_path(@project) do + = icon('share fw') + %span + Services + = nav_link path: 'events#index' do + = link_to ci_project_events_path(@project) do + = icon('book fw') + %span + Events + %li.separate-item + = nav_link path: 'projects#edit' do + = link_to edit_ci_project_path(@project) do + = icon('cogs fw') + %span + Settings -- cgit v1.2.1 From 071ae2619cbe8d39b87518eef65009cbfb3939c9 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 20:25:57 +0200 Subject: Update CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index ceebdc12d0f..8e516322a6a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -44,6 +44,8 @@ v 8.0.0 (unreleased) - Retrieving oauth token with LDAP credentials - Load Application settings from running database unless env var USE_DB=false - Added Drone CI integration (Kirill Zaitsev) + - Allow developers to retry builds + - Hide advanced project options for non-admin users - Fail builds if no .gitlab-ci.yml is found - Refactored service API and added automatically service docs generator (Kirill Zaitsev) - Added web_url key project hook_attrs (Kirill Zaitsev) -- cgit v1.2.1 From 24a0e86dd3bb682802784af3b011a00f43104c13 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 Sep 2015 09:22:43 -0700 Subject: Remove deleted milestones from merge requests Closes https://github.com/gitlabhq/gitlabhq/issues/9643 --- .../20150918161719_remove_invalid_milestones_from_merge_requests.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb diff --git a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb new file mode 100644 index 00000000000..0aad6fe5e6e --- /dev/null +++ b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb @@ -0,0 +1,5 @@ +class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration + def up + execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)") + end +end -- cgit v1.2.1 From 2f88459297d9fe8a33f608bd924f0bd5ad40161b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 15:35:29 -0400 Subject: Ensure raise_error expectations provide arguments --- spec/models/ci/variable_spec.rb | 3 ++- spec/models/ci/web_hook_spec.rb | 3 ++- spec/requests/ci/api/projects_spec.rb | 3 ++- spec/services/ci/create_project_service_spec.rb | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 97a3d0081f4..d034a6c7b9f 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -38,7 +38,8 @@ describe Ci::Variable do 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 + expect { subject.value }. + to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt') end end end diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index c4c0b007c11..bf9481ab81d 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -56,7 +56,8 @@ describe Ci::WebHook do it "catches exceptions" do expect(Ci::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(RuntimeError, 'Some HTTP Post error') end end end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 2adae52e79e..409f47fa448 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -165,7 +165,8 @@ describe Ci::API::API 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 + expect { project.reload }. + to raise_error(ActiveRecord::RecordNotFound) end it "non-manager is not authorized" do diff --git a/spec/services/ci/create_project_service_spec.rb b/spec/services/ci/create_project_service_spec.rb index c0af515aa8f..2de7b0deca7 100644 --- a/spec/services/ci/create_project_service_spec.rb +++ b/spec/services/ci/create_project_service_spec.rb @@ -15,7 +15,8 @@ describe Ci::CreateProjectService do context 'without project dump' do it 'should raise exception' do - expect { service.execute(current_user, '', '') }.to raise_error + expect { service.execute(current_user, '', '') }. + to raise_error(NoMethodError) end end -- cgit v1.2.1 From 3a4c5986fb9e1cf5511a491ffd2902c4c70a6e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Mon, 21 Sep 2015 22:05:13 +0200 Subject: clarify confirmation text on user deletion. --- app/views/admin/users/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 5e40d95d1c5..e3698ac1c46 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -97,5 +97,5 @@ - if user.access_locked? = link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success", data: { confirm: 'Are you sure?' } - if user.can_be_removed? - = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All tickets linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove" + = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove" = paginate @users, theme: "gitlab" -- cgit v1.2.1 From 157ee6623c4165f8395eb6db9a36b7e568dd9ebc Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 22:36:33 +0200 Subject: Update guide --- doc/migrate_ci_to_ce/README.md | 51 ++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 8525fb3847d..bddd8a82739 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -30,7 +30,7 @@ The migration is divided into three parts: #### 1. Stop GitLab CI sudo service gitlab_ci stop - + #### 2. Create a backup The migration procedure modifies the structure of the CI database. If something @@ -44,10 +44,8 @@ setting a `MYSQL_TO_POSTGRESQL` flag. You can check which database each install is using by viewing their database configuration files: -```sh -cat /home/gitlab_ci/gitlab-ci/config/database.yml -cat /home/git/gitlab/config/database.yml -``` + cat /home/gitlab_ci/gitlab-ci/config/database.yml + cat /home/git/gitlab/config/database.yml - If both applications use the same database `adapter`, create the backup with this command: @@ -56,22 +54,19 @@ cat /home/git/gitlab/config/database.yml cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production ``` - + - If CI uses MySQL, and CE (or EE) uses PostgreSQL, create the backup with this command (note the `MYSQL_TO_POSTGRESQL` flag): - ```bash cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 ``` - + #### 3. Remove cronjob -``` -cd /home/gitlab_ci/gitlab-ci -sudo -u gitlab_ci -H bundle exec whenever --clear-crontab -``` + cd /home/gitlab_ci/gitlab-ci + sudo -u gitlab_ci -H bundle exec whenever --clear-crontab ### Part II: GitLab CE (or EE) @@ -80,21 +75,33 @@ sudo -u gitlab_ci -H bundle exec whenever --clear-crontab Your GitLab CE or EE installation **must be version 8.0**. If it's not, follow the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md). -#### 2. Stop GitLab +#### 2. Prevent CI usage for time of migration + +As Admin go to Admin Area -> Settings -> and uncheck +**Disable to prevent CI usage until rake ci:migrate is run (8.0 only)**. + +This will prevent from creating the CI projects till you finish migration. + +#### 3. Stop GitLab Before you can migrate data you need to stop the GitLab service first: sudo service gitlab stop -#### 3. Create a backup +#### 4. Create a backup This migration poses a **significant risk** of breaking your GitLab installation. Create a backup before proceeding: cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production + +It's possible to speedup backup creation. To do that you can skip repositories and uploads. -#### 4. Copy secret tokens from CI + cd /home/git/gitlab + sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=repositories,uploads + +#### 5. Copy secret tokens from CI The `secrets.yml` file stores encryption keys for secure variables. @@ -105,7 +112,7 @@ same file in GitLab CE: sudo chown git:git /home/git/gitlab/config/secrets.yml sudo chown 0600 /home/git/gitlab/config/secrets.yml -#### 5. New configuration options for `gitlab.yml` +#### 6. New configuration options for `gitlab.yml` There are new configuration options available for `gitlab.yml`. View them with the command below and apply them manually to your current `gitlab.yml`: @@ -114,12 +121,18 @@ the command below and apply them manually to your current `gitlab.yml`: The new options include configuration settings for GitLab CI. -#### 6. Copy backup from GitLab CI +#### 7. Copy backup from GitLab CI sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar -#### 7. Import GitLab CI backup +If moving across the servers you can use **scp**. +However, this requires you to provide authorized key or password to login to GitLab CE servers from CI server. +You can try to use ssh-agent from your local machine to have that: login to your GitLab CI server using `ssh -A`. + + scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup + +#### 8. Import GitLab CI backup Now you'll import the GitLab CI database dump that you created earlier into the GitLab CE or EE database: @@ -128,7 +141,7 @@ GitLab CE or EE database: This task will take some time. -#### 8. Start GitLab +#### 9. Start GitLab You can start GitLab CE (or EE) now and see if everything is working: -- cgit v1.2.1 From 12969a14dd72224017c4852b6c0cb4a6b50317b3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 21 Sep 2015 22:47:33 +0200 Subject: Add commands for Omnibus installation --- doc/migrate_ci_to_ce/README.md | 73 +++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index bddd8a82739..a8614e5fc49 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -19,7 +19,7 @@ time. Make sure to plan ahead. If you are running a version of GitLab CI prior to 8.0 please follow the appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update/). -The migration is divided into three parts: +The migration is divided into three parts and covers manual and omnibus installations: 1. [GitLab CI](#part-i-gitlab-ci) 1. [Gitlab CE (or EE)](#part-ii-gitlab-ce-or-ee) @@ -29,7 +29,11 @@ The migration is divided into three parts: #### 1. Stop GitLab CI + # Manual installation sudo service gitlab_ci stop + + # Omnibus installation + sudo gitlab-ctl stop ci-unicorn ci-sidekiq #### 2. Create a backup @@ -41,6 +45,8 @@ If your GitLab CI installation uses **MySQL** and your GitLab CE (or EE) installation uses **PostgreSQL** you'll need to convert the CI database by setting a `MYSQL_TO_POSTGRESQL` flag. +If you use Omnibus package you most likely use the **PostgreSQL** on GitLab CE (or EE) and CI. + You can check which database each install is using by viewing their database configuration files: @@ -50,21 +56,26 @@ database configuration files: - If both applications use the same database `adapter`, create the backup with this command: - ```bash - cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production - ``` + # Manual installation + cd /home/gitlab_ci/gitlab-ci + sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production + + # Omnibus installation + sudo gitlab-ci-rake backup:create - If CI uses MySQL, and CE (or EE) uses PostgreSQL, create the backup with this command (note the `MYSQL_TO_POSTGRESQL` flag): - ```bash - cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 - ``` + # Manual installation + cd /home/gitlab_ci/gitlab-ci + sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 + + # Omnibus installation + sudo gitlab-ci-rake backup:create MYSQL_TO_POSTGRESQL=1 -#### 3. Remove cronjob +#### 3. Remove cronjob (manual installation) + # Manual installation cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec whenever --clear-crontab @@ -75,6 +86,8 @@ database configuration files: Your GitLab CE or EE installation **must be version 8.0**. If it's not, follow the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md). +If you use Omnibus package simply do `apt-get upgrade` to install a new version. + #### 2. Prevent CI usage for time of migration As Admin go to Admin Area -> Settings -> and uncheck @@ -86,20 +99,32 @@ This will prevent from creating the CI projects till you finish migration. Before you can migrate data you need to stop the GitLab service first: + # Manual installation sudo service gitlab stop + + # Omnibus installation + sudo gitlab-ctl stop unicorn sidekiq #### 4. Create a backup This migration poses a **significant risk** of breaking your GitLab installation. Create a backup before proceeding: + # Manual installation cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production + # Omnibus installation + sudo gitlab-rake gitlab:backup:create + It's possible to speedup backup creation. To do that you can skip repositories and uploads. + # Manual installation cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=repositories,uploads + + # Omnibus installation + sudo gitlab-rake gitlab:backup:create SKIP=repositories,uploads #### 5. Copy secret tokens from CI @@ -108,11 +133,18 @@ The `secrets.yml` file stores encryption keys for secure variables. You need to copy the contents of GitLab CI's `config/secrets.yml` file to the same file in GitLab CE: + # Manual installation 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 -#### 6. New configuration options for `gitlab.yml` +If you use Omnibus installation and your CI server is on the same server as GitLab CE (or EE) you don't need to do anything, +because the secrets are stored in **/etc/gitlab/gitlab-secrets.json**. + +If you migrate your Omnibus CI server to other server you need to copy **gitlab_ci** +section of **/etc/gitlab/gitlab-secrets.json** to the other server. + +#### 6. New configuration options for `gitlab.yml` (manual installation only) There are new configuration options available for `gitlab.yml`. View them with the command below and apply them manually to your current `gitlab.yml`: @@ -123,21 +155,34 @@ The new options include configuration settings for GitLab CI. #### 7. Copy backup from GitLab CI + # Manual installation sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar + + # Omnibus installation + sudo cp -v /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar /var/opt/gitlab/backups/ + sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar If moving across the servers you can use **scp**. However, this requires you to provide authorized key or password to login to GitLab CE servers from CI server. You can try to use ssh-agent from your local machine to have that: login to your GitLab CI server using `ssh -A`. + # Manual installation scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup + + # Omnibus installation + scp /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/var/opt/gitlab/backups/ #### 8. Import GitLab CI backup Now you'll import the GitLab CI database dump that you created earlier into the GitLab CE or EE database: + # Manual installation sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production + + # Omnibus installation + sudo gitlab-rake ci:migrate This task will take some time. @@ -145,10 +190,16 @@ This task will take some time. You can start GitLab CE (or EE) now and see if everything is working: + # Manual installation sudo service gitlab start + + # Omnibus installation + sudo gitlab-ctl restart unicorn sidekiq ### Part III: Finishing Up +This part is only for **Manual installations**. + #### 1. Update Nginx configuration To ensure that your existing CI runners are able to communicate with the -- cgit v1.2.1 From 0346e9724a8caf82378b5acc631d389f0cac64d9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 21 Sep 2015 14:08:26 -0700 Subject: Tweak remaining CSS for Markdown preview Missed a few items in !1378 --- app/views/projects/milestones/_form.html.haml | 2 +- app/views/projects/wikis/_form.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index b93462e5bdf..74e9668052d 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -21,7 +21,7 @@ .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 - = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .hint .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}. diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 904600499ae..05d754adbe5 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -21,7 +21,7 @@ .form-group.wiki-content = f.label :content, class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do = render 'projects/zen', f: f, attr: :content, classes: 'description form-control' .col-sm-12.hint .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'} diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 1aa1e3c6c97..33ec726e93c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -24,7 +24,7 @@ = f.label :description, 'Description', class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview', locals: { preview_class: "wiki", referenced_users: true } do + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .col-sm-12.hint -- cgit v1.2.1 From 7fa0c793d25c40a1165028a81758be66a6df0c62 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 18:02:50 -0400 Subject: Whitespace --- doc/migrate_ci_to_ce/README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index a8614e5fc49..b40a09a4d97 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -31,10 +31,10 @@ The migration is divided into three parts and covers manual and omnibus installa # Manual installation sudo service gitlab_ci stop - + # Omnibus installation sudo gitlab-ctl stop ci-unicorn ci-sidekiq - + #### 2. Create a backup The migration procedure modifies the structure of the CI database. If something @@ -59,20 +59,20 @@ database configuration files: # Manual installation cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production - + # Omnibus installation sudo gitlab-ci-rake backup:create - + - If CI uses MySQL, and CE (or EE) uses PostgreSQL, create the backup with this command (note the `MYSQL_TO_POSTGRESQL` flag): # Manual installation cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 - + # Omnibus installation sudo gitlab-ci-rake backup:create MYSQL_TO_POSTGRESQL=1 - + #### 3. Remove cronjob (manual installation) # Manual installation @@ -101,7 +101,7 @@ Before you can migrate data you need to stop the GitLab service first: # Manual installation sudo service gitlab stop - + # Omnibus installation sudo gitlab-ctl stop unicorn sidekiq @@ -113,16 +113,16 @@ installation. Create a backup before proceeding: # Manual installation cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production - + # Omnibus installation sudo gitlab-rake gitlab:backup:create - + It's possible to speedup backup creation. To do that you can skip repositories and uploads. # Manual installation cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=repositories,uploads - + # Omnibus installation sudo gitlab-rake gitlab:backup:create SKIP=repositories,uploads @@ -158,7 +158,7 @@ The new options include configuration settings for GitLab CI. # Manual installation sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar - + # Omnibus installation sudo cp -v /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar /var/opt/gitlab/backups/ sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar @@ -169,7 +169,7 @@ You can try to use ssh-agent from your local machine to have that: login to your # Manual installation scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup - + # Omnibus installation scp /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/var/opt/gitlab/backups/ @@ -180,7 +180,7 @@ GitLab CE or EE database: # Manual installation sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production - + # Omnibus installation sudo gitlab-rake ci:migrate @@ -192,7 +192,7 @@ You can start GitLab CE (or EE) now and see if everything is working: # Manual installation sudo service gitlab start - + # Omnibus installation sudo gitlab-ctl restart unicorn sidekiq -- cgit v1.2.1 From 96fd255597319f41998f3a1edd56475c65af7342 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 18:46:20 -0400 Subject: More migration guide updates - Rename "Finishing Up" to "Nginx configuration" - Add fourth "Finishing Up" section - Add Troubleshooting section with a link to backup restoration docs - Change a few indented blocks into fenced blocks so they're highlighted properly. - Added Marin's suggestions for CI-to-CE-on-different-server section. --- doc/migrate_ci_to_ce/README.md | 101 +++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index b40a09a4d97..38f25c09f9e 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -17,13 +17,16 @@ This migration cannot be performed online and takes a significant amount of time. Make sure to plan ahead. If you are running a version of GitLab CI prior to 8.0 please follow the -appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update/). +appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update/) +before proceeding. -The migration is divided into three parts and covers manual and omnibus installations: +The migration is divided into four parts and covers both manual and Omnibus +installations: 1. [GitLab CI](#part-i-gitlab-ci) 1. [Gitlab CE (or EE)](#part-ii-gitlab-ce-or-ee) -1. [Finishing Up](#part-iii-finishing-up) +1. [Nginx configuration](#part-iii-nginx-configuration) +1. [Finishing Up](#part-iv-finishing-up) ### Part I: GitLab CI @@ -45,7 +48,8 @@ If your GitLab CI installation uses **MySQL** and your GitLab CE (or EE) installation uses **PostgreSQL** you'll need to convert the CI database by setting a `MYSQL_TO_POSTGRESQL` flag. -If you use Omnibus package you most likely use the **PostgreSQL** on GitLab CE (or EE) and CI. +If you use the Omnibus package you most likely use **PostgreSQL** on both GitLab +CE (or EE) and CI. You can check which database each install is using by viewing their database configuration files: @@ -84,16 +88,20 @@ database configuration files: #### 1. Ensure GitLab is updated Your GitLab CE or EE installation **must be version 8.0**. If it's not, follow -the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md). +the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md) +before proceeding. -If you use Omnibus package simply do `apt-get upgrade` to install a new version. +If you use the Omnibus packages simply run `apt-get upgrade` to install the +latest version. -#### 2. Prevent CI usage for time of migration +#### 2. Prevent CI usage during the migration process -As Admin go to Admin Area -> Settings -> and uncheck -**Disable to prevent CI usage until rake ci:migrate is run (8.0 only)**. +As an administrator, go to **Admin Area** -> **Settings**, and under **Continuous +Integration** uncheck **Disable to prevent CI usage until rake ci:migrate is run +(8.0 only)**. -This will prevent from creating the CI projects till you finish migration. +This will disable the CI integration and prevent users from creating CI projects +until the migration process is completed. #### 3. Stop GitLab @@ -117,7 +125,7 @@ installation. Create a backup before proceeding: # Omnibus installation sudo gitlab-rake gitlab:backup:create -It's possible to speedup backup creation. To do that you can skip repositories and uploads. +It's possible to speed up backup creation by skipping repositories and uploads: # Manual installation cd /home/git/gitlab @@ -130,21 +138,32 @@ It's possible to speedup backup creation. To do that you can skip repositories a The `secrets.yml` file stores encryption keys for secure variables. -You need to copy the contents of GitLab CI's `config/secrets.yml` file to the -same file in GitLab CE: +- **Manual installations** need to copy the contents of GitLab CI's + `config/secrets.yml` file to the same file in GitLab CE: + ```bash # Manual installation 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 + ``` -If you use Omnibus installation and your CI server is on the same server as GitLab CE (or EE) you don't need to do anything, -because the secrets are stored in **/etc/gitlab/gitlab-secrets.json**. +- **Omnibus installations** where GitLab CI and CE (or EE) are on the same + server don't need to do anything further, because the secrets are stored in + `/etc/gitlab/gitlab-secrets.json`. -If you migrate your Omnibus CI server to other server you need to copy **gitlab_ci** -section of **/etc/gitlab/gitlab-secrets.json** to the other server. +- **Omnibus installations** where GitLab CI is on a different server than CE (or + EE) will need to: + 1. On the CI server, copy the `db_key_base` value from + `/etc/gitlab/gitlab-secrets.json` + 1. On the CE (or EE) server, add `gitlab_ci['db_key_base'] = + "VALUE_FROM_ABOVE"` to the `/etc/gitlab/gitlab.rb` file and run `sudo + gitlab-ctl reconfigure` -#### 6. New configuration options for `gitlab.yml` (manual installation only) +#### 6. New configuration options for `gitlab.yml` + +**Note:** This step is only required for manual installations. Omnibus +installations can [skip to the next step](#7-copy-backup-from-gitlab-ci). There are new configuration options available for `gitlab.yml`. View them with the command below and apply them manually to your current `gitlab.yml`: @@ -155,23 +174,29 @@ The new options include configuration settings for GitLab CI. #### 7. Copy backup from GitLab CI - # Manual installation - sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups - sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar +```bash +# Manual installation +sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups +sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar - # Omnibus installation - sudo cp -v /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar /var/opt/gitlab/backups/ - sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar +# Omnibus installation +sudo cp -v /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar /var/opt/gitlab/backups/ +sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar +``` -If moving across the servers you can use **scp**. -However, this requires you to provide authorized key or password to login to GitLab CE servers from CI server. -You can try to use ssh-agent from your local machine to have that: login to your GitLab CI server using `ssh -A`. +If moving across the servers you can use `scp`. +However, this requires you to provide an authorized key or password to login to +the GitLab CE (or EE) server from the CI server. You can try to use ssh-agent +from your local machine to have that: login to your GitLab CI server using `ssh +-A`. - # Manual installation - scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup +```bash +# Manual installation +scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup - # Omnibus installation - scp /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/var/opt/gitlab/backups/ +# Omnibus installation +scp /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/var/opt/gitlab/backups/ +``` #### 8. Import GitLab CI backup @@ -196,9 +221,10 @@ You can start GitLab CE (or EE) now and see if everything is working: # Omnibus installation sudo gitlab-ctl restart unicorn sidekiq -### Part III: Finishing Up +### Part III: Nginx configuration -This part is only for **Manual installations**. +This part is only required for **Manual installations**. Omnibus users can [skip +to the final step](#part-iv-finishing-up). #### 1. Update Nginx configuration @@ -276,10 +302,17 @@ You should also make sure that you can: sudo /etc/init.d/nginx restart -#### 4. Done! +### Part IV: Finishing Up If everything went well you should be able to access your migrated CI install by visiting `https://gitlab.example.com/ci/`. If you visit the old GitLab CI address, you should be redirected to the new one. **Enjoy!** + +### Troubleshooting + +#### Restore from backup + +If something went wrong and you need to restore a backup, consult the [Backup +restoration](../raketasks/backup_restore.md) guide. -- cgit v1.2.1 From 541d08b8f646081a9024f0af0ae6b3a15ce20136 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 21:10:16 -0400 Subject: Be consistent about "only required for manual install" notes --- doc/migrate_ci_to_ce/README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 38f25c09f9e..3ec541fb441 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -77,7 +77,10 @@ database configuration files: # Omnibus installation sudo gitlab-ci-rake backup:create MYSQL_TO_POSTGRESQL=1 -#### 3. Remove cronjob (manual installation) +#### 3. Remove cronjob + +**Note:** This step is only required for **manual installations**. Omnibus users +can [skip to the next step](#part-ii-gitlab-ce-or-ee). # Manual installation cd /home/gitlab_ci/gitlab-ci @@ -162,8 +165,8 @@ The `secrets.yml` file stores encryption keys for secure variables. #### 6. New configuration options for `gitlab.yml` -**Note:** This step is only required for manual installations. Omnibus -installations can [skip to the next step](#7-copy-backup-from-gitlab-ci). +**Note:** This step is only required for **manual installations**. Omnibus users +can [skip to the next step](#7-copy-backup-from-gitlab-ci). There are new configuration options available for `gitlab.yml`. View them with the command below and apply them manually to your current `gitlab.yml`: @@ -223,8 +226,8 @@ You can start GitLab CE (or EE) now and see if everything is working: ### Part III: Nginx configuration -This part is only required for **Manual installations**. Omnibus users can [skip -to the final step](#part-iv-finishing-up). +This section is only required for **manual installations**. Omnibus users can +[skip to the final step](#part-iv-finishing-up). #### 1. Update Nginx configuration -- cgit v1.2.1 From da54661bbcb56856312a8917143166911434d3ff Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 21:10:50 -0400 Subject: Remove unintended linebreak --- doc/migrate_ci_to_ce/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 3ec541fb441..735862b31c4 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -190,8 +190,8 @@ sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar If moving across the servers you can use `scp`. However, this requires you to provide an authorized key or password to login to the GitLab CE (or EE) server from the CI server. You can try to use ssh-agent -from your local machine to have that: login to your GitLab CI server using `ssh --A`. +from your local machine to have that: login to your GitLab CI server using +`ssh -A`. ```bash # Manual installation -- cgit v1.2.1 From 524381dcf0ab981e070020adc40553bdbc1f3d69 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 21 Sep 2015 21:10:57 -0400 Subject: Add note about automatically re-enabling CI during migrate task [ci skip] --- doc/migrate_ci_to_ce/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 735862b31c4..c5eb4534c70 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -214,6 +214,9 @@ GitLab CE or EE database: This task will take some time. +This migration task automatically re-enables the CI setting that you +[disabled earlier](#2-prevent-ci-usage-during-the-migration-process). + #### 9. Start GitLab You can start GitLab CE (or EE) now and see if everything is working: -- cgit v1.2.1 From 156ace47783097e05f855a159f6d6f43d1ec4ce0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 22 Sep 2015 09:45:52 +0200 Subject: Add missing rake and change nginx config for CI migration --- doc/migrate_ci_to_ce/README.md | 16 ++-------------- lib/support/nginx/gitlab_ci | 12 ------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index c5eb4534c70..1f050e2def2 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -62,7 +62,7 @@ database configuration files: # Manual installation cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production + sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production # Omnibus installation sudo gitlab-ci-rake backup:create @@ -72,7 +72,7 @@ database configuration files: # Manual installation cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 + sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 # Omnibus installation sudo gitlab-ci-rake backup:create MYSQL_TO_POSTGRESQL=1 @@ -262,18 +262,6 @@ 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; diff --git a/lib/support/nginx/gitlab_ci b/lib/support/nginx/gitlab_ci index ce179d6f599..bf05edfd780 100644 --- a/lib/support/nginx/gitlab_ci +++ b/lib/support/nginx/gitlab_ci @@ -18,18 +18,6 @@ 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 8be6c74ac3e44141df2c3380ca2ce6ebaba137be Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Tue, 22 Sep 2015 11:00:11 +0200 Subject: documentation on ssl verification in web hooks --- doc/web_hooks/ssl.png | Bin 0 -> 77165 bytes doc/web_hooks/web_hooks.md | 13 +++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 doc/web_hooks/ssl.png diff --git a/doc/web_hooks/ssl.png b/doc/web_hooks/ssl.png new file mode 100644 index 00000000000..698f1a0f64a Binary files /dev/null and b/doc/web_hooks/ssl.png differ diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index f4701bb6db2..a0280e1c735 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -6,7 +6,12 @@ You can configure web hooks to listen for specific events like pushes, issues or Web hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. -If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates. +## SSL Verification + +Web hooks do SSL verification by default. +You can turn this off in the web hook settings in your GitLab projects. + +![SSL Verification](ssl.png) ## Push events @@ -34,7 +39,7 @@ X-Gitlab-Event: Push Hook "name": "Diaspora", "url": "git@example.com:mike/diasporadiaspora.git", "description": "", - "homepage": "http://example.com/mike/diaspora", + "homepage": "http://example.com/mike/diaspora", "git_http_url":"http://example.com/mike/diaspora.git", "git_ssh_url":"git@example.com:mike/diaspora.git", "visibility_level":0 @@ -513,8 +518,8 @@ server.mount_proc '/' do |req, res| puts req.body end -trap 'INT' do - server.shutdown +trap 'INT' do + server.shutdown end server.start ``` -- cgit v1.2.1 From 531c208e62da1936a9f7ca30e6a88dd30a85c899 Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Tue, 22 Sep 2015 09:12:06 +0000 Subject: update documentation on web hooks by suggestion [ci skip] --- doc/web_hooks/web_hooks.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index a0280e1c735..c185ccfcac3 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -8,7 +8,10 @@ Web hooks can be used to update an external issue tracker, trigger CI builds, up ## SSL Verification -Web hooks do SSL verification by default. +By default, the SSL certificate of the webhook endpoint is verified based on +an internal list of Certificate Authorities, +which means the certificate cannot be self-signed. + You can turn this off in the web hook settings in your GitLab projects. ![SSL Verification](ssl.png) @@ -534,4 +537,4 @@ When you press 'Test Hook' in GitLab, you should see something like this in the {"before":"077a85dd266e6f3573ef7e9ef8ce3343ad659c4e","after":"95cd4a99e93bc4bbabacfa2cd10e6725b1403c60",} example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0 - -> / -``` +``` \ No newline at end of file -- cgit v1.2.1 From f8fb5f14666a2653a5efe6f0de17eae413e13339 Mon Sep 17 00:00:00 2001 From: Ferenc Kovacs Date: Tue, 22 Sep 2015 13:22:18 +0200 Subject: Update CHANGELOG 8.0.0 has been released --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8e516322a6a..b35869dcf67 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ Please view this file on the master branch, on stable branches it's out of date. -v 8.0.0 (unreleased) +v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) -- cgit v1.2.1 From 67b38c8d715176271076e054d31c14ecc3254bc9 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:33:02 +0200 Subject: Update/improve CI migration docs for 8.0.1 --- doc/migrate_ci_to_ce/README.md | 336 ++++++++++++++++++----------------------- 1 file changed, 147 insertions(+), 189 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 1f050e2def2..c2df33a22e0 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -7,230 +7,198 @@ into the CE and EE applications. This guide will detail the process of migrating your CI installation and data into your GitLab CE or EE installation. -### Before we begin +We recommend that you read through the entire migration process in this +document before beginning. -**You need to have a working installation of GitLab CI version 8.0 to perform -this migration. The older versions are not supported and will most likely break -this migration procedure.** +### Overview -This migration cannot be performed online and takes a significant amount of -time. Make sure to plan ahead. +In this document we assume you have a GitLab server and a GitLab CI server. It +does not matter if these are the same machine. -If you are running a version of GitLab CI prior to 8.0 please follow the -appropriate [update guide](https://gitlab.com/gitlab-org/gitlab-ci/tree/master/doc/update/) -before proceeding. +The migration consists of three parts: updating GitLab and GitLab CI, moving +data, and redirecting traffic. -The migration is divided into four parts and covers both manual and Omnibus -installations: +Please note that CI builds triggered on your GitLab server in the time between +updating to 8.0 and finishing the migration will be lost. -1. [GitLab CI](#part-i-gitlab-ci) -1. [Gitlab CE (or EE)](#part-ii-gitlab-ce-or-ee) -1. [Nginx configuration](#part-iii-nginx-configuration) -1. [Finishing Up](#part-iv-finishing-up) +### Before upgrading -### Part I: GitLab CI +- (1) Make sure that the backup script on both servers can connect to the database. -#### 1. Stop GitLab CI - - # Manual installation - sudo service gitlab_ci stop - - # Omnibus installation - sudo gitlab-ctl stop ci-unicorn ci-sidekiq - -#### 2. Create a backup - -The migration procedure modifies the structure of the CI database. If something -goes wrong, you will not be able to revert to a previous version without a -backup. - -If your GitLab CI installation uses **MySQL** and your GitLab CE (or EE) -installation uses **PostgreSQL** you'll need to convert the CI database by -setting a `MYSQL_TO_POSTGRESQL` flag. - -If you use the Omnibus package you most likely use **PostgreSQL** on both GitLab -CE (or EE) and CI. - -You can check which database each install is using by viewing their -database configuration files: - - cat /home/gitlab_ci/gitlab-ci/config/database.yml - cat /home/git/gitlab/config/database.yml - -- If both applications use the same database `adapter`, create the backup with - this command: - - # Manual installation - cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production - - # Omnibus installation - sudo gitlab-ci-rake backup:create - -- If CI uses MySQL, and CE (or EE) uses PostgreSQL, create the backup with this - command (note the `MYSQL_TO_POSTGRESQL` flag): - - # Manual installation - cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production MYSQL_TO_POSTGRESQL=1 - - # Omnibus installation - sudo gitlab-ci-rake backup:create MYSQL_TO_POSTGRESQL=1 - -#### 3. Remove cronjob - -**Note:** This step is only required for **manual installations**. Omnibus users -can [skip to the next step](#part-ii-gitlab-ce-or-ee). - - # Manual installation - cd /home/gitlab_ci/gitlab-ci - sudo -u gitlab_ci -H bundle exec whenever --clear-crontab - -### Part II: GitLab CE (or EE) - -#### 1. Ensure GitLab is updated - -Your GitLab CE or EE installation **must be version 8.0**. If it's not, follow -the [update guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.14-to-8.0.md) -before proceeding. - -If you use the Omnibus packages simply run `apt-get upgrade` to install the -latest version. - -#### 2. Prevent CI usage during the migration process - -As an administrator, go to **Admin Area** -> **Settings**, and under **Continuous -Integration** uncheck **Disable to prevent CI usage until rake ci:migrate is run -(8.0 only)**. - -This will disable the CI integration and prevent users from creating CI projects -until the migration process is completed. - -#### 3. Stop GitLab - -Before you can migrate data you need to stop the GitLab service first: - - # Manual installation - sudo service gitlab stop - - # Omnibus installation - sudo gitlab-ctl stop unicorn sidekiq +``` +# CI server +# Omnibus +sudo gitlab-ci-rake backup:create -#### 4. Create a backup +# Source +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production +``` -This migration poses a **significant risk** of breaking your GitLab -installation. Create a backup before proceeding: +``` +# GitLab server +# Omnibus +sudo gitlab-rake gitlab:backup:create SKIP=repositories,uploads - # Manual installation - cd /home/git/gitlab - sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=repositories,uploads +``` - # Omnibus installation - sudo gitlab-rake gitlab:backup:create +If this fails you need to fix it before upgrading to 8.0. Also see +https://about.gitlab.com/getting-help/ -It's possible to speed up backup creation by skipping repositories and uploads: +- (2) Check what databases you use on your GitLab server and your CI server. + Look for the 'adapter:' line. If your CI server and your GitLab server use +the same database adapter no special care is needed. If your CI server uses +MySQL and your GitLab server uses PostgreSQL you need to pass a special option +during the 'Moving data' part. **If your CI server uses PostgreSQL and your +GitLab server uses MySQL you cannot migrate your CI data to GitLab 8.0.*** - # Manual installation - cd /home/git/gitlab - sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=repositories,uploads +- (3) Decide where to store CI build traces on GitLab server. GitLab CI uses + files on disk to store CI build traces. The default path for these build +traces is `/var/opt/gitlab/gitlab-ci/build` (Omnibus) or +`/home/git/gitlab/builds` (Source). If you are storing your repository data in +a special location, or if you are using NFS, you should make sure that you +store build traces on the same storage as your Git repositories. - # Omnibus installation - sudo gitlab-rake gitlab:backup:create SKIP=repositories,uploads +``` +# CI server +# Omnibus +sudo gitlab-ci-rake env:info -#### 5. Copy secret tokens from CI +# Source +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec rake env:info RAILS_ENV=production +``` -The `secrets.yml` file stores encryption keys for secure variables. +``` +# GitLab server +# Omnibus +sudo gitlab-rake gitlab:env:info -- **Manual installations** need to copy the contents of GitLab CI's - `config/secrets.yml` file to the same file in GitLab CE: +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +``` - ```bash - # Manual installation - 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 - ``` +### Upgrading -- **Omnibus installations** where GitLab CI and CE (or EE) are on the same - server don't need to do anything further, because the secrets are stored in - `/etc/gitlab/gitlab-secrets.json`. +From this point on, GitLab CI will be unavailable for your end users. -- **Omnibus installations** where GitLab CI is on a different server than CE (or - EE) will need to: - 1. On the CI server, copy the `db_key_base` value from - `/etc/gitlab/gitlab-secrets.json` - 1. On the CE (or EE) server, add `gitlab_ci['db_key_base'] = - "VALUE_FROM_ABOVE"` to the `/etc/gitlab/gitlab.rb` file and run `sudo - gitlab-ctl reconfigure` +- (1) First upgrade your GitLab server to version 8.0: +https://about.gitlab.com/update/ +- (2) After you update, go to the admin panel and temporarily disable CI. As + an administrator, go to **Admin Area** -> **Settings**, and under +**Continuous Integration** uncheck **Disable to prevent CI usage until rake +ci:migrate is run (8.0 only)**. +- (3) If you want to use custom CI settings (e.g. change where builds are + stored), please update `/etc/gitlab/gitlab.rb` (Omnibus) or +`/home/git/gitlab/config/gitlab.yml` (Source). +- (4) Now upgrade GitLab CI to version 8.0. If you are using Omnibus packages, + this may have already happened when you upgraded GitLab to 8.0. -#### 6. New configuration options for `gitlab.yml` +- (5) Disable GitLab CI after upgrading to 8.0. -**Note:** This step is only required for **manual installations**. Omnibus users -can [skip to the next step](#7-copy-backup-from-gitlab-ci). +``` +# CI server +# Omnibus +sudo gitlab-ctl stop ci-unicorn +sudo gitlab-ctl stop ci-sidekiq + +# Source +sudo service gitlab_ci stop +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec whenever --clear-crontab +``` -There are new configuration options available for `gitlab.yml`. View them with -the command below and apply them manually to your current `gitlab.yml`: +### Moving data - git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example +- (1) Move the database encryption key from your CI server to your GitLab server. -The new options include configuration settings for GitLab CI. +``` +# CI server +# Omnibus +sudo gitlab-ci-rake backup:show_secrets -#### 7. Copy backup from GitLab CI +# Source +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec rake backup:show_secrets RAILS_ENV=production +``` -```bash -# Manual installation -sudo cp -v /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups -sudo chown git:git /home/git/gitlab/tmp/backups/*_gitlab_ci_backup.tar +- (2) Create your final CI data export. If you are converting from MySQL to + PostgreSQL, add ` MYSQL_TO_POSTGRESQL=1` to the end of the rake command. When +the command finishes it will print the path to your data export archive; you +will need this file later. -# Omnibus installation -sudo cp -v /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar /var/opt/gitlab/backups/ -sudo chown git:git /var/opt/gitlab/backups/*_gitlab_ci_backup.tar ``` +# CI server +# Omnibus +sudo gitlab-ci-rake backup:create -If moving across the servers you can use `scp`. -However, this requires you to provide an authorized key or password to login to -the GitLab CE (or EE) server from the CI server. You can try to use ssh-agent -from your local machine to have that: login to your GitLab CI server using -`ssh -A`. +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake backup:create RAILS_ENV=production +``` -```bash -# Manual installation -scp /home/gitlab_ci/gitlab-ci/tmp/backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/home/git/gitlab/tmp/backup +- (3) Copy your CI data archive to your GitLab server. If you were running + GitLab and GitLab CI on the same server you can skip this step. There are +many ways to do this, below we use SSH agent forwarding and 'scp', which will +be easy and fast for most setups. You can also copy the data archive first from +the CI server to your laptop and then from your laptop to the GitLab server. -# Omnibus installation -scp /var/opt/gitlab/ci-backups/*_gitlab_ci_backup.tar root@gitlab.example.com:/var/opt/gitlab/backups/ +``` +# Start from your laptop +ssh -A ci_admin@ci_server.example +# Now on the CI server +scp /path/to/12345_gitlab_ci_backup.tar gitlab_admin@gitlab_server.example:~ ``` -#### 8. Import GitLab CI backup +- (4) Make the CI data archive discoverable for GitLab. We assume below that + you store backups in the default path, adjust the command if necessary. -Now you'll import the GitLab CI database dump that you created earlier into the -GitLab CE or EE database: +``` +# GitLab server +# Omnibus +sudo mv /path/to/12345_gitlab_ci_backup.tar /var/opt/gitlab/backups/ - # Manual installation - sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production +# Source +sudo mv /path/to/12345_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups/ +``` - # Omnibus installation - sudo gitlab-rake ci:migrate +- (5) Import the CI data into GitLab. -This task will take some time. +``` +# GitLab server +# Omnibus +sudo gitlab-rake ci:migrate -This migration task automatically re-enables the CI setting that you -[disabled earlier](#2-prevent-ci-usage-during-the-migration-process). +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production +``` -#### 9. Start GitLab +- (6) Restart GitLab -You can start GitLab CE (or EE) now and see if everything is working: +``` +# GitLab server +# Omnibus +sudo gitlab-ctl hup unicorn +sudo gitlab-ctl restart sidekiq - # Manual installation - sudo service gitlab start +# Source +sudo service gitlab reload +``` - # Omnibus installation - sudo gitlab-ctl restart unicorn sidekiq +### Redirecting traffic -### Part III: Nginx configuration +If you were running GitLab CI with Omnibus packages and you were using the +internal NGINX configuration your CI service should now be available both at +`ci.example.com` (the old address) and `gitlab.example.com/ci`. You are done! -This section is only required for **manual installations**. Omnibus users can -[skip to the final step](#part-iv-finishing-up). +If you installed GitLab CI from source we now need to configure a redirect in +NGINX so that existing CI runners can keep using the old CI server address, and +so that existing links to your CI server keep working. #### 1. Update Nginx configuration @@ -296,16 +264,6 @@ You should also make sure that you can: sudo /etc/init.d/nginx restart -### Part IV: Finishing Up - -If everything went well you should be able to access your migrated CI install by -visiting `https://gitlab.example.com/ci/`. If you visit the old GitLab CI -address, you should be redirected to the new one. - -**Enjoy!** - -### Troubleshooting - #### Restore from backup If something went wrong and you need to restore a backup, consult the [Backup -- cgit v1.2.1 From c3805c1830e203c6782f06502d2eebf05e9f5745 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:40:44 +0200 Subject: More text outside code blocks --- doc/migrate_ci_to_ce/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index c2df33a22e0..f74cbee9d07 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -35,6 +35,8 @@ cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production ``` +Also check on your GitLab server. + ``` # GitLab server # Omnibus -- cgit v1.2.1 From 1afaf5f7fb3074bfc698dca6d598ae3e0d73b21d Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:42:47 +0200 Subject: Fix ordering of paragraphs and code blocks --- doc/migrate_ci_to_ce/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index f74cbee9d07..83fd447ff07 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -55,14 +55,7 @@ https://about.gitlab.com/getting-help/ the same database adapter no special care is needed. If your CI server uses MySQL and your GitLab server uses PostgreSQL you need to pass a special option during the 'Moving data' part. **If your CI server uses PostgreSQL and your -GitLab server uses MySQL you cannot migrate your CI data to GitLab 8.0.*** - -- (3) Decide where to store CI build traces on GitLab server. GitLab CI uses - files on disk to store CI build traces. The default path for these build -traces is `/var/opt/gitlab/gitlab-ci/build` (Omnibus) or -`/home/git/gitlab/builds` (Source). If you are storing your repository data in -a special location, or if you are using NFS, you should make sure that you -store build traces on the same storage as your Git repositories. +GitLab server uses MySQL you cannot migrate your CI data to GitLab 8.0.** ``` # CI server @@ -84,6 +77,13 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production ``` +- (3) Decide where to store CI build traces on GitLab server. GitLab CI uses + files on disk to store CI build traces. The default path for these build +traces is `/var/opt/gitlab/gitlab-ci/build` (Omnibus) or +`/home/git/gitlab/builds` (Source). If you are storing your repository data in +a special location, or if you are using NFS, you should make sure that you +store build traces on the same storage as your Git repositories. + ### Upgrading From this point on, GitLab CI will be unavailable for your end users. -- cgit v1.2.1 From 23fa5d7d9e0ccb3663f4c714efa6c48593dbd449 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:44:43 +0200 Subject: Fix typo --- doc/migrate_ci_to_ce/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 83fd447ff07..ab69f0fde7e 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -79,7 +79,7 @@ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production - (3) Decide where to store CI build traces on GitLab server. GitLab CI uses files on disk to store CI build traces. The default path for these build -traces is `/var/opt/gitlab/gitlab-ci/build` (Omnibus) or +traces is `/var/opt/gitlab/gitlab-ci/builds` (Omnibus) or `/home/git/gitlab/builds` (Source). If you are storing your repository data in a special location, or if you are using NFS, you should make sure that you store build traces on the same storage as your Git repositories. -- cgit v1.2.1 From 29bb3b52156b41cca9ef6f6f729d66410f47f8ec Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:52:53 +0200 Subject: More explanation about moving DB secrets --- doc/migrate_ci_to_ce/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index ab69f0fde7e..1cf23649f6f 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -116,7 +116,11 @@ sudo -u gitlab_ci -H bundle exec whenever --clear-crontab ### Moving data -- (1) Move the database encryption key from your CI server to your GitLab server. +- (1) Move the database encryption key from your CI server to your GitLab + server. The command below will show you what you need to copy-paste to your +GitLab server. On Omnibus GitLab servers you will have to add a line to +`/etc/gitlab/gitlab.rb`. On GitLab servers installed from source you will have +to replace the contents of `/home/git/gitlab/config/secrets.yml`. ``` # CI server -- cgit v1.2.1 From 917c9ea9278d3f3d3ae925351911e8ecc5c55a1f Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:59:22 +0200 Subject: Migration only works 8.0 -> 8.0 --- doc/migrate_ci_to_ce/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 1cf23649f6f..4070d15808c 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -5,7 +5,9 @@ Edition (EE), GitLab CI is no longer its own application, but is instead built into the CE and EE applications. This guide will detail the process of migrating your CI installation and data -into your GitLab CE or EE installation. +into your GitLab CE or EE installation. **You can only migrate CI data from +GitLab CI 8.0 to GitLab 8.0; migrating between other versions (e.g.7.14 to 8.1) +is not possible.** We recommend that you read through the entire migration process in this document before beginning. -- cgit v1.2.1 From e72ba928f98358d090fad68ae3794f494ad971cd Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 13:59:40 +0200 Subject: Downtime expectations --- doc/migrate_ci_to_ce/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 4070d15808c..838832d5cd4 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -21,7 +21,10 @@ The migration consists of three parts: updating GitLab and GitLab CI, moving data, and redirecting traffic. Please note that CI builds triggered on your GitLab server in the time between -updating to 8.0 and finishing the migration will be lost. +updating to 8.0 and finishing the migration will be lost. Your GitLab server +can be online for most of the procedure; the only GitLab downtime (if any) is +during the upgrade to 8.0. Your CI service will be offline from the moment you +upgrade to 8.0 until you finish the migration procedure. ### Before upgrading -- cgit v1.2.1 From f0f5be82b05116a1f916ec7a407bf3ea0a195f22 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 14:02:36 +0200 Subject: Add global numbering --- doc/migrate_ci_to_ce/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 838832d5cd4..6ac79d36884 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -89,7 +89,7 @@ traces is `/var/opt/gitlab/gitlab-ci/builds` (Omnibus) or a special location, or if you are using NFS, you should make sure that you store build traces on the same storage as your Git repositories. -### Upgrading +### I. Upgrading From this point on, GitLab CI will be unavailable for your end users. @@ -119,7 +119,7 @@ cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec whenever --clear-crontab ``` -### Moving data +### II. Moving data - (1) Move the database encryption key from your CI server to your GitLab server. The command below will show you what you need to copy-paste to your @@ -201,7 +201,7 @@ sudo gitlab-ctl restart sidekiq sudo service gitlab reload ``` -### Redirecting traffic +### III. Redirecting traffic If you were running GitLab CI with Omnibus packages and you were using the internal NGINX configuration your CI service should now be available both at -- cgit v1.2.1 From 82d95c461271237eb62eb55846ade3145aa1a9c0 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 14:49:39 +0200 Subject: Use headings to mark steps --- doc/migrate_ci_to_ce/README.md | 70 ++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 6ac79d36884..534b1fdb2db 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -28,7 +28,9 @@ upgrade to 8.0 until you finish the migration procedure. ### Before upgrading -- (1) Make sure that the backup script on both servers can connect to the database. +#### 1. Verify that backups work + +Make sure that the backup script on both servers can connect to the database. ``` # CI server @@ -55,7 +57,9 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=r If this fails you need to fix it before upgrading to 8.0. Also see https://about.gitlab.com/getting-help/ -- (2) Check what databases you use on your GitLab server and your CI server. +#### 2. Check source and target database types + +Check what databases you use on your GitLab server and your CI server. Look for the 'adapter:' line. If your CI server and your GitLab server use the same database adapter no special care is needed. If your CI server uses MySQL and your GitLab server uses PostgreSQL you need to pass a special option @@ -82,7 +86,9 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production ``` -- (3) Decide where to store CI build traces on GitLab server. GitLab CI uses +#### 3. Storage planning + +Decide where to store CI build traces on GitLab server. GitLab CI uses files on disk to store CI build traces. The default path for these build traces is `/var/opt/gitlab/gitlab-ci/builds` (Omnibus) or `/home/git/gitlab/builds` (Source). If you are storing your repository data in @@ -93,19 +99,32 @@ store build traces on the same storage as your Git repositories. From this point on, GitLab CI will be unavailable for your end users. -- (1) First upgrade your GitLab server to version 8.0: +#### 1. Upgrade GitLab to 8.0 + +First upgrade your GitLab server to version 8.0: https://about.gitlab.com/update/ -- (2) After you update, go to the admin panel and temporarily disable CI. As + +#### 2. Disable CI on the GitLab server during the migration + +After you update, go to the admin panel and temporarily disable CI. As an administrator, go to **Admin Area** -> **Settings**, and under **Continuous Integration** uncheck **Disable to prevent CI usage until rake ci:migrate is run (8.0 only)**. -- (3) If you want to use custom CI settings (e.g. change where builds are + +#### 3. CI settings are now in GitLab + +If you want to use custom CI settings (e.g. change where builds are stored), please update `/etc/gitlab/gitlab.rb` (Omnibus) or `/home/git/gitlab/config/gitlab.yml` (Source). -- (4) Now upgrade GitLab CI to version 8.0. If you are using Omnibus packages, + +#### 4. Upgrade GitLab CI to 8.0 + +Now upgrade GitLab CI to version 8.0. If you are using Omnibus packages, this may have already happened when you upgraded GitLab to 8.0. -- (5) Disable GitLab CI after upgrading to 8.0. +#### 5. Disable GitLab CI on the CI server + +Disable GitLab CI after upgrading to 8.0. ``` # CI server @@ -121,7 +140,9 @@ sudo -u gitlab_ci -H bundle exec whenever --clear-crontab ### II. Moving data -- (1) Move the database encryption key from your CI server to your GitLab +#### 1. Database encryption key + +Move the database encryption key from your CI server to your GitLab server. The command below will show you what you need to copy-paste to your GitLab server. On Omnibus GitLab servers you will have to add a line to `/etc/gitlab/gitlab.rb`. On GitLab servers installed from source you will have @@ -137,7 +158,9 @@ cd /home/gitlab_ci/gitlab-ci sudo -u gitlab_ci -H bundle exec rake backup:show_secrets RAILS_ENV=production ``` -- (2) Create your final CI data export. If you are converting from MySQL to +#### 2. SQL data and build traces + +Create your final CI data export. If you are converting from MySQL to PostgreSQL, add ` MYSQL_TO_POSTGRESQL=1` to the end of the rake command. When the command finishes it will print the path to your data export archive; you will need this file later. @@ -152,11 +175,15 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake backup:create RAILS_ENV=production ``` -- (3) Copy your CI data archive to your GitLab server. If you were running - GitLab and GitLab CI on the same server you can skip this step. There are -many ways to do this, below we use SSH agent forwarding and 'scp', which will -be easy and fast for most setups. You can also copy the data archive first from -the CI server to your laptop and then from your laptop to the GitLab server. +#### 3. Copy data to the GitLab server + +If you were running GitLab and GitLab CI on the same server you can skip this +step. + +Copy your CI data archive to your GitLab server. There are many ways to do +this, below we use SSH agent forwarding and 'scp', which will be easy and fast +for most setups. You can also copy the data archive first from the CI server to +your laptop and then from your laptop to the GitLab server. ``` # Start from your laptop @@ -165,8 +192,10 @@ ssh -A ci_admin@ci_server.example scp /path/to/12345_gitlab_ci_backup.tar gitlab_admin@gitlab_server.example:~ ``` -- (4) Make the CI data archive discoverable for GitLab. We assume below that - you store backups in the default path, adjust the command if necessary. +#### 4. Move data to the GitLab backups folder + +Make the CI data archive discoverable for GitLab. We assume below that you +store backups in the default path, adjust the command if necessary. ``` # GitLab server @@ -177,7 +206,10 @@ sudo mv /path/to/12345_gitlab_ci_backup.tar /var/opt/gitlab/backups/ sudo mv /path/to/12345_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups/ ``` -- (5) Import the CI data into GitLab. +#### 5. Import the CI data into GitLab. + +This step will delete any existing CI data on your GitLab server. There should +be no CI data yet because you turned CI on the GitLab server off earlier. ``` # GitLab server @@ -189,7 +221,7 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production ``` -- (6) Restart GitLab +#### 6. Restart GitLab ``` # GitLab server -- cgit v1.2.1 From 3e926c6eb030eafb10c66c4aade66237e1a41d7a Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 14:54:26 +0200 Subject: Emphasize when Omnibus installs are done --- doc/migrate_ci_to_ce/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 534b1fdb2db..31b572ee8ba 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -237,7 +237,7 @@ sudo service gitlab reload If you were running GitLab CI with Omnibus packages and you were using the internal NGINX configuration your CI service should now be available both at -`ci.example.com` (the old address) and `gitlab.example.com/ci`. You are done! +`ci.example.com` (the old address) and `gitlab.example.com/ci`. **You are done!** If you installed GitLab CI from source we now need to configure a redirect in NGINX so that existing CI runners can keep using the old CI server address, and -- cgit v1.2.1 From d2ab95d1f3bea31e83241ac57ba934eb93c4eb14 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 22 Sep 2015 15:26:40 +0200 Subject: Make code block comments more verbose --- doc/migrate_ci_to_ce/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 31b572ee8ba..208aab42b56 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -33,7 +33,7 @@ upgrade to 8.0 until you finish the migration procedure. Make sure that the backup script on both servers can connect to the database. ``` -# CI server +# On your CI server: # Omnibus sudo gitlab-ci-rake backup:create @@ -45,7 +45,7 @@ sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production Also check on your GitLab server. ``` -# GitLab server +# On your GitLab server: # Omnibus sudo gitlab-rake gitlab:backup:create SKIP=repositories,uploads @@ -67,7 +67,7 @@ during the 'Moving data' part. **If your CI server uses PostgreSQL and your GitLab server uses MySQL you cannot migrate your CI data to GitLab 8.0.** ``` -# CI server +# On your CI server: # Omnibus sudo gitlab-ci-rake env:info @@ -77,7 +77,7 @@ sudo -u gitlab_ci -H bundle exec rake env:info RAILS_ENV=production ``` ``` -# GitLab server +# On your GitLab server: # Omnibus sudo gitlab-rake gitlab:env:info @@ -127,7 +127,7 @@ Now upgrade GitLab CI to version 8.0. If you are using Omnibus packages, Disable GitLab CI after upgrading to 8.0. ``` -# CI server +# On your CI server: # Omnibus sudo gitlab-ctl stop ci-unicorn sudo gitlab-ctl stop ci-sidekiq @@ -149,7 +149,7 @@ GitLab server. On Omnibus GitLab servers you will have to add a line to to replace the contents of `/home/git/gitlab/config/secrets.yml`. ``` -# CI server +# On your CI server: # Omnibus sudo gitlab-ci-rake backup:show_secrets @@ -166,7 +166,7 @@ the command finishes it will print the path to your data export archive; you will need this file later. ``` -# CI server +# On your CI server: # Omnibus sudo gitlab-ci-rake backup:create @@ -198,7 +198,7 @@ Make the CI data archive discoverable for GitLab. We assume below that you store backups in the default path, adjust the command if necessary. ``` -# GitLab server +# On your GitLab server: # Omnibus sudo mv /path/to/12345_gitlab_ci_backup.tar /var/opt/gitlab/backups/ @@ -212,7 +212,7 @@ This step will delete any existing CI data on your GitLab server. There should be no CI data yet because you turned CI on the GitLab server off earlier. ``` -# GitLab server +# On your GitLab server: # Omnibus sudo gitlab-rake ci:migrate @@ -224,7 +224,7 @@ sudo -u git -H bundle exec rake ci:migrate RAILS_ENV=production #### 6. Restart GitLab ``` -# GitLab server +# On your GitLab server: # Omnibus sudo gitlab-ctl hup unicorn sudo gitlab-ctl restart sidekiq -- cgit v1.2.1 From a1b60003f01611729b9c77d9ed90b588e8771343 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 22 Sep 2015 15:36:40 +0200 Subject: Mention Reply by email to installation doc. --- doc/install/installation.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 3b074fc8467..5887891c1ab 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -474,9 +474,22 @@ Using a self-signed certificate is discouraged but if you must use it follow the ``` 1. In the `config.yml` of gitlab-shell set `self_signed_cert` to `true`. -### Additional Markup Styles +### Enable Reply by email -Apart from the always supported markdown style there are other rich text files that GitLab can display. But you might have to install a dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information. +See the ["Reply by email" documentation](../incoming_email/README.md) for more information on how to set this up. + +### LDAP Authentication + +You can configure LDAP authentication in `config/gitlab.yml`. Please restart GitLab after editing this file. + +### 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 ### Custom Redis Connection @@ -502,15 +515,6 @@ If you are running SSH on a non-standard port, you must change the GitLab user's You also need to change the corresponding options (e.g. `ssh_user`, `ssh_host`, `admin_uri`) in the `config\gitlab.yml` file. -### LDAP Authentication - -You can configure LDAP authentication in `config/gitlab.yml`. Please restart GitLab after editing this file. - -### Using Custom Omniauth Providers - -See the [omniauth integration document](../integration/omniauth.md) - -### Build your projects +### Additional Markup Styles -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 +Apart from the always supported markdown style there are other rich text files that GitLab can display. But you might have to install a dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information. -- cgit v1.2.1 From dabbee65d44a3cdd7e78c48176d3f5def51da1b7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 22 Sep 2015 16:08:35 +0200 Subject: Fix Slack notification URL and remove the usage of Ci::RoutesHelper --- app/helpers/ci/routes_helper.rb | 29 ---------------------- app/models/project_services/ci/hip_chat_message.rb | 8 +++--- app/models/project_services/ci/slack_message.rb | 10 +++++--- app/models/project_services/gitlab_ci_service.rb | 2 +- 4 files changed, 12 insertions(+), 37 deletions(-) delete mode 100644 app/helpers/ci/routes_helper.rb diff --git a/app/helpers/ci/routes_helper.rb b/app/helpers/ci/routes_helper.rb deleted file mode 100644 index 42cd54b064f..00000000000 --- a/app/helpers/ci/routes_helper.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Ci - module RoutesHelper - class Base - include Gitlab::Application.routes.url_helpers - - def default_url_options - { - host: Settings.gitlab['host'], - protocol: Settings.gitlab['https'] ? "https" : "http", - port: Settings.gitlab['port'] - } - end - end - - def url_helpers - @url_helpers ||= Base.new - end - - def self.method_missing(method, *args, &block) - @url_helpers ||= 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/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb index 58825fe066c..25c72033eac 100644 --- a/app/models/project_services/ci/hip_chat_message.rb +++ b/app/models/project_services/ci/hip_chat_message.rb @@ -1,5 +1,7 @@ module Ci class HipChatMessage + include Gitlab::Application.routes.url_helpers + attr_reader :build def initialize(build) @@ -8,13 +10,13 @@ module Ci def to_s lines = Array.new - lines.push("#{project.name} - ") + 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}
    ") + lines.push("Build '#{first_build.name}' ##{first_build.id}
    ") end lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}
    ") diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb index 491ace50111..757b1961143 100644 --- a/app/models/project_services/ci/slack_message.rb +++ b/app/models/project_services/ci/slack_message.rb @@ -2,6 +2,8 @@ require 'slack-notifier' module Ci class SlackMessage + include Gitlab::Application.routes.url_helpers + def initialize(commit) @commit = commit end @@ -27,7 +29,7 @@ module Ci 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)." + value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)." } end end @@ -44,12 +46,12 @@ module Ci attr_reader :commit def attachment_message - out = "<#{Ci::RoutesHelper.ci_project_url(project)}|#{project_name}>: " + out = "<#{ci_project_url(project)}|#{project_name}>: " if commit.matrix? - out << "Commit <#{Ci::RoutesHelper.ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> " + out << "Commit <#{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_path(project, build)}|\##{build.id}> " + out << "Build <#{ci_project_build_url(project, build)}|\##{build.id}> " end out << "(<#{commit_sha_link}|#{commit.short_sha}>) " out << "of <#{commit_ref_link}|#{commit.ref}> " diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 9e2b3bcd873..388a49e791d 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -88,7 +88,7 @@ class GitlabCiService < CiService def build_page(sha, ref) if project.gitlab_ci_project.present? - Ci::RoutesHelper.ci_project_ref_commits_path(project.gitlab_ci_project, ref, sha) + ci_project_ref_commits_url(project.gitlab_ci_project, ref, sha) end end -- cgit v1.2.1 From da20553ab6acb1e353319d6b2f5e2bc60bef3d4f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 22 Sep 2015 16:51:16 +0200 Subject: Fix CI --- app/models/project_services/gitlab_ci_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 388a49e791d..64c1d5cbf60 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -19,6 +19,8 @@ # class GitlabCiService < CiService + include Gitlab::Application.routes.url_helpers + prop_accessor :token after_save :compose_service_hook, if: :activated? -- cgit v1.2.1 From e2be419c9587b21b53c2d426d7783f99751df741 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 22 Sep 2015 16:57:43 +0200 Subject: Add to migration on what to do if you don't want your data. --- doc/migrate_ci_to_ce/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 208aab42b56..f83ecb01e44 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -28,6 +28,14 @@ upgrade to 8.0 until you finish the migration procedure. ### Before upgrading +If you have GitLab CI installed using omnibus-gitlab packages but *you don't want to migrate your existing data*: + +```bash +mv /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds.$(date +%s) +``` + +and run `sudo gitlab-ctl reconfigure`. + #### 1. Verify that backups work Make sure that the backup script on both servers can connect to the database. -- cgit v1.2.1 From d80750b50f8f3c85c04f9aad6c3dd23003385de1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 22 Sep 2015 17:49:45 +0200 Subject: Fix error when MR source branch no longer exists. --- app/controllers/projects/merge_requests_controller.rb | 3 +-- app/views/projects/merge_requests/widget/_merged.html.haml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index c1231994f25..7574842cd43 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -258,8 +258,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @commits = @merge_request.commits @merge_request_diff = @merge_request.merge_request_diff - @source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name) - + if @merge_request.locked_long_ago? @merge_request.unlock_mr @merge_request.close diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index d22dfa085b8..f223f687def 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -20,7 +20,7 @@ The changes were merged into %span.label-branch= @merge_request.target_branch You can remove the source branch now. - = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do + = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do %i.fa.fa-times Remove Source Branch -- cgit v1.2.1 From a42f418f183a350a5a3d5eebc5223e5639e16981 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 22 Sep 2015 18:28:08 +0200 Subject: Fix GitlabCiService specs --- spec/models/project_services/gitlab_ci_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index e0da04a3f40..8cdd551a0ca 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -39,8 +39,8 @@ describe GitlabCiService do end describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")} - it { expect(@service.build_page("issue#2", 'master')).to eq("/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")} + it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")} + it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")} end describe "execute" do -- cgit v1.2.1 From 2cdd88e8ed66d96eafbcacc3778f6befad6433be Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 22 Sep 2015 12:29:29 -0400 Subject: 8.0.0 is released; add 8.0.1 entry to CHANGELOG [ci skip] --- CHANGELOG | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8e516322a6a..1cb338f16da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Please view this file on the master branch, on stable branches it's out of date. -v 8.0.0 (unreleased) +v 8.0.1 + - Improve CI migration procedure and documentation + +v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) -- cgit v1.2.1 From ef2679d0b9c0b145085725cf75203d7851fcc143 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 22 Sep 2015 11:17:36 -0700 Subject: Make commit graphs responsive to window width changes Closes #2653 --- CHANGELOG | 1 + app/views/projects/graphs/commits.html.haml | 76 +++++++++++++---------------- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1cb338f16da..dcedaa96d79 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.1 - Improve CI migration procedure and documentation + - Make commit graphs responsive to window width changes (Stan Hu) v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index bf0cac539b8..112be875b6b 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -32,61 +32,55 @@ %div %p.slead Commits per day of month - %canvas#month-chart{width: 800, height: 400} + %canvas#month-chart .row .col-md-6 %div %p.slead Commits per day hour (UTC) - %canvas#hour-chart{width: 800, height: 400} + %canvas#hour-chart .col-md-6 %div %p.slead Commits per weekday - %canvas#weekday-chart{width: 800, height: 400} + %canvas#weekday-chart :coffeescript - data = { - labels : #{@commits_per_time.keys.to_json}, - datasets : [{ - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : #{@commits_per_time.values.to_json} - }] - } + responsiveChart = (selector, data) -> + options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false } - ctx = $("#hour-chart").get(0).getContext("2d"); - new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2}) + # get selector by context + ctx = selector.get(0).getContext("2d") + # pointing parent container to make chart.js inherit its width + container = $(selector).parent() - data = { - labels : #{@commits_per_week_days.keys.to_json}, - datasets : [{ - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : #{@commits_per_week_days.values.to_json} - }] - } + generateChart = -> + selector.attr('width', $(container).width()) + new Chart(ctx).Bar(data, options) + + # enabling auto-resizing + $(window).resize( generateChart ) - ctx = $("#weekday-chart").get(0).getContext("2d"); - new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2}) + generateChart() - data = { - labels : #{@commits_per_month.keys.to_json}, - datasets : [{ - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data : #{@commits_per_month.values.to_json} - }] + chartData = (keys, values) -> + data = { + labels : keys, + datasets : [{ + fillColor : "rgba(220,220,220,0.5)", + strokeColor : "rgba(220,220,220,1)", + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data : values + }] } - ctx = $("#month-chart").get(0).getContext("2d"); - new Chart(ctx).Bar(data, {"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2}) + hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json}) + responsiveChart($('#hour-chart'), hourData) + + dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json}) + responsiveChart($('#weekday-chart'), dayData) + + monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json}) + responsiveChart($('#month-chart'), monthData) -- cgit v1.2.1 From 110e90c855681967b051a1b811c1bf9df2f38dbd Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 22 Sep 2015 22:56:49 +0300 Subject: Skip check_initd_configured_correctly on omnibus installs This was causing the task `gitlab-rake gitlab:incoming_email:check` to fail. --- lib/tasks/gitlab/check.rake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index c8222f8ce11..66f1ecf385f 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -679,6 +679,11 @@ namespace :gitlab do def check_initd_configured_correctly print "Init.d configured correctly? ... " + if omnibus_gitlab? + puts 'skipped (omnibus-gitlab has no init script)'.magenta + return + end + path = "/etc/default/gitlab" if File.exist?(path) && File.read(path).include?("mail_room_enabled=true") -- cgit v1.2.1 From 37ecae33ecae95fd2e27bf1e1f479c32e11de5f4 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 22 Sep 2015 19:56:51 +0000 Subject: Add better deprecation notice for the upgrader. Too easy to miss otherwise https://twitter.com/0x663030623472/status/646262552796594176 --- doc/update/upgrader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index 6854250dab7..fd0327686b1 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -1,4 +1,4 @@ -# GitLab Upgrader +# GitLab Upgrader (deprecated) *DEPRECATED* We recommend to [switch to the Omnibus package and repository server](https://about.gitlab.com/update/) instead of using this script. -- cgit v1.2.1 From 3c46ae1df31cdd6bc54105f6b9788582a969ed42 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 22 Sep 2015 16:25:23 -0400 Subject: Fix CI backup step in migration guide [ci skip] --- doc/migrate_ci_to_ce/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index f83ecb01e44..2725bf343ee 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -179,8 +179,8 @@ will need this file later. sudo gitlab-ci-rake backup:create # Source -cd /home/git/gitlab -sudo -u git -H bundle exec rake backup:create RAILS_ENV=production +cd /home/gitlab_ci/gitlab-ci +sudo -u gitlab_ci -H bundle exec rake backup:create RAILS_ENV=production ``` #### 3. Copy data to the GitLab server -- cgit v1.2.1 From f60eb60473a1dcfd2b874d5ebac6dca60da7c1ea Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Tue, 22 Sep 2015 16:26:59 -0500 Subject: Added ability to update or set the identity of an existing user, like the documentation said it was possible, but actually wasn't. --- lib/api/users.rb | 11 +++++++++++ spec/requests/api/users_spec.rb | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/api/users.rb b/lib/api/users.rb index 813cc379e43..a98d668e02d 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -121,6 +121,17 @@ module API User.where(username: attrs[:username]). where.not(id: user.id).count > 0 + identity_attrs = attributes_for_keys [:provider, :extern_uid] + if identity_attrs.any? + identity = user.identities.find_by(provider: identity_attrs[:provider]) + if identity + identity.update_attributes(identity_attrs) + else + identity = user.identities.build(identity_attrs) + identity.save + end + end + if user.update_attributes(attrs) present user, with: Entities::UserFull else diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f9bc63680ba..d26a300ed82 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -7,6 +7,7 @@ describe API::API, api: true do let(:admin) { create(:admin) } let(:key) { create(:key, user: user) } let(:email) { create(:email, user: user) } + let(:omniauth_user) { create(:omniauth_user) } describe "GET /users" do context "when unauthenticated" do @@ -230,6 +231,19 @@ describe API::API, api: true do expect(user.reload.username).to eq(user.username) end + it "should update user's existing identity" do + put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321' + expect(response.status).to eq(200) + expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321') + end + + it 'should update user with new identity' do + put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890' + expect(response.status).to eq(200) + expect(user.reload.identities.first.extern_uid).to eq('67890') + expect(user.reload.identities.first.provider).to eq('github') + end + it "should update admin status" do put api("/users/#{user.id}", admin), { admin: true } expect(response.status).to eq(200) -- cgit v1.2.1 From c551c81e28418c67fb398d6efb2183588c09f862 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 22 Sep 2015 18:26:41 -0400 Subject: Fix "User permissions" help page path --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 5f38086b8e3..a0ff856ebb6 100644 --- a/doc/README.md +++ b/doc/README.md @@ -51,7 +51,7 @@ ### Administrator documentation -+ [User permissions](permissions/README.md) ++ [User permissions](permissions/permissions.md) + [API](api/README.md) ## Contributor documentation -- cgit v1.2.1 From 64e0dfa5306aebdedeb65f2a6db95f4d2314f143 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 22 Sep 2015 18:26:51 -0400 Subject: Prevent double-prefixing of help page paths Prior, because the link "api/README.md" was matched twice, the first link became "help/help/api/README.md". Now we do a negative lookahead to make sure the link doesn't start with `help/`. This fix is still not ideal, see TODO note. --- app/controllers/help_controller.rb | 18 ++++++++++++++++++ app/views/help/index.html.haml | 6 +----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index ad00948da51..7283c4f4a4c 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -4,6 +4,8 @@ class HelpController < ApplicationController layout 'help' def index + @help_index = File.read(Rails.root.join('doc', 'README.md')) + prefix_help_links!(@help_index) end def show @@ -57,6 +59,22 @@ class HelpController < ApplicationController params end + # Prefix links in a Markdown document with `help/` unless they already have + # been + # + # TODO (rspeicher): This should be a pipeline filter that only gets included + # for help pages, and it should operate on the Nokogiri doc to be more robust. + # + # text - Markdown String + # + # Modifies `text` in-place + def prefix_help_links!(text) + # Match text inside a Markdown link unless it already starts with `help/` + # + # See http://rubular.com/r/nwwhzH6Z8X + text.gsub!(%r{(\]\()(?!help\/)([^\)\(]+)(\))}x, '\1help/\2\3') + end + PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact) # Taken from ActionDispatch::FileHandler diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index f492aaf4c0a..ab7ed1b5d95 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -27,11 +27,7 @@ .col-md-8 .documentation-index = preserve do - - readme_text = File.read(Rails.root.join("doc", "README.md")) - - text = readme_text.dup - - readme_text.scan(/\]\(([^(]+)\)/) { |match| text.gsub!(match.first, "help/#{match.first}") } - = markdown text - + = markdown(@help_index) .col-md-4 .panel.panel-default .panel-heading -- cgit v1.2.1 From 95f73a68fa3ff235c35217b1c72666c5bad6ce03 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 00:24:17 -0400 Subject: Simplify help path prefixing --- app/controllers/help_controller.rb | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 7283c4f4a4c..55050615473 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -5,7 +5,10 @@ class HelpController < ApplicationController def index @help_index = File.read(Rails.root.join('doc', 'README.md')) - prefix_help_links!(@help_index) + + # Prefix Markdown links with `help/` unless they already have been + # See http://rubular.com/r/nwwhzH6Z8X + @help_index.gsub!(/(\]\()(?!help\/)([^\)\(]+)(\))/, '\1help/\2\3') end def show @@ -59,22 +62,6 @@ class HelpController < ApplicationController params end - # Prefix links in a Markdown document with `help/` unless they already have - # been - # - # TODO (rspeicher): This should be a pipeline filter that only gets included - # for help pages, and it should operate on the Nokogiri doc to be more robust. - # - # text - Markdown String - # - # Modifies `text` in-place - def prefix_help_links!(text) - # Match text inside a Markdown link unless it already starts with `help/` - # - # See http://rubular.com/r/nwwhzH6Z8X - text.gsub!(%r{(\]\()(?!help\/)([^\)\(]+)(\))}x, '\1help/\2\3') - end - PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact) # Taken from ActionDispatch::FileHandler -- cgit v1.2.1 From a7b0ee3fd17d4e2afd427606b002d3a9f7c6673d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 22 Sep 2015 21:56:27 -0700 Subject: Fix cases where Markdown did not render links in activity feed HTML would be stripped in `truncate_if_block` when a comment had multiple lines and the first wasn't long enough to be truncated. The use of `node.content` would strip all HTML tags. Using `node.inner_html` retains these tags and puts the "..." in between paragraph tags. Closes #2586 --- CHANGELOG | 1 + app/helpers/gitlab_markdown_helper.rb | 2 +- spec/helpers/gitlab_markdown_helper_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1cb338f16da..fff641fcb48 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.1 + - Fix cases where Markdown did not render links in activity feed (Stan Hu) - Improve CI migration procedure and documentation v 8.0.0 diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 78bf25f55e7..153a44870f6 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -165,7 +165,7 @@ module GitlabMarkdownHelper # 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 + node.inner_html = "#{node.inner_html}..." if node.next_sibling true else truncated diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index b8101ae77ec..be0e0c747b7 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -146,4 +146,24 @@ describe GitlabMarkdownHelper do expect(random_markdown_tip).to eq 'Random tip' end end + + describe '#first_line_in_markdown' do + let(:text) { "@#{user.username}, can you look at this?\nHello world\n"} + + it 'truncates Markdown properly' do + actual = first_line_in_markdown(text, 100, project: project) + + doc = Nokogiri::HTML.parse(actual) + + # Make sure we didn't create invalid markup + expect(doc.errors).to be_empty + + # Leading user link + expect(doc.css('a').length).to eq(1) + expect(doc.css('a')[0].attr('href')).to eq user_path(user) + expect(doc.css('a')[0].text).to eq "@#{user.username}" + + expect(doc.content).to eq "@#{user.username}, can you look at this?..." + end + end end -- cgit v1.2.1 From 9103602620ec4eb527ac45954a78b3f3466e97a2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 22 Sep 2015 15:14:26 +0200 Subject: Improve UI for rendered markdown --- app/assets/stylesheets/base/mixins.scss | 72 ++++++++++++++++++++------ app/assets/stylesheets/generic/typography.scss | 8 ++- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index a2f6c3e21f4..421588f64d8 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -93,46 +93,87 @@ } h1 { - margin-top: 45px; - font-size: 2.5em; + font-size: 1.3em; + font-weight: 600; + margin: 24px 0 12px 0; + padding: 0 0 10px 0; + border-bottom: 1px solid #e7e9ed; + color: #313236; } h2 { - margin-top: 40px; - font-size: 2em; + font-size: 1.2em; + font-weight: 600; + margin: 24px 0 12px 0; + color: #313236; } h3 { - margin-top: 35px; - font-size: 1.5em; + margin: 24px 0 12px 0; + font-size: 1.25em; } h4 { - margin-top: 30px; - font-size: 1.2em; + margin: 24px 0 12px 0; + font-size: 1.1em; + } + + h5 { + margin: 24px 0 12px 0; + font-size: 1em; + } + + h6 { + margin: 24px 0 12px 0; + font-size: 0.90em; } blockquote { - color: #888; + padding: 8px 21px; + margin: 12px 0 12px; + border-left: 3px solid #e7e9ed; + } + + blockquote p { + color: #7f8fa4 !important; font-size: 15px; line-height: 1.5; } - + + p { + color:#5c5d5e; + margin:6px 0 0 0; + } + table { @extend .table; @extend .table-bordered; + margin: 12px 0 12px 0; + color: #5c5d5e; th { - background: #EEE; - } + background: #f8fafc; + } + } + + pre { + margin: 12px 0 12px 0 !important; + background-color: #f8fafc !important; + font-size: 13px !important; + color: #5b6169 !important; + line-height: 1.6em !important; } p > code { - font-size: inherit; font-weight: inherit; } + + ul { + color: #5c5d5e; + } + li { - line-height: 1.5; + line-height: 1.6em; } a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { @@ -152,6 +193,7 @@ } } + @mixin str-truncated($max_width: 82%) { display: inline-block; overflow: hidden; @@ -183,7 +225,7 @@ &.active { background: #f9f9f9; a { - font-weight: bold; + font-weight: 600; } } diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index d5f0d86a307..7a8a17ced99 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -55,6 +55,7 @@ a > code { @include md-typography; word-wrap: break-word; + padding: 7px; /* Link to current header. */ h1, h2, h3, h4, h5, h6 { @@ -83,9 +84,12 @@ a > code { } } - ul { + ul,ol { padding: 0; - margin: 0 0 9px 25px !important; + margin: 6px 0 6px 18px !important; + } + ol { + color: #5c5d5e; } } -- cgit v1.2.1 From 64ec7a3e0e7eedf960e02910f7086e6757ce5cc7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 12:21:22 +0200 Subject: Bump version and changelog Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 3 +++ VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1cb338f16da..5d28897425c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Please view this file on the master branch, on stable branches it's out of date. +v 8.1.0 (unreleased) + - + v 8.0.1 - Improve CI migration procedure and documentation diff --git a/VERSION b/VERSION index 939cbc3ea74..a2264f05f50 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.0.pre +8.1.0.pre -- cgit v1.2.1 From 21dfaa000d0117fcf70ecd0578d4431362d5c2a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 12:18:16 +0200 Subject: Show CI status on all pages where commits list is rendered Signed-off-by: Dmitriy Zaporozhets --- app/helpers/ci_status_helper.rb | 34 ++++++++++++++++++++++++++++ app/helpers/commits_helper.rb | 2 +- app/models/project.rb | 4 ++++ app/views/projects/commits/_commit.html.haml | 10 +++++++- spec/models/project_spec.rb | 10 ++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 app/helpers/ci_status_helper.rb diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb new file mode 100644 index 00000000000..18c30ddb281 --- /dev/null +++ b/app/helpers/ci_status_helper.rb @@ -0,0 +1,34 @@ +module CiStatusHelper + def ci_status_path(ci_commit) + ci_project_ref_commits_path(ci_commit.project, ci_commit.ref, ci_commit) + end + + def ci_status_icon(ci_commit) + icon_name = + case ci_commit.status + when 'success' + 'check' + when 'failed' + 'close' + when 'running', 'pending' + 'clock-o' + else + 'circle' + end + + icon(icon_name) + end + + def ci_status_color(ci_commit) + case ci_commit.status + when 'success' + 'green' + when 'failed' + 'red' + when 'running', 'pending' + 'yellow' + else + 'gray' + end + end +end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index d13d80be293..9df20c9fce5 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -135,7 +135,7 @@ module CommitsHelper # 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) diff --git a/app/models/project.rb b/app/models/project.rb index 1a5c1c978c9..72120885105 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -735,4 +735,8 @@ class Project < ActiveRecord::Base errors.add(:base, 'Failed create wiki') false end + + def ci_commit(sha) + gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci? + end end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 74f8d8b15cf..efad4cb1473 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -4,7 +4,11 @@ - notes = commit.notes - note_count = notes.user.count -= cache [project.id, commit.id, note_count] do +- ci_commit = project.ci_commit(commit.sha) +- cache_key = [project.id, commit.id, note_count] +- cache_key.push(ci_commit.status) if ci_commit + += cache(cache_key) do %li.commit.js-toggle-container .commit-row-title %strong.str-truncated @@ -13,6 +17,10 @@ %a.text-expander.js-toggle-button ... .pull-right + - if ci_commit + = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do + = ci_status_icon(ci_commit) +   = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" .notes_count diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 2fcbd5ae108..ba57e31f7fe 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -401,4 +401,14 @@ describe Project do it { should eq "http://localhost#{avatar_path}" } end end + + describe :ci_commit do + let(:project) { create :project } + let(:ci_project) { create :ci_project, gl_project: project } + let(:commit) { create :ci_commit, project: ci_project } + + before { project.create_gitlab_ci_service(active: true) } + + it { expect(project.ci_commit(commit.sha)).to eq(commit) } + end end -- cgit v1.2.1 From 3b6915d8910296296676e32129138c50bb1b0c5c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 12:34:21 +0200 Subject: Add tests for CiStatusHelper and changelog item Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 2 +- spec/helpers/ci_status_helper_spec.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 spec/helpers/ci_status_helper_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 5d28897425c..80998016563 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - - + - Show CI status on all pages where commits list is rendered v 8.0.1 - Improve CI migration procedure and documentation diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb new file mode 100644 index 00000000000..7fc53eb1472 --- /dev/null +++ b/spec/helpers/ci_status_helper_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe CiStatusHelper do + include IconsHelper + + let(:success_commit) { double("Ci::Commit", status: 'success') } + let(:failed_commit) { double("Ci::Commit", status: 'failed') } + + describe 'ci_status_color' do + it { expect(ci_status_icon(success_commit)).to include('fa-check') } + it { expect(ci_status_icon(failed_commit)).to include('fa-close') } + end + + describe 'ci_status_color' do + it { expect(ci_status_color(success_commit)).to eq('green') } + it { expect(ci_status_color(failed_commit)).to eq('red') } + end +end -- cgit v1.2.1 From 2eb9a20f36054d99e52342aae1b018206e7791a3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 13:11:40 +0200 Subject: Enable CI for gitlab when .gitlab-ci.yml is pushed Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/models/project.rb | 19 +++++++++++++++++++ app/models/project_services/gitlab_ci_service.rb | 2 +- app/services/git_push_service.rb | 10 ++++++++++ spec/models/project_spec.rb | 10 ++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 80998016563..4ee55016576 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.1.0 (unreleased) - Show CI status on all pages where commits list is rendered + - Automatically enable CI when push .gitlab-ci.yml file to repository v 8.0.1 - Improve CI migration procedure and documentation diff --git a/app/models/project.rb b/app/models/project.rb index 72120885105..c5c94cbfba2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -739,4 +739,23 @@ class Project < ActiveRecord::Base def ci_commit(sha) gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci? end + + def enable_ci(user) + # Enable service + service = gitlab_ci_service || create_gitlab_ci_service + service.active = true + service.save + + # Create Ci::Project + params = OpenStruct.new({ + id: self.id, + name_with_namespace: self.name_with_namespace, + path_with_namespace: self.path_with_namespace, + web_url: self.web_url, + default_branch: self.default_branch, + ssh_url_to_repo: self.ssh_url_to_repo + }) + + Ci::CreateProjectService.new.execute(user, params) + end end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 9e2b3bcd873..ec3bd44024f 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -72,7 +72,7 @@ class GitlabCiService < CiService }) ci_project = Ci::Project.find_by!(gitlab_id: project.id) - + Ci::CreateProjectService.new.execute( current_user, params, diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 0a73244774a..fb4a6dd8742 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -55,6 +55,12 @@ class GitPushService @push_data = build_push_data(oldrev, newrev, ref) + # If CI was disabled but .gitlab-ci.yml file was pushed + # we enable CI automatically + if !project.gitlab_ci? && gitlab_ci_yaml?(newrev) + project.enable_ci(user) + end + EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) @@ -143,4 +149,8 @@ class GitPushService def commit_user(commit) commit.author || user end + + def gitlab_ci_yaml?(sha) + @project.repository.blob_at(sha, '.gitlab-ci.yml') + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ba57e31f7fe..9e7b6f5cb30 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -411,4 +411,14 @@ describe Project do it { expect(project.ci_commit(commit.sha)).to eq(commit) } end + + describe :enable_ci do + let(:project) { create :project } + let(:user) { create :user } + + before { project.enable_ci(user) } + + it { expect(project.gitlab_ci?).to be_truthy } + it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } + end end -- cgit v1.2.1 From aba9668acebd3bc741affc4ff06ca3e75224d66a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 14:29:41 +0200 Subject: Rescue rugged error when detect ci yml file Signed-off-by: Dmitriy Zaporozhets --- app/services/git_push_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index fb4a6dd8742..8193b6e192d 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -152,5 +152,7 @@ class GitPushService def gitlab_ci_yaml?(sha) @project.repository.blob_at(sha, '.gitlab-ci.yml') + rescue Rugged::ReferenceError + nil end end -- cgit v1.2.1 From 0731a7af319e4c19e98a43b7355fa26f5e8bd6f5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 15:00:05 +0200 Subject: Show only enabled CI projects Since CI is enabled by pushing .gitlab-ci.yml file there is no need to add CI project via dashboard Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/ci/pager.js.coffee | 42 ------------------------------ app/controllers/ci/projects_controller.rb | 27 ++----------------- app/views/ci/projects/_project.html.haml | 11 -------- app/views/ci/projects/_search.html.haml | 7 +---- app/views/ci/projects/index.html.haml | 43 +++++++++++++------------------ 5 files changed, 21 insertions(+), 109 deletions(-) delete mode 100644 app/assets/javascripts/ci/pager.js.coffee diff --git a/app/assets/javascripts/ci/pager.js.coffee b/app/assets/javascripts/ci/pager.js.coffee deleted file mode 100644 index 226fbd654ab..00000000000 --- a/app/assets/javascripts/ci/pager.js.coffee +++ /dev/null @@ -1,42 +0,0 @@ -@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) => - CiPager.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: -> - CiPager.disable - - callback: (i) => - unless $(".loading").is(':visible') - $(".loading").show() - CiPager.getItems() diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 40b61edb0a9..e480c0be907 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,11 +1,9 @@ module Ci class ProjectsController < Ci::ApplicationController - PROJECTS_BATCH = 100 - 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, :badge, :index, :show, :new, :create, :disabled] + before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :disabled] 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] @@ -18,14 +16,9 @@ module Ci end def index - @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i - @page = @offset == 0 ? 1 : (@offset / @limit + 1) - if current_user @projects = ProjectListBuilder.new.execute(current_user, params[:search]) - - @projects = @projects.page(@page).per(@limit) - + @projects = @projects.page(params[:page]).per(40) @total_count = @projects.size end @@ -48,22 +41,6 @@ module Ci def integration end - def create - project_data = OpenStruct.new(JSON.parse(params["project"])) - - 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 - - @project = Ci::CreateProjectService.new.execute(current_user, project_data) - - 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 diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index 844b6677b3d..e0ea02cf1af 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -24,14 +24,3 @@ Private %td = ci_project.commits.count -- else - %tr.light - %td - = project.name_with_namespace - %td - %small Not added to CI - %td - %td - = form_tag ci_projects_path do - = 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' diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml index 4ab43a403f7..a956ed4c0bc 100644 --- a/app/views/ci/projects/_search.html.haml +++ b/app/views/ci/projects/_search.html.haml @@ -1,11 +1,6 @@ .search - = form_tag "#", method: :get, class: 'ci-search-form' do |f| + = form_tag ci_root_path, 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 - $('.ci-search-form').submit -> - CiPager.init "#{ci_projects_path}" + "?search=" + query, #{Ci::ProjectsController::PROJECTS_BATCH}, false - false diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index 2b618d61f79..efb1a1e4208 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -1,30 +1,23 @@ - if current_user - - if @offset > 0 - = render @projects - - else - .gray-content-block.top-block - = render "search" - .projects - .gray-content-block.clearfix.light.second-block - .pull-left.fetch-status - - if params[:search].present? - by keyword: "#{params[:search]}", - #{@total_count} projects + .gray-content-block.top-block + = render "search" + .projects + .gray-content-block.clearfix.light.second-block + .pull-left.fetch-status + - if params[:search].present? + by keyword: "#{params[:search]}", + #{@total_count} projects - .wide-table-holder - %table.table.projects-table.content-list - %thead - %tr - %th Project Name - %th Last commit - %th Access - %th Commits - - = render @projects - %p.text-center.hide.loading - %i.fa.fa-refresh.fa-spin - :coffeescript - CiPager.init "#{ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false + .wide-table-holder + %table.table.projects-table.content-list + %thead + %tr + %th Project Name + %th Last commit + %th Access + %th Commits + = render @projects + = paginate @projects, theme: 'gitlab' - else = render 'public' -- cgit v1.2.1 From 31b15e3dce2817d8cd6d70cda97c6b9dbf5c7f8a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 15:30:24 +0200 Subject: Simplify CI projects query Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 9 ++++-- app/models/ci/project.rb | 1 + app/views/ci/projects/_project.html.haml | 50 +++++++++++++++---------------- lib/ci/project_list_builder.rb | 21 ------------- 4 files changed, 31 insertions(+), 50 deletions(-) delete mode 100644 lib/ci/project_list_builder.rb diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index e480c0be907..6b7545e5447 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -16,12 +16,15 @@ module Ci end def index + @projects = Ci::Project.all + if current_user - @projects = ProjectListBuilder.new.execute(current_user, params[:search]) - @projects = @projects.page(params[:page]).per(40) - @total_count = @projects.size + @projects = @projects.where(gitlab_id: current_user.authorized_projects.pluck(:id)) end + @projects = @projects.includes(:last_commit).order('ci_commits.created_at DESC') + @projects = @projects.page(params[:page]).per(40) + respond_to do |format| format.json do pager_json("ci/projects/index", @total_count) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 37fbcc287bb..a52e28615f7 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -41,6 +41,7 @@ module Ci 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' + has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit' # Project services has_many :services, dependent: :destroy, class_name: 'Ci::Service' diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml index e0ea02cf1af..58022de9bc1 100644 --- a/app/views/ci/projects/_project.html.haml +++ b/app/views/ci/projects/_project.html.haml @@ -1,26 +1,24 @@ -- if project.gitlab_ci_project - - ci_project = project.gitlab_ci_project - - last_commit = ci_project.last_commit - %tr - %td - = link_to [:ci, ci_project] do - = ci_project.name - %td - - if last_commit - = ci_status_with_icon(last_commit.status) - = commit_link(last_commit) - · - - if ci_project.last_commit_date - = time_ago_in_words ci_project.last_commit_date - ago - - else - No builds yet - %td - - if ci_project.public - %i.fa.fa-globe - Public - - else - %i.fa.fa-lock - Private - %td - = ci_project.commits.count +- last_commit = project.last_commit +%tr + %td + = link_to [:ci, project] do + = project.name + %td + - if last_commit + = ci_status_with_icon(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.fa-globe + Public + - else + %i.fa.fa-lock + Private + %td + = project.commits.count diff --git a/lib/ci/project_list_builder.rb b/lib/ci/project_list_builder.rb deleted file mode 100644 index da26f9a9f47..00000000000 --- a/lib/ci/project_list_builder.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Ci - class ProjectListBuilder - def execute(current_user, search = nil) - projects = current_user.authorized_projects - projects = projects.search(search) if search - - projects. - joins("LEFT JOIN ci_projects ON projects.id = ci_projects.gitlab_id - LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.id = last_commit.project_id"). - reorder("ci_projects.id is NULL ASC, - CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, - last_commit.committed_at DESC") - end - - private - - def last_commit_subquery - "(SELECT project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY project_id)" - end - end -end -- cgit v1.2.1 From c3f63d6f779ac5178841c1412cedf195047a2f85 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 15:33:36 +0200 Subject: Fix search for ci projects and cleanup unnecessary code Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 8 +------- app/views/ci/projects/index.html.haml | 6 ------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 6b7545e5447..f3ee55bfbd1 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -22,15 +22,9 @@ module Ci @projects = @projects.where(gitlab_id: current_user.authorized_projects.pluck(:id)) end + @projects = @projects.search(params[:search]) if params[:search].present? @projects = @projects.includes(:last_commit).order('ci_commits.created_at DESC') @projects = @projects.page(params[:page]).per(40) - - respond_to do |format| - format.json do - pager_json("ci/projects/index", @total_count) - end - format.html - end end def show diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml index efb1a1e4208..046095a3c12 100644 --- a/app/views/ci/projects/index.html.haml +++ b/app/views/ci/projects/index.html.haml @@ -2,12 +2,6 @@ .gray-content-block.top-block = render "search" .projects - .gray-content-block.clearfix.light.second-block - .pull-left.fetch-status - - if params[:search].present? - by keyword: "#{params[:search]}", - #{@total_count} projects - .wide-table-holder %table.table.projects-table.content-list %thead -- cgit v1.2.1 From 368a40fce672ff6954f1672b67d2bf204516b054 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 15:35:43 +0200 Subject: Remove non-existing page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index f3ee55bfbd1..250a2e79313 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -2,9 +2,9 @@ module Ci class ProjectsController < Ci::ApplicationController 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 :project, only: [:build, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :disabled] - before_action :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_action :authorize_manage_project!, only: [:edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] before_action :authenticate_token!, only: [:build] before_action :no_cache, only: [:badge] skip_before_action :check_enable_flag!, only: [:disabled] @@ -35,9 +35,6 @@ module Ci @commits = @commits.page(params[:page]).per(20) end - def integration - end - def edit end -- cgit v1.2.1 From 4bc1e040d4b7d73bd7afb3dbb95c87983eef2b04 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 16:20:48 +0200 Subject: Remove test for non existing functionality Signed-off-by: Dmitriy Zaporozhets --- spec/controllers/ci/projects_controller_spec.rb | 50 ------------------------- 1 file changed, 50 deletions(-) delete mode 100644 spec/controllers/ci/projects_controller_spec.rb diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb deleted file mode 100644 index 3e579f9a7d6..00000000000 --- a/spec/controllers/ci/projects_controller_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -require "spec_helper" - -describe Ci::ProjectsController do - before do - @project = FactoryGirl.create :ci_project - end - - describe "POST /projects" do - let(:project_dump) { OpenStruct.new({ id: @project.gitlab_id }) } - - let(:user) do - create(:user) - end - - 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') - expect(assigns(:project)).not_to be_a_new(Ci::Project) - end - - it "shows error" do - 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(:user) do - create(:user) - end - - before do - sign_in(user) - end - - it "searches projects" do - xhr :get, :index, { search: "str", format: "json" }.with_indifferent_access - - expect(response).to be_success - expect(response.code).to eq('200') - end - end -end -- cgit v1.2.1 From eae27d1ecbfec7ea928b27e0ffc96abeb4a49f7a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 16:16:45 +0200 Subject: Move CI charts to project graphs area Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/charts_controller.rb | 24 --------------- app/controllers/projects/graphs_controller.rb | 10 +++++++ 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/layouts/ci/_nav_project.html.haml | 6 ---- app/views/projects/graphs/_head.html.haml | 4 +++ app/views/projects/graphs/ci.html.haml | 6 ++++ app/views/projects/graphs/ci/_build_times.haml | 21 +++++++++++++ app/views/projects/graphs/ci/_builds.haml | 41 ++++++++++++++++++++++++++ app/views/projects/graphs/ci/_overall.haml | 22 ++++++++++++++ config/routes.rb | 1 + features/project/graph.feature | 6 ++++ features/steps/project/graph.rb | 22 ++++++++++++-- spec/features/ci/projects_spec.rb | 12 -------- 16 files changed, 131 insertions(+), 131 deletions(-) delete mode 100644 app/controllers/ci/charts_controller.rb delete mode 100644 app/views/ci/charts/_build_times.haml delete mode 100644 app/views/ci/charts/_builds.haml delete mode 100644 app/views/ci/charts/_overall.haml delete mode 100644 app/views/ci/charts/show.html.haml create mode 100644 app/views/projects/graphs/ci.html.haml create mode 100644 app/views/projects/graphs/ci/_build_times.haml create mode 100644 app/views/projects/graphs/ci/_builds.haml create mode 100644 app/views/projects/graphs/ci/_overall.haml diff --git a/app/controllers/ci/charts_controller.rb b/app/controllers/ci/charts_controller.rb deleted file mode 100644 index aa875e70987..00000000000 --- a/app/controllers/ci/charts_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Ci - class ChartsController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_access_project! - before_action :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/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 0b6f7f5c91e..8bc5746a42c 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -23,6 +23,16 @@ class Projects::GraphsController < Projects::ApplicationController @commits_per_month = @commits_graph.commits_per_month end + def ci + ci_project = @project.gitlab_ci_project + + @charts = {} + @charts[:week] = Ci::Charts::WeekChart.new(ci_project) + @charts[:month] = Ci::Charts::MonthChart.new(ci_project) + @charts[:year] = Ci::Charts::YearChart.new(ci_project) + @charts[:build_times] = Ci::Charts::BuildTime.new(ci_project) + end + private def fetch_graph diff --git a/app/views/ci/charts/_build_times.haml b/app/views/ci/charts/_build_times.haml deleted file mode 100644 index c3c2f572414..00000000000 --- a/app/views/ci/charts/_build_times.haml +++ /dev/null @@ -1,21 +0,0 @@ -%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 deleted file mode 100644 index 1b0039fb834..00000000000 --- a/app/views/ci/charts/_builds.haml +++ /dev/null @@ -1,41 +0,0 @@ -%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 deleted file mode 100644 index f522f35a629..00000000000 --- a/app/views/ci/charts/_overall.haml +++ /dev/null @@ -1,21 +0,0 @@ -%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 deleted file mode 100644 index 0497f037721..00000000000 --- a/app/views/ci/charts/show.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -#charts.ci-charts - = render 'builds' - = render 'build_times' -= render 'overall' diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index cb1dece073c..284735e45c4 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -10,12 +10,6 @@ %span Commits %span.count= @project.commits.count - - if can?(current_user, :admin_project, gl_project) - = nav_link path: 'charts#show' do - = link_to ci_project_charts_path(@project) do - = icon('bar-chart fw') - %span - Charts = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do = link_to ci_project_runners_path(@project) do = icon('cog fw') diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index 9383df13305..bbfaf422a82 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -3,3 +3,7 @@ = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do = link_to 'Commits', commits_namespace_project_graph_path + - if @project.gitlab_ci? + = nav_link(action: :ci) do + = link_to ci_namespace_project_graph_path do + Continuous Integration diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml new file mode 100644 index 00000000000..2e07b67f8c0 --- /dev/null +++ b/app/views/projects/graphs/ci.html.haml @@ -0,0 +1,6 @@ +- page_title "Continuous Integration", "Graphs" += render 'head' +#charts.ci-charts + = render 'projects/graphs/ci/builds' + = render 'projects/graphs/ci/build_times' += render 'projects/graphs/ci/overall' diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml new file mode 100644 index 00000000000..c3c2f572414 --- /dev/null +++ b/app/views/projects/graphs/ci/_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/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml new file mode 100644 index 00000000000..1b0039fb834 --- /dev/null +++ b/app/views/projects/graphs/ci/_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/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml new file mode 100644 index 00000000000..9550d719471 --- /dev/null +++ b/app/views/projects/graphs/ci/_overall.haml @@ -0,0 +1,22 @@ +- ci_project = @project.gitlab_ci_project +%fieldset + %legend Overall + %p + Total: + %strong= pluralize ci_project.builds.count(:all), 'build' + %p + Successful: + %strong= pluralize ci_project.builds.success.count(:all), 'build' + %p + Failed: + %strong= pluralize ci_project.builds.failed.count(:all), 'build' + + %p + Success ratio: + %strong + #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}% + + %p + Commits covered: + %strong + = ci_project.commits.count(:all) diff --git a/config/routes.rb b/config/routes.rb index 512dda7b547..4a07c449b4e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -511,6 +511,7 @@ Gitlab::Application.routes.draw do resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do member do get :commits + get :ci end end diff --git a/features/project/graph.feature b/features/project/graph.feature index 89064242c1c..2acd65aea5f 100644 --- a/features/project/graph.feature +++ b/features/project/graph.feature @@ -12,3 +12,9 @@ Feature: Project Graph Scenario: I should see project commits graphs When I visit project "Shop" commits graph page Then page should have commits graphs + + @javascript + Scenario: I should see project ci graphs + Given project "Shop" has CI enabled + When I visit project "Shop" CI graph page + Then page should have CI graphs diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb index 5e7e573a6ab..9f9d099961d 100644 --- a/features/steps/project/graph.rb +++ b/features/steps/project/graph.rb @@ -7,12 +7,10 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps end When 'I visit project "Shop" graph page' do - project = Project.find_by(name: "Shop") visit namespace_project_graph_path(project.namespace, project, "master") end step 'I visit project "Shop" commits graph page' do - project = Project.find_by(name: "Shop") visit commits_namespace_project_graph_path(project.namespace, project, "master") end @@ -20,4 +18,24 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps expect(page).to have_content "Commit statistics for master" expect(page).to have_content "Commits per day of month" end + + step 'I visit project "Shop" CI graph page' do + visit ci_namespace_project_graph_path(project.namespace, project, 'master') + end + + step 'project "Shop" has CI enabled' do + project.enable_ci(@user) + end + + step 'page should have CI graphs' do + expect(page).to have_content 'Overall' + expect(page).to have_content 'Builds chart for last week' + expect(page).to have_content 'Builds chart for last month' + expect(page).to have_content 'Builds chart for last year' + expect(page).to have_content 'Commit duration in minutes for last 30 commits' + end + + def project + project ||= Project.find_by(name: "Shop") + end end diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb index ff17aeca447..2ae6a7a29f8 100644 --- a/spec/features/ci/projects_spec.rb +++ b/spec/features/ci/projects_spec.rb @@ -45,16 +45,4 @@ describe "Projects" do expect(find_field('Timeout').value).to eq '70' end end - - describe "GET /ci/projects/:id/charts" do - before do - visit ci_project_charts_path(@project) - end - - 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 -- cgit v1.2.1 From d538955ac89c0bffd5b0d5ebff73f81c4efb21ee Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 16:17:07 +0200 Subject: Update CHANGELOG Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 4ee55016576..067ac37815d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository + - Move CI charts to project graphs area v 8.0.1 - Improve CI migration procedure and documentation -- cgit v1.2.1 From 73a3df4de351528dfe954c0f44af1f0e45b221d0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 23 Sep 2015 16:37:59 +0200 Subject: Fix LDAP attribute mapping --- lib/gitlab/ldap/auth_hash.rb | 3 ++- spec/lib/gitlab/ldap/auth_hash_spec.rb | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb index 55deeeacd90..bf4dd9542d5 100644 --- a/lib/gitlab/ldap/auth_hash.rb +++ b/lib/gitlab/ldap/auth_hash.rb @@ -6,7 +6,7 @@ module Gitlab private def get_info(key) - attributes = ldap_config.attributes[key] + attributes = ldap_config.attributes[key.to_s] return super unless attributes attributes = Array(attributes) @@ -14,6 +14,7 @@ module Gitlab value = nil attributes.each do |attribute| value = get_raw(attribute) + value = value.first if value break if value.present? end diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb index 18c7924fea1..7d8268536a4 100644 --- a/spec/lib/gitlab/ldap/auth_hash_spec.rb +++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb @@ -24,10 +24,10 @@ describe Gitlab::LDAP::AuthHash do let(:raw_info) do { - uid: '123456', - email: 'johnsmith@example.com', - cn: 'Smith, J.', - fullName: 'John Smith' + uid: ['123456'], + email: ['johnsmith@example.com'], + cn: ['Smith, J.'], + fullName: ['John Smith'] } end @@ -45,8 +45,8 @@ describe Gitlab::LDAP::AuthHash do context "with overridden attributes" do let(:attributes) do { - username: ['mail', 'email'], - name: 'fullName' + 'username' => ['mail', 'email'], + 'name' => 'fullName' } end -- cgit v1.2.1 From 0a57c3f610433d0093ecc1f638fce339c4128abc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 23 Sep 2015 16:52:26 +0200 Subject: Refactor Project#enable_ci method Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index c5c94cbfba2..a7ea1236b86 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -747,15 +747,6 @@ class Project < ActiveRecord::Base service.save # Create Ci::Project - params = OpenStruct.new({ - id: self.id, - name_with_namespace: self.name_with_namespace, - path_with_namespace: self.path_with_namespace, - web_url: self.web_url, - default_branch: self.default_branch, - ssh_url_to_repo: self.ssh_url_to_repo - }) - - Ci::CreateProjectService.new.execute(user, params) + Ci::CreateProjectService.new.execute(user, self) end end -- cgit v1.2.1 From 150fb81ef90cba74bf7828e652e052b9ababcdf8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 23 Sep 2015 08:21:51 -0700 Subject: Remove git refs used internally by GitLab from network graph Closes #2702 --- CHANGELOG | 1 + app/helpers/graph_helper.rb | 5 ++++- spec/helpers/graph_helper_spec.rb | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 spec/helpers/graph_helper_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 1cb338f16da..b0540151fce 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.1 + - Remove git refs used internally by GitLab from network graph (Stan Hu) - Improve CI migration procedure and documentation v 8.0.0 diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index e1dda20de85..1e372d5631d 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -1,7 +1,10 @@ module GraphHelper def get_refs(repo, commit) refs = "" - refs << commit.ref_names(repo).join(' ') + # Commit::ref_names already strips the refs/XXX from important refs (e.g. refs/heads/XXX) + # so anything leftover is internally used by GitLab + commit_refs = commit.ref_names(repo).reject{ |name| name.starts_with?('refs/') } + refs << commit_refs.join(' ') # append note count refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 diff --git a/spec/helpers/graph_helper_spec.rb b/spec/helpers/graph_helper_spec.rb new file mode 100644 index 00000000000..4acf38771b7 --- /dev/null +++ b/spec/helpers/graph_helper_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe GraphHelper do + describe '#get_refs' do + let(:project) { create(:project) } + let(:commit) { project.commit("master") } + let(:graph) { Network::Graph.new(project, 'master', commit, '') } + + it 'filter our refs used by GitLab' do + allow(commit).to receive(:ref_names).and_return(['refs/merge-requests/abc', 'master', 'refs/tmp/xyz']) + self.instance_variable_set(:@graph, graph) + refs = get_refs(project.repository, commit) + expect(refs).to eq('master') + end + end +end -- cgit v1.2.1 From b57bc0389e6d11063e8ed0413e5ac2d3db23c42e Mon Sep 17 00:00:00 2001 From: Chia Yu Pai Date: Thu, 24 Sep 2015 00:35:22 +0800 Subject: Update 7.14-to-8.0.md Add missing path change back --- 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 2c7003ed063..392d8ed12b9 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -99,6 +99,7 @@ Don't store it in the same place as your database backups, otherwise your secrets are exposed if one of your backups is compromised. ``` +cd /home/git/gitlab sudo -u git -H cp config/secrets.yml.example config/secrets.yml sudo -u git -H chmod 0600 config/secrets.yml ``` -- cgit v1.2.1 From 15bf2e44572b409e97f5cad5fed0ffbf4fd83314 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 12:58:00 -0400 Subject: Fix top margin for sign-in button on public pages Closes #2615 --- app/assets/stylesheets/generic/common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 48fad7701ef..b659717b4e1 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -302,7 +302,7 @@ table { } .btn-sign-in { - margin-top: 15px; + margin-top: 8px; text-shadow: none; } -- cgit v1.2.1 From be696b72eb796d7376bdfd0c72a1a089a5acee4f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 13:24:37 -0400 Subject: Update CHANGELOG to add unreleased 8.0.2 entry [ci skip] --- CHANGELOG | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1a3b102f4e1..8f7d118eca6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,11 +3,18 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository + - Fix cases where Markdown did not render links in activity feed (Stan Hu) + +v 8.0.2 (unreleased) + - Skip check_initd_configured_correctly on omnibus installs + - Prevent double-prefixing of help page paths + - Clarify confirmation text on user deletion + - Make commit graphs responsive to window width changes (Stan Hu) + - Fix top margin for sign-in button on public pages + - Fix LDAP attribute mapping v 8.0.1 - - Fix cases where Markdown did not render links in activity feed (Stan Hu) - Improve CI migration procedure and documentation - - Make commit graphs responsive to window width changes (Stan Hu) v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) -- cgit v1.2.1 From ebcd503e9372df6b5d3dfecbb548b0bd1e3d1ecb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:01:35 -0400 Subject: Only surface version information on help index to admins Closes #2721 --- app/views/help/index.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index ab7ed1b5d95..5e0cf06e15e 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,10 +1,11 @@ %div %h1 GitLab - %span= Gitlab::VERSION - %small= Gitlab::REVISION - - if current_application_settings.version_check_enabled - = version_status_badge + - if current_user && current_user.admin? + %span= Gitlab::VERSION + %small= Gitlab::REVISION + - if current_application_settings.version_check_enabled + = version_status_badge %p.slead GitLab is open source software to collaborate on code. %br -- cgit v1.2.1 From 316358345aee2e8668f1ab048d57c2176e8788b4 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:02:21 -0400 Subject: Make all "Quick help" link text the entire body of the link Prior, it wasn't obvious which parts of each item was an actual link. --- app/views/help/index.html.haml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 5e0cf06e15e..8982831e20d 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -34,19 +34,8 @@ .panel-heading Quick help %ul.well-list - %li - See our website for - = link_to 'getting help', promo_url + '/getting-help/' - %li - Use the - = link_to 'search bar', '#', onclick: 'Shortcuts.focusSearch(event)' - on the top of this page - %li - Use - = link_to 'shortcuts', '#', onclick: 'Shortcuts.showHelp(event)' - %li - Get a support - = link_to 'subscription', 'https://about.gitlab.com/pricing/' - %li - = link_to 'Compare', 'https://about.gitlab.com/features/#compare' - GitLab editions + %li= link_to 'See our website for getting help', promo_url + '/getting-help/' + %li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)' + %li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.showHelp(event)' + %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/' + %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare' -- cgit v1.2.1 From de844ebf6ce6044b98f0e7e1d2e8cb9d032f7f00 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:03:38 -0400 Subject: "fine grained" -> "fine-grained" --- app/views/help/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 8982831e20d..b05e960abc6 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -9,7 +9,7 @@ %p.slead GitLab is open source software to collaborate on code. %br - Manage git repositories with fine grained access controls that keep your code secure. + Manage git repositories with fine-grained access controls that keep your code secure. %br Perform code reviews and enhance collaboration with merge requests. %br -- cgit v1.2.1 From 73288a8edb10e45348846dc6491db0732945a29c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:03:52 -0400 Subject: Add "Community Edition" to the version info on the help page If the version information was hidden because the user was not an admin, the "GitLab" text looked lonely. This also brings us more in line with EE which shows "Enterprise Edition". --- app/views/help/index.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index b05e960abc6..abffabfc41d 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,6 +1,7 @@ %div %h1 GitLab + Community Edition - if current_user && current_user.admin? %span= Gitlab::VERSION %small= Gitlab::REVISION -- cgit v1.2.1 From b41274c172b526705e130edca68cfab12d2c0e4a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 23 Sep 2015 10:31:21 -0700 Subject: Use standard Markdown font in Markdown preview instead of fixed-width font Closes #2585 --- CHANGELOG | 1 + app/assets/stylesheets/generic/typography.scss | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8f7d118eca6..5a5787030fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.0.2 (unreleased) - Make commit graphs responsive to window width changes (Stan Hu) - Fix top margin for sign-in button on public pages - Fix LDAP attribute mapping + - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu) v 8.0.1 - Improve CI migration procedure and documentation diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 7a8a17ced99..551db31db12 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -110,7 +110,6 @@ textarea.js-gfm-input { } .md-preview { - font-family: $monospace_font; } .strikethrough { -- cgit v1.2.1 From 3d177a818f1c623704df94d6ab6649cf9f70dbbc Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 17 Sep 2015 20:24:47 +0200 Subject: Add links to first and/or last page with kaminari Also update Kaminari gem to the latest version --- CHANGELOG | 2 ++ Gemfile | 2 +- Gemfile.lock | 4 ++-- app/views/kaminari/gitlab/_first_page.html.haml | 2 +- app/views/kaminari/gitlab/_last_page.html.haml | 2 +- app/views/kaminari/gitlab/_paginator.html.haml | 9 +++++++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8f7d118eca6..aa3ae7b768e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,8 @@ v 8.0.1 v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) +v 8.0.0 (unreleased) + - Add first and last to pagination (Zeger-Jan van de Weg) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - 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) diff --git a/Gemfile b/Gemfile index 924ee382f4d..b29846f738a 100644 --- a/Gemfile +++ b/Gemfile @@ -77,7 +77,7 @@ gem "stamp", '~> 0.5.0' gem 'enumerize', '~> 0.7.0' # Pagination -gem "kaminari", "~> 0.15.1" +gem "kaminari", "~> 0.16.3" # HAML gem "haml-rails", '~> 0.5.3' diff --git a/Gemfile.lock b/Gemfile.lock index 320f7629fb6..6070beb9d07 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -367,7 +367,7 @@ GEM railties (>= 3.2.16) json (1.8.3) jwt (1.5.1) - kaminari (0.15.1) + kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) kgio (2.9.3) @@ -846,7 +846,7 @@ DEPENDENCIES jquery-scrollto-rails (~> 1.4.3) jquery-turbolinks (~> 2.0.1) jquery-ui-rails (~> 4.2.1) - kaminari (~> 0.15.1) + kaminari (~> 0.16.3) letter_opener (~> 1.1.2) mail_room (~> 0.5.1) minitest (~> 5.7.0) diff --git a/app/views/kaminari/gitlab/_first_page.html.haml b/app/views/kaminari/gitlab/_first_page.html.haml index 41c9c0b3af6..ada7306d98d 100644 --- a/app/views/kaminari/gitlab/_first_page.html.haml +++ b/app/views/kaminari/gitlab/_first_page.html.haml @@ -5,5 +5,5 @@ -# num_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%span.first +%li.first = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote diff --git a/app/views/kaminari/gitlab/_last_page.html.haml b/app/views/kaminari/gitlab/_last_page.html.haml index b03a206224c..3431d029bcc 100644 --- a/app/views/kaminari/gitlab/_last_page.html.haml +++ b/app/views/kaminari/gitlab/_last_page.html.haml @@ -5,5 +5,5 @@ -# num_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%span.last +%li.last = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {remote: remote} diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml index b8d419b5894..2f645186921 100644 --- a/app/views/kaminari/gitlab/_paginator.html.haml +++ b/app/views/kaminari/gitlab/_paginator.html.haml @@ -8,10 +8,15 @@ = paginator.render do %div.gl-pagination %ul.pagination.clearfix - = prev_page_tag unless current_page.first? + - unless current_page.first? + = first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages + = prev_page_tag - 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? + - unless current_page.last? + = next_page_tag + = last_page_tag unless num_pages < 5 + -- cgit v1.2.1 From fe6ea911073a723e9472632e90363fafb9a50553 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 23 Sep 2015 13:39:58 +0200 Subject: Fix styling conform the new style Add my name to the right page of the changelog --- CHANGELOG | 3 +-- app/assets/stylesheets/generic/pagination.scss | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index aa3ae7b768e..3f16a370a94 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.1.0 (unreleased) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository - Fix cases where Markdown did not render links in activity feed (Stan Hu) + - Add first and last to pagination (Zeger-Jan van de Weg) v 8.0.2 (unreleased) - Skip check_initd_configured_correctly on omnibus installs @@ -19,8 +20,6 @@ v 8.0.1 v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) -v 8.0.0 (unreleased) - - Add first and last to pagination (Zeger-Jan van de Weg) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - 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) diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/generic/pagination.scss index a937677ebdc..6677f94dafd 100644 --- a/app/assets/stylesheets/generic/pagination.scss +++ b/app/assets/stylesheets/generic/pagination.scss @@ -9,6 +9,8 @@ margin: 0; display: block; + li.first, + li.last, li.next, li.prev { > a { -- cgit v1.2.1 From 48cfad9013f1aa4745bf2b07bfd07e34724ba037 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:20:58 -0400 Subject: Add a view spec (gasp!) for help/index --- spec/views/help/index.html.haml_spec.rb | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/views/help/index.html.haml_spec.rb diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb new file mode 100644 index 00000000000..4fd561428f6 --- /dev/null +++ b/spec/views/help/index.html.haml_spec.rb @@ -0,0 +1,55 @@ +require 'rails_helper' + +describe 'help/index' do + describe 'version information' do + it 'is hidden from guests' do + stub_user + stub_version('8.0.2', 'abcdefg') + stub_helpers + + render + + expect(rendered).not_to match '8.0.2' + expect(rendered).not_to match 'abcdefg' + end + + it 'is hidden from users' do + stub_user(admin?: false) + stub_version('8.0.2', 'abcdefg') + stub_helpers + + render + + expect(rendered).not_to match '8.0.2' + expect(rendered).not_to match 'abcdefg' + end + + it 'is shown to admins' do + stub_user(admin?: true) + stub_version('8.0.2', 'abcdefg') + stub_helpers + + render + + expect(rendered).to match '8.0.2' + expect(rendered).to match 'abcdefg' + end + end + + def stub_user(messages = {}) + user = messages.empty? ? nil : double(messages) + + allow(view).to receive(:current_user).and_return(user) + end + + def stub_version(version, revision) + stub_const('Gitlab::VERSION', version) + stub_const('Gitlab::REVISION', revision) + end + + def stub_helpers + allow(view).to receive(:markdown).and_return('') + allow(view).to receive(:current_application_settings). + and_return(double.as_null_object) + end +end -- cgit v1.2.1 From be142e6bd687d223dd4a543295d18676f408b7e3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 16:26:15 -0400 Subject: Move the `version_check_enabled` check from view to helper --- app/helpers/version_check_helper.rb | 2 +- app/views/help/index.html.haml | 3 +-- spec/views/help/index.html.haml_spec.rb | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb index f64d730b448..a674564c4ec 100644 --- a/app/helpers/version_check_helper.rb +++ b/app/helpers/version_check_helper.rb @@ -1,6 +1,6 @@ module VersionCheckHelper def version_status_badge - if Rails.env.production? + if Rails.env.production? && current_application_settings.version_check_enabled image_tag VersionCheck.new.url end end diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index abffabfc41d..2333e6fdf1d 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -5,8 +5,7 @@ - if current_user && current_user.admin? %span= Gitlab::VERSION %small= Gitlab::REVISION - - if current_application_settings.version_check_enabled - = version_status_badge + = version_status_badge %p.slead GitLab is open source software to collaborate on code. %br diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb index 4fd561428f6..91cf4cb98c0 100644 --- a/spec/views/help/index.html.haml_spec.rb +++ b/spec/views/help/index.html.haml_spec.rb @@ -49,7 +49,6 @@ describe 'help/index' do def stub_helpers allow(view).to receive(:markdown).and_return('') - allow(view).to receive(:current_application_settings). - and_return(double.as_null_object) + allow(view).to receive(:version_status_badge).and_return('') end end -- cgit v1.2.1 From 84d450bf4d94159529904be23f4056085629b1c1 Mon Sep 17 00:00:00 2001 From: karen Carias Date: Wed, 23 Sep 2015 13:33:58 -0700 Subject: fixed step to instructions --- doc/gitlab-basics/create-your-ssh-keys.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md index c8a73feb028..f31c353f2cf 100644 --- a/doc/gitlab-basics/create-your-ssh-keys.md +++ b/doc/gitlab-basics/create-your-ssh-keys.md @@ -10,11 +10,7 @@ After you confirm, go to GitLab and sign in to your account. ## Add your SSH Key -At the top right corner, click on "profile settings": - -![profile settings](basicsimages/profile_settings.png) - -On the left side menu click on "SSH Keys": +On the left side menu, click on "profile settings" and then click on "SSH Keys": ![SSH Keys](basicsimages/shh_keys.png) -- cgit v1.2.1 From cfcb5f0f6aac821ad5367645c994fbf7b8f41ea2 Mon Sep 17 00:00:00 2001 From: karen Carias Date: Wed, 23 Sep 2015 13:43:57 -0700 Subject: fixed step to instructions --- doc/ssh/README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 7b294a70fe7..b6b8000af4e 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -3,27 +3,27 @@ ## SSH keys An SSH key allows you to establish a secure connection between your -computer and GitLab. +computer and GitLab. Before generating an SSH key in your shell, check if your system +already has one by running the following command: +```bash +cat ~/.ssh/id_rsa.pub +``` -Before generating an SSH key, check if your system already has one by -running `cat ~/.ssh/id_rsa.pub`. If you see a long string starting with -`ssh-rsa` or `ssh-dsa`, you can skip the ssh-keygen step. +If you see a long string starting with `ssh-rsa` or `ssh-dsa`, you can skip the `ssh-keygen` step. -To generate a new SSH key, just open your terminal and use code below. The -ssh-keygen command prompts you for a location and filename to store the key -pair and for a password. When prompted for the location and filename, you -can press enter to use the default. - -It is a best practice to use a password for an SSH key, but it is not +Note: It is a best practice to use a password for an SSH key, but it is not required and you can skip creating a password by pressing enter. Note that the password you choose here can't be altered or retrieved. +To generate a new SSH key, use the following command: ```bash ssh-keygen -t rsa -C "$your_email" ``` +This command will prompt you for a location and filename to store the key +pair and for a password. When prompted for the location and filename, you +can press enter to use the default. -Use the code below to show your public key. - +Use the command below to show your public key: ```bash cat ~/.ssh/id_rsa.pub ``` @@ -32,7 +32,7 @@ Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your user profile. Please copy the complete key starting with `ssh-` and ending with your username and host. -Use code below to copy your public key to the clipboard. Depending on your +To copy your public key to the clipboard, use code below. Depending on your OS you'll need to use a different command: **Windows:** -- cgit v1.2.1 From 45824aabc634e06d9f7dd853e573c153b7bf9a78 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 17:18:15 -0400 Subject: Allow non-admin users to see version information We want users to know what features they have available (and to pressure their admins to upgrade). --- app/views/help/index.html.haml | 2 +- spec/views/help/index.html.haml_spec.rb | 23 +++++------------------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 2333e6fdf1d..57bc91ea5a9 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -2,7 +2,7 @@ %h1 GitLab Community Edition - - if current_user && current_user.admin? + - if user_signed_in? %span= Gitlab::VERSION %small= Gitlab::REVISION = version_status_badge diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb index 91cf4cb98c0..6b07fcfc987 100644 --- a/spec/views/help/index.html.haml_spec.rb +++ b/spec/views/help/index.html.haml_spec.rb @@ -3,18 +3,7 @@ require 'rails_helper' describe 'help/index' do describe 'version information' do it 'is hidden from guests' do - stub_user - stub_version('8.0.2', 'abcdefg') - stub_helpers - - render - - expect(rendered).not_to match '8.0.2' - expect(rendered).not_to match 'abcdefg' - end - - it 'is hidden from users' do - stub_user(admin?: false) + stub_user(nil) stub_version('8.0.2', 'abcdefg') stub_helpers @@ -24,8 +13,8 @@ describe 'help/index' do expect(rendered).not_to match 'abcdefg' end - it 'is shown to admins' do - stub_user(admin?: true) + it 'is shown to users' do + stub_user stub_version('8.0.2', 'abcdefg') stub_helpers @@ -36,10 +25,8 @@ describe 'help/index' do end end - def stub_user(messages = {}) - user = messages.empty? ? nil : double(messages) - - allow(view).to receive(:current_user).and_return(user) + def stub_user(user = double) + allow(view).to receive(:user_signed_in?).and_return(user) end def stub_version(version, revision) -- cgit v1.2.1 From e8ce9639da161fca0053cdaeba4a822d4dd71771 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 20:36:17 -0400 Subject: Add Troubleshooting section to Install guide --- doc/install/installation.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/install/installation.md b/doc/install/installation.md index 5887891c1ab..282c84fd1e1 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -518,3 +518,13 @@ You also need to change the corresponding options (e.g. `ssh_user`, `ssh_host`, ### Additional Markup Styles Apart from the always supported markdown style there are other rich text files that GitLab can display. But you might have to install a dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information. + +## Troubleshooting + +### "You appear to have cloned an empty repository." + +If you see this message when attempting to clone a repository hosted by GitLab, +this is likely due to a missing or misconfigured `gitlab-git-http-server` +instance. Double-check that you've [installed Go](#3-go), [installed +gitlab-git-http-server](#install-gitlab-git-http-server), and correctly +[configured Nginx](#site-configuration). -- cgit v1.2.1 From 6e65108325ef77ab66466700011b7d2d419ebc5a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 23 Sep 2015 20:36:25 -0400 Subject: Add Troubleshooting section to 7.14 to 8.0 Update guide --- doc/update/7.14-to-8.0.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 2c7003ed063..e25b38a3533 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -190,3 +190,12 @@ 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. + +## Troubleshooting + +### "You appear to have cloned an empty repository." + +If you see this message when attempting to clone a repository hosted by GitLab, +this is likely due to a missing or misconfigured `gitlab-git-http-server` +instance. Double-check that you correctly completed [Step 5](#5-install-gitlab-git-http-server) +to install the daemon and [Step 8](#new-nginx-configuration) to reconfigure Nginx. -- cgit v1.2.1 From 64e12d5853b44c945a43a9ed7fc7046138884426 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 09:44:38 +0200 Subject: Add header for ci graphs and check that it is enabled Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/application_controller.rb | 7 +++++++ app/views/projects/graphs/ci.html.haml | 1 + 2 files changed, 8 insertions(+) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index ee88d49b400..e8af4c092da 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -1,6 +1,7 @@ class Projects::ApplicationController < ApplicationController before_action :project before_action :repository + before_action :ci_enabled, only: :ci layout 'project' def authenticate_user! @@ -25,4 +26,10 @@ class Projects::ApplicationController < ApplicationController ) end end + + private + + def ci_enabled + return render_404 unless @project.gitlab_ci? + end end diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml index 2e07b67f8c0..4f69cc64f7c 100644 --- a/app/views/projects/graphs/ci.html.haml +++ b/app/views/projects/graphs/ci.html.haml @@ -1,4 +1,5 @@ - page_title "Continuous Integration", "Graphs" += render "header_title" = render 'head' #charts.ci-charts = render 'projects/graphs/ci/builds' -- cgit v1.2.1 From 50cff3e4006ef2d57ebcb258c619a42af00bddc0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 09:47:52 +0200 Subject: Check for CI enabled in correct place Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/application_controller.rb | 1 - app/controllers/projects/graphs_controller.rb | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index e8af4c092da..48c922f450c 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -1,7 +1,6 @@ class Projects::ApplicationController < ApplicationController before_action :project before_action :repository - before_action :ci_enabled, only: :ci layout 'project' def authenticate_user! diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 8bc5746a42c..418b92040bc 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -5,6 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_download_code! + before_action :ci_enabled, only: :ci def show respond_to do |format| -- cgit v1.2.1 From 5faf3e8176fc68fdcff18de4f28360765432739a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 11:21:10 +0200 Subject: Refactor ci-status badge css Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/projects.scss | 34 ------------------------------ app/assets/stylesheets/ci/status.scss | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 34 deletions(-) create mode 100644 app/assets/stylesheets/ci/status.scss diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/ci/projects.scss index c63a67ab720..8c5273abcda 100644 --- a/app/assets/stylesheets/ci/projects.scss +++ b/app/assets/stylesheets/ci/projects.scss @@ -56,38 +56,4 @@ margin-bottom: 16px; } } - - .ci-status { - padding: 2px 7px; - margin-right: 5px; - border: 1px solid #EEE; - white-space: nowrap; - @include border-radius(4px); - - &.ci-failed { - color: $gl-danger; - border-color: $gl-danger; - } - - &.ci-success { - color: $gl-success; - border-color: $gl-success; - } - - &.ci-info { - color: $gl-info; - border-color: $gl-info; - } - - &.ci-disabled { - color: $gl-gray; - border-color: $gl-gray; - } - - &.ci-pending, - &.ci-running { - color: $gl-warning; - border-color: $gl-warning; - } - } } diff --git a/app/assets/stylesheets/ci/status.scss b/app/assets/stylesheets/ci/status.scss new file mode 100644 index 00000000000..a7d3b2197f1 --- /dev/null +++ b/app/assets/stylesheets/ci/status.scss @@ -0,0 +1,37 @@ +.ci-status { + padding: 2px 7px; + margin-right: 5px; + border: 1px solid #EEE; + white-space: nowrap; + @include border-radius(4px); + + &:hover { + text-decoration: none; + } + + &.ci-failed { + color: $gl-danger; + border-color: $gl-danger; + } + + &.ci-success { + color: $gl-success; + border-color: $gl-success; + } + + &.ci-info { + color: $gl-info; + border-color: $gl-info; + } + + &.ci-disabled { + color: $gl-gray; + border-color: $gl-gray; + } + + &.ci-pending, + &.ci-running { + color: $gl-warning; + border-color: $gl-warning; + } +} -- cgit v1.2.1 From 9c4307e287cade71c180c4c37fb14a018cf9fe28 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 11:21:39 +0200 Subject: Show CI status on commit page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/commit_controller.rb | 2 ++ app/views/projects/commit/_commit_box.html.haml | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 78d42d695b6..2fae5057138 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -22,6 +22,8 @@ class Projects::CommitController < Projects::ApplicationController commit_id: @commit.id } + @ci_commit = project.ci_commit(commit.sha) + respond_to do |format| format.html format.diff { render text: @commit.to_diff } diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 2ac79e87b4a..fbf0a9ec0c3 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -38,6 +38,13 @@ - @commit.parents.each do |parent| = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent) +- if @ci_commit + .pull-right + = link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do + = ci_status_icon(@ci_commit) + build: + = @ci_commit.status + .commit-info-row.branches %i.fa.fa-spinner.fa-spin -- cgit v1.2.1 From 2be714dcf61842e41d0432fae3abdf1a09215012 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 11:40:48 +0200 Subject: Show CI status on commit page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + features/project/commits/commits.feature | 5 +++++ features/steps/project/commits/commits.rb | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b1e61ddf8e7..4a34a3835a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.1.0 (unreleased) - Move CI charts to project graphs area - Fix cases where Markdown did not render links in activity feed (Stan Hu) - Add first and last to pagination (Zeger-Jan van de Weg) + - Show CI status on commit page v 8.0.2 (unreleased) - Skip check_initd_configured_correctly on omnibus installs diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature index 3ebc8a39aae..34161b81d44 100644 --- a/features/project/commits/commits.feature +++ b/features/project/commits/commits.feature @@ -16,6 +16,11 @@ Feature: Project Commits Then I see commit info And I see side-by-side diff button + Scenario: I browse commit with ci from list + Given commit has ci status + And I click on commit link + Then I see commit ci info + Scenario: I browse commit with side-by-side diff view Given I click on commit link And I click side-by-side diff button diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 23e67371f96..56f1f06fb06 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -101,4 +101,13 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'I click side-by-side diff button' do find('#parallel-diff-btn').click end + + step 'commit has ci status' do + @project.enable_ci(@user) + create :ci_commit, project: @project.gitlab_ci_project, sha: sample_commit.id + end + + step 'I see commit ci info' do + expect(page).to have_content "build: skipped" + end end -- cgit v1.2.1 From 68f7bc63bc014d9366a73c4be44fc8c640eaf888 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 24 Sep 2015 12:08:03 +0200 Subject: Add the newrelic-grape gem This allows us to see a breakdown of API requests in New Relic. --- Gemfile | 1 + Gemfile.lock | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index b29846f738a..15b6bdab9d7 100644 --- a/Gemfile +++ b/Gemfile @@ -283,6 +283,7 @@ group :production do end gem "newrelic_rpm", '~> 3.9.4.245' +gem 'newrelic-grape' gem 'octokit', '~> 3.7.0' diff --git a/Gemfile.lock b/Gemfile.lock index 6070beb9d07..15a0b019b1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -402,6 +402,9 @@ GEM net-ssh (>= 2.6.5) net-ssh (2.9.2) netrc (0.10.3) + newrelic-grape (2.0.0) + grape + newrelic_rpm newrelic_rpm (3.9.4.245) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) @@ -853,6 +856,7 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) nested_form (~> 0.3.2) + newrelic-grape newrelic_rpm (~> 3.9.4.245) nprogress-rails (~> 0.1.2.3) oauth2 (~> 1.0.0) @@ -930,6 +934,3 @@ DEPENDENCIES webmock (~> 1.21.0) whenever (~> 0.8.4) wikicloth (= 0.8.1) - -BUNDLED WITH - 1.10.6 -- cgit v1.2.1 From 353d426e4e68006ba8cea90e2f6ad4e8956cb6a9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 14:00:23 +0200 Subject: Show CI status on Your projects page and Starred projects page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/assets/stylesheets/pages/projects.scss | 8 +++++++- app/views/dashboard/projects/_projects.html.haml | 2 +- app/views/shared/projects/_list.html.haml | 3 ++- app/views/shared/projects/_project.html.haml | 13 +++++++++++-- features/dashboard/dashboard.feature | 4 +++- features/steps/dashboard/dashboard.rb | 4 ++++ features/steps/project/graph.rb | 4 ---- features/steps/shared/project.rb | 10 ++++++++++ 9 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9c9554eb00f..48d2b646339 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.1.0 (unreleased) - Fix cases where Markdown did not render links in activity feed (Stan Hu) - Add first and last to pagination (Zeger-Jan van de Weg) - Show CI status on commit page + - Show CI status on Your projects page and Starred projects page v 8.0.2 (unreleased) - Skip check_initd_configured_correctly on omnibus installs diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index a986fafff07..8d75f3aad49 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -297,9 +297,15 @@ pre.light-well { color: #4c4e54; } - .pull-right.light { + .project-controls { + float: right; + color: $gl-gray; line-height: 45px; color: #7f8fa4; + + a:hover { + text-decoration: none; + } } .project-description { diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index ef9b9ce756a..c8c315c73d8 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -7,4 +7,4 @@ = link_to new_project_path, class: 'btn btn-success' do New project - = render 'shared/projects/list', projects: @projects + = render 'shared/projects/list', projects: @projects, ci: true diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 330b0626d63..16e1d8421de 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -1,12 +1,13 @@ - projects_limit = 20 unless local_assigns[:projects_limit] - avatar = true unless local_assigns[:avatar] == false - stars = true unless local_assigns[:stars] == false +- ci = false unless local_assigns[:ci] == true %ul.projects-list - projects.each_with_index do |project, i| - css_class = (i >= projects_limit) ? 'hide' : nil = render "shared/projects/project", project: project, - avatar: avatar, stars: stars, css_class: css_class + avatar: avatar, stars: stars, css_class: css_class, ci: ci - if projects.size > projects_limit %li.bottom.center diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 5318c6011f4..1c529c91c51 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -1,9 +1,10 @@ - avatar = true unless local_assigns[:avatar] == false - stars = true unless local_assigns[:stars] == false +- ci = false unless local_assigns[:ci] == true - css_class = '' unless local_assigns[:css_class] - css_class += " no-description" unless project.description.present? %li.project-row{ class: css_class } - = cache [project.namespace, project, controller.controller_name, controller.action_name, 'v2.1'] do + = cache [project.namespace, project, controller.controller_name, controller.action_name, 'v2.2'] do = link_to project_path(project), class: dom_class(project) do - if avatar .dash-project-avatar @@ -15,8 +16,16 @@ \/ %span.project-name.filter-title = project.name + + .project-controls + - if ci && !project.empty_repo? + - if ci_commit = project.ci_commit(project.commit.sha) + = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", + title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do + = ci_status_icon(ci_commit) +   - if stars - %span.pull-right.light + %span %i.fa.fa-star = project.star_count - if project.description.present? diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index 392d4235eff..b667b587c5b 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -4,12 +4,14 @@ Feature: Dashboard Given I sign in as a user And I own project "Shop" And project "Shop" has push event + And project "Shop" has CI enabled + And project "Shop" has CI build And I visit dashboard page - @javascript Scenario: I should see projects list Then I should see "New Project" link Then I should see "Shop" project link + Then I should see "Shop" project CI status @javascript Scenario: I should see activity list diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index cb3a80cac29..f0fbd8a826a 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -11,6 +11,10 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps expect(page).to have_link "Shop" end + step 'I should see "Shop" project CI status' do + expect(page).to have_link "Build status: skipped" + end + step 'I should see last push widget' do expect(page).to have_content "You pushed to fix" expect(page).to have_link "Create Merge Request" diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb index 9f9d099961d..9453d636445 100644 --- a/features/steps/project/graph.rb +++ b/features/steps/project/graph.rb @@ -23,10 +23,6 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps visit ci_namespace_project_graph_path(project.namespace, project, 'master') end - step 'project "Shop" has CI enabled' do - project.enable_ci(@user) - end - step 'page should have CI graphs' do expect(page).to have_content 'Overall' expect(page).to have_content 'Builds chart for last week' diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index a9cf426852e..fa841f67510 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -196,4 +196,14 @@ module SharedProject create(:label, project: project, title: 'feature') create(:label, project: project, title: 'enhancement') end + + step 'project "Shop" has CI enabled' do + project = Project.find_by(name: "Shop") + project.enable_ci(@user) + end + + step 'project "Shop" has CI build' do + project = Project.find_by(name: "Shop") + create :ci_commit, project: project.gitlab_ci_project, sha: project.commit.sha + end end -- cgit v1.2.1 From bd6c982bf1dce111d8aa17d7c4c5acd073051a38 Mon Sep 17 00:00:00 2001 From: Julio Date: Thu, 24 Sep 2015 12:03:23 +0000 Subject: Documentation of omniauth-ldap limitations Further documentation about limitations directly impacting settings of users' LDAP servers. --- doc/integration/ldap.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index 3bc5df21ef4..9b7d8fa3969 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -173,3 +173,23 @@ Tip: if you want to limit access to the nested members of an Active Directory gr ``` Please note that GitLab does not support the custom filter syntax used by omniauth-ldap. + +## Limitations + +GitLab's LDAP client is based on [omniauth-ldap](https://gitlab.com/gitlab-org/omniauth-ldap) +which encapsulates Ruby's `Net::LDAP` class. It provides a pure-Ruby implementation +of the LDAP client protocol. As a result, GitLab is limited by `omniauth-ldap` and may impact your LDAP +server settings. + +### TLS Client Authentication +Not implemented by `Net::LDAP`. +So you should disable anonymous LDAP authentication and enable simple or SASL +authentication. TLS client authentication setting in your LDAP server cannot be +mandatory and clients cannot be authenticated with the TLS protocol. + +### TLS Server Authentication +Not supported by GitLab's configuration options. +When setting `method: ssl`, the underlying authentication method used by +`omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with +the LDAP server before any LDAP-protocol data is exchanged but no validation of +the LDAP server's SSL certificate is performed. \ No newline at end of file -- cgit v1.2.1 From 2759b13ae99dc1d30516123390bbdb73258fa270 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 24 Sep 2015 15:08:33 +0200 Subject: Remove CI dashboard With CI status integrated at GitLab projects page there is no reason to keep it anymore. Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/projects_controller.rb | 16 ++-------------- app/views/ci/projects/_project.html.haml | 24 ------------------------ app/views/ci/projects/_public.html.haml | 16 ---------------- app/views/ci/projects/_search.html.haml | 6 ------ app/views/ci/projects/index.html.haml | 17 ----------------- app/views/layouts/ci/_nav_project.html.haml | 5 +++-- app/views/layouts/ci/_page.html.haml | 5 +++-- app/views/layouts/nav/_dashboard.html.haml | 5 ----- spec/features/ci/projects_spec.rb | 10 ---------- 10 files changed, 9 insertions(+), 96 deletions(-) delete mode 100644 app/views/ci/projects/_project.html.haml delete mode 100644 app/views/ci/projects/_public.html.haml delete mode 100644 app/views/ci/projects/_search.html.haml delete mode 100644 app/views/ci/projects/index.html.haml diff --git a/CHANGELOG b/CHANGELOG index 48d2b646339..710f99cbf51 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.1.0 (unreleased) - Add first and last to pagination (Zeger-Jan van de Weg) - Show CI status on commit page - Show CI status on Your projects page and Starred projects page + - Remove "Continuous Integration" page from dashboard v 8.0.2 (unreleased) - Skip check_initd_configured_correctly on omnibus installs diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 250a2e79313..6111753a7fb 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,9 +1,9 @@ module Ci class ProjectsController < Ci::ApplicationController - before_action :authenticate_user!, except: [:build, :badge, :index, :show] + before_action :authenticate_user!, except: [:build, :badge, :show] before_action :authenticate_public_page!, only: :show before_action :project, only: [:build, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] - before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :disabled] + before_action :authorize_access_project!, except: [:build, :badge, :show, :new, :disabled] before_action :authorize_manage_project!, only: [:edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] before_action :authenticate_token!, only: [:build] before_action :no_cache, only: [:badge] @@ -15,18 +15,6 @@ module Ci def disabled end - def index - @projects = Ci::Project.all - - if current_user - @projects = @projects.where(gitlab_id: current_user.authorized_projects.pluck(:id)) - end - - @projects = @projects.search(params[:search]) if params[:search].present? - @projects = @projects.includes(:last_commit).order('ci_commits.created_at DESC') - @projects = @projects.page(params[:page]).per(40) - end - def show @ref = params[:ref] diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml deleted file mode 100644 index 58022de9bc1..00000000000 --- a/app/views/ci/projects/_project.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- last_commit = project.last_commit -%tr - %td - = link_to [:ci, project] do - = project.name - %td - - if last_commit - = ci_status_with_icon(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.fa-globe - Public - - else - %i.fa.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 deleted file mode 100644 index bcbd60b83f0..00000000000 --- a/app/views/ci/projects/_public.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= content_for :title do - %h3.project-title - Public 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 deleted file mode 100644 index a956ed4c0bc..00000000000 --- a/app/views/ci/projects/_search.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.search - = form_tag ci_root_path, 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 diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml deleted file mode 100644 index 046095a3c12..00000000000 --- a/app/views/ci/projects/index.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -- if current_user - .gray-content-block.top-block - = render "search" - .projects - .wide-table-holder - %table.table.projects-table.content-list - %thead - %tr - %th Project Name - %th Last commit - %th Access - %th Commits - - = render @projects - = paginate @projects, theme: 'gitlab' -- else - = render 'public' diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 284735e45c4..7daf9342e42 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -1,8 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to ci_root_path, title: 'Back to CI projects', data: {placement: 'right'}, class: 'back-link' do + = link_to project_path(@project.gl_project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') - %span= 'Back to CI projects' + %span + Back to project %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/_page.html.haml b/app/views/layouts/ci/_page.html.haml index c598f63c4c8..bb5ec727bff 100644 --- a/app/views/layouts/ci/_page.html.haml +++ b/app/views/layouts/ci/_page.html.haml @@ -2,10 +2,11 @@ = 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 + = link_to 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 + %h3 GitLab + - if defined?(sidebar) && sidebar = render "layouts/ci/#{sidebar}" - elsif current_user diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index b94165aac39..21d8655131f 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -31,11 +31,6 @@ %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count - = nav_link(path: ['ci/projects#index', 'ci/projects#disabled']) do - = link_to ci_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do - = icon('building fw') - %span - Continuous Integration = nav_link(controller: :snippets) do = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do = icon('clipboard fw') diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb index 2ae6a7a29f8..7c8cd1ce5c7 100644 --- a/spec/features/ci/projects_spec.rb +++ b/spec/features/ci/projects_spec.rb @@ -9,16 +9,6 @@ describe "Projects" do @project.gl_project.team << [user, :master] end - describe "GET /ci/projects", js: true do - before do - stub_js_gitlab_calls - 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 /ci/projects/:id" do before do visit ci_project_path(@project) -- cgit v1.2.1 From 29e0a43efe7c06d4c7ae52600beef79993744191 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Sep 2015 15:11:41 +0200 Subject: Update mail_room --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index b29846f738a..ac272dd2160 100644 --- a/Gemfile +++ b/Gemfile @@ -286,7 +286,7 @@ gem "newrelic_rpm", '~> 3.9.4.245' gem 'octokit', '~> 3.7.0' -gem "mail_room", "~> 0.5.1" +gem "mail_room", "~> 0.5.2" gem 'email_reply_parser', '~> 0.5.8' diff --git a/Gemfile.lock b/Gemfile.lock index 6070beb9d07..5bc1c998f38 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -384,7 +384,7 @@ GEM systemu (~> 2.6.2) mail (2.6.3) mime-types (>= 1.16, < 3) - mail_room (0.5.1) + mail_room (0.5.2) method_source (0.8.2) mime-types (1.25.1) mimemagic (0.3.0) @@ -848,7 +848,7 @@ DEPENDENCIES jquery-ui-rails (~> 4.2.1) kaminari (~> 0.16.3) letter_opener (~> 1.1.2) - mail_room (~> 0.5.1) + mail_room (~> 0.5.2) minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) -- cgit v1.2.1 From 83347954fc3c0c317c77f0528cdbaa456093771a Mon Sep 17 00:00:00 2001 From: Pavel Forkert Date: Thu, 24 Sep 2015 16:14:16 +0300 Subject: Add option to admin area to sign in as a specific user Closes #2291 --- app/controllers/admin/users_controller.rb | 6 ++++++ app/views/admin/users/index.html.haml | 3 ++- config/routes.rb | 1 + spec/controllers/admin/users_controller_spec.rb | 15 +++++++++++++++ spec/features/admin/admin_users_spec.rb | 16 ++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index a19b1abee27..00f41a10dd1 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -63,6 +63,12 @@ class Admin::UsersController < Admin::ApplicationController end end + def login_as + sign_in(user) + flash[:alert] = "Logged in as #{user.username}" + redirect_to root_path + end + def disable_two_factor user.disable_two_factor! redirect_to admin_user_path(user), diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index e3698ac1c46..8dbce7a4a15 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -72,7 +72,7 @@ = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm" %ul.well-list - @users.each do |user| - %li + %li{ class: "user-#{user.id}" } .list-item-name - if user.blocked? %i.fa.fa-lock.cred @@ -90,6 +90,7 @@   = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-xs" - unless user == current_user + = link_to 'Log in', login_as_admin_user_path(user), method: :put, class: "btn btn-xs btn-primary" - if user.blocked? = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success" - else diff --git a/config/routes.rb b/config/routes.rb index 4a07c449b4e..5f7d06a620e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -262,6 +262,7 @@ Gitlab::Application.routes.draw do put :unblock put :unlock put :confirm + put :login_as patch :disable_two_factor delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index c40b2c2a583..e4c32cd2a14 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -7,6 +7,21 @@ describe Admin::UsersController do sign_in(admin) end + describe 'PUT login_as' do + let(:user) { create(:user) } + + it 'logs admin as another user' do + expect(warden.authenticate(scope: :user)).not_to eq(user) + put :login_as, id: user.username + expect(warden.authenticate(scope: :user)).to eq(user) + end + + it 'redirects user to homepage' do + put :login_as, id: user.username + expect(response).to redirect_to(root_path) + end + end + describe 'DELETE #user with projects' do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 86717761582..870a82d0ee0 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -166,4 +166,20 @@ describe "Admin::Users", feature: true do end end end + + it 'should be able to log in as another user' do + another_user = create(:user) + + visit admin_users_path + + page.within ".user-#{another_user.id}" do + click_link 'Log in' + end + + expect(page).to have_content("Logged in as #{another_user.username}") + + page.within '.sidebar-user .username' do + expect(page).to have_content(another_user.username) + end + end end -- cgit v1.2.1 From 477017722b4e3a9e3ab8e3c51608aedc9bf2f8d2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Sep 2015 15:15:25 +0200 Subject: Add and document option to use StartTLS with mail_room. --- config/mail_room.yml.example | 2 ++ doc/incoming_email/README.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example index dd8edfc42eb..82e1a42058e 100644 --- a/config/mail_room.yml.example +++ b/config/mail_room.yml.example @@ -6,6 +6,8 @@ # :port: 993 # # Whether the IMAP server uses SSL # :ssl: true + # # Whether the IMAP server uses StartTLS + # :start_tls: false # # Email account username. Usually the full email address. # :email: "replies@gitlab.example.com" # # Email account password diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md index c94e25bd4cc..01ab22321ed 100644 --- a/doc/incoming_email/README.md +++ b/doc/incoming_email/README.md @@ -88,6 +88,8 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. :port: 993 # Whether the IMAP server uses SSL :ssl: true + # Whether the IMAP server uses StartTLS + :start_tls: false # Email account username. Usually the full email address. :email: "gitlab-incoming@gmail.com" # Email account password @@ -161,6 +163,8 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. :port: 993 # Whether the IMAP server uses SSL :ssl: true + # Whether the IMAP server uses StartTLS + :start_tls: false # Email account username. Usually the full email address. :email: "gitlab-incoming@gmail.com" # Email account password -- cgit v1.2.1 From f6be2e0d6c666e12f0025e476db9e8387ff8ed79 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Sep 2015 15:15:29 +0200 Subject: Update changelog --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 48d2b646339..9f967830009 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,8 @@ v 8.0.2 (unreleased) - Fix top margin for sign-in button on public pages - Fix LDAP attribute mapping - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu) + - Fix Reply by email for non-UTF-8 messages. + - Add option to use StartTLS with Reply by email IMAP server. v 8.0.1 - Remove git refs used internally by GitLab from network graph (Stan Hu) -- cgit v1.2.1 From 64295a18c5bd9f4e92cddb5d1f391657a3757fe2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 23 Sep 2015 12:47:29 +0200 Subject: Get GitLabCiService token from CI project --- app/models/project_services/gitlab_ci_service.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index df92c6158e7..773b61efd6f 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -21,8 +21,6 @@ class GitlabCiService < CiService include Gitlab::Application.routes.url_helpers - prop_accessor :token - after_save :compose_service_hook, if: :activated? def compose_service_hook @@ -53,6 +51,12 @@ class GitlabCiService < CiService end end + def token + if project.gitlab_ci_project.present? + project.gitlab_ci_project.token + end + end + def get_ci_commit(sha, ref) Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref) end @@ -69,8 +73,7 @@ class GitlabCiService < CiService name_with_namespace: new_project.name_with_namespace, path_with_namespace: new_project.path_with_namespace, web_url: new_project.web_url, - default_branch: new_project.default_branch, - ssh_url_to_repo: new_project.ssh_url_to_repo + default_branch: new_project.default_branch }) ci_project = Ci::Project.find_by!(gitlab_id: project.id) -- cgit v1.2.1 From 7545190c8ae82f29f8667bf6a04a4e411bce469f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 24 Sep 2015 15:22:30 +0200 Subject: Remove unneeded change --- app/models/project_services/gitlab_ci_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 773b61efd6f..23ab206efba 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -73,7 +73,8 @@ class GitlabCiService < CiService name_with_namespace: new_project.name_with_namespace, path_with_namespace: new_project.path_with_namespace, web_url: new_project.web_url, - default_branch: new_project.default_branch + default_branch: new_project.default_branch, + ssh_url_to_repo: new_project.ssh_url_to_repo }) ci_project = Ci::Project.find_by!(gitlab_id: project.id) -- cgit v1.2.1 From eb9528b8b964c78ef3d33818286c529b83c35a5e Mon Sep 17 00:00:00 2001 From: Pavel Forkert Date: Thu, 24 Sep 2015 16:34:04 +0300 Subject: Move login button to user page, switched to POST method --- app/views/admin/users/_head.html.haml | 2 ++ app/views/admin/users/index.html.haml | 1 - config/routes.rb | 2 +- spec/controllers/admin/users_controller_spec.rb | 6 ++-- spec/features/admin/admin_users_spec.rb | 37 ++++++++++++++----------- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 9d5e934c8ba..4245d0f1eda 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -6,6 +6,8 @@ %span.cred (Admin) .pull-right + - unless @user == current_user + = link_to 'Log in as this user', login_as_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 8dbce7a4a15..82a88863eb7 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -90,7 +90,6 @@   = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-xs" - unless user == current_user - = link_to 'Log in', login_as_admin_user_path(user), method: :put, class: "btn btn-xs btn-primary" - if user.blocked? = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success" - else diff --git a/config/routes.rb b/config/routes.rb index 5f7d06a620e..0792cb559e5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -262,7 +262,7 @@ Gitlab::Application.routes.draw do put :unblock put :unlock put :confirm - put :login_as + post :login_as patch :disable_two_factor delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index e4c32cd2a14..7168db117d6 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -7,17 +7,17 @@ describe Admin::UsersController do sign_in(admin) end - describe 'PUT login_as' do + describe 'POST login_as' do let(:user) { create(:user) } it 'logs admin as another user' do expect(warden.authenticate(scope: :user)).not_to eq(user) - put :login_as, id: user.username + post :login_as, id: user.username expect(warden.authenticate(scope: :user)).to eq(user) end it 'redirects user to homepage' do - put :login_as, id: user.username + post :login_as, id: user.username expect(response).to redirect_to(root_path) end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 870a82d0ee0..67da3c199ad 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -111,6 +111,27 @@ describe "Admin::Users", feature: true do expect(page).to have_content(@user.name) end + describe 'Login as another user' do + it 'should show login button for other users' do + another_user = create(:user) + + visit admin_user_path(another_user) + + click_link 'Log in as this user' + + expect(page).to have_content("Logged in as #{another_user.username}") + + page.within '.sidebar-user .username' do + expect(page).to have_content(another_user.username) + end + end + + it 'should not show login button for admin itself' do + visit admin_user_path(@user) + expect(page).not_to have_content('Log in as this user') + end + end + describe 'Two-factor Authentication status' do it 'shows when enabled' do @user.update_attribute(:two_factor_enabled, true) @@ -166,20 +187,4 @@ describe "Admin::Users", feature: true do end end end - - it 'should be able to log in as another user' do - another_user = create(:user) - - visit admin_users_path - - page.within ".user-#{another_user.id}" do - click_link 'Log in' - end - - expect(page).to have_content("Logged in as #{another_user.username}") - - page.within '.sidebar-user .username' do - expect(page).to have_content(another_user.username) - end - end end -- cgit v1.2.1 From 82eeb5e284bd22bc04c82def521cc3d65eb2bcd1 Mon Sep 17 00:00:00 2001 From: Pavel Forkert Date: Thu, 24 Sep 2015 16:38:52 +0300 Subject: Remove stuff from previous UI --- app/views/admin/users/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 82a88863eb7..e3698ac1c46 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -72,7 +72,7 @@ = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm" %ul.well-list - @users.each do |user| - %li{ class: "user-#{user.id}" } + %li .list-item-name - if user.blocked? %i.fa.fa-lock.cred -- cgit v1.2.1 From 3dec9dc4a36db340a61fc2a0fbdc056957b0279f Mon Sep 17 00:00:00 2001 From: Pavel Forkert Date: Thu, 24 Sep 2015 16:40:21 +0300 Subject: Clarify spec title explanation --- spec/features/admin/admin_users_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 67da3c199ad..c2c7364f6c5 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -112,7 +112,7 @@ describe "Admin::Users", feature: true do end describe 'Login as another user' do - it 'should show login button for other users' do + it 'should show login button for other users and check that it works' do another_user = create(:user) visit admin_user_path(another_user) -- cgit v1.2.1 From 0c877875ea3e7ccbdf48b7736f8c35e2a179ab45 Mon Sep 17 00:00:00 2001 From: Pavel Forkert Date: Thu, 24 Sep 2015 16:42:27 +0300 Subject: Add entry to changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 4a34a3835a7..c19e36ed47a 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.1.0 (unreleased) + - Add option to admin area to sign in as a specific user (Pavel Forkert) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository - Move CI charts to project graphs area -- cgit v1.2.1 From 297f969babcd509b95b08e6531e09e4a75018d8c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 24 Sep 2015 16:25:40 +0200 Subject: Fix specs --- app/services/ci/create_project_service.rb | 2 +- spec/requests/api/projects_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/ci/create_project_service.rb b/app/services/ci/create_project_service.rb index 839d3f6b444..f42babd2388 100644 --- a/app/services/ci/create_project_service.rb +++ b/app/services/ci/create_project_service.rb @@ -10,7 +10,7 @@ module Ci gl_project = ::Project.find(@project.gitlab_id) gl_project.build_missing_services - gl_project.gitlab_ci_service.update_attributes(active: true, token: @project.token) + gl_project.gitlab_ci_service.update_attributes(active: true) end if forked_project diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 3007a15b0b1..580bbec77d1 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -89,7 +89,7 @@ describe API::API, api: true do it 'returns projects in the correct order when ci_enabled_first parameter is passed' do [project, project2, project3].each{ |project| project.build_missing_services } - project2.gitlab_ci_service.update(active: true, token: "token") + project2.gitlab_ci_service.update(active: true) get api('/projects', user), { ci_enabled_first: 'true' } expect(response.status).to eq(200) expect(json_response).to be_an Array -- cgit v1.2.1 From 452621208caecec6d4b11f94e17e3a5ed0519baa Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 23 Sep 2015 08:42:32 -0700 Subject: Fix default avatar not showing up in network graph Closes https://github.com/gitlabhq/gitlabhq/issues/9657 --- CHANGELOG | 1 + app/views/projects/network/show.json.erb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 710f99cbf51..7e96d9bbbc5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.1.0 (unreleased) - Remove "Continuous Integration" page from dashboard v 8.0.2 (unreleased) + - Fix default avatar not rendering in network graph (Stan Hu) - Skip check_initd_configured_correctly on omnibus installs - Prevent double-prefixing of help page paths - Clarify confirmation text on user deletion diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb index dc82adcb2c6..122e84b41b2 100644 --- a/app/views/projects/network/show.json.erb +++ b/app/views/projects/network/show.json.erb @@ -9,7 +9,7 @@ author: { name: c.author_name, email: c.author_email, - icon: avatar_icon(c.author_email, 20) + icon: image_path(avatar_icon(c.author_email, 20)) }, time: c.time, space: c.spaces.first, -- cgit v1.2.1 From 040718e10695e7d59e1aa06fd0a91b04ef692014 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 24 Sep 2015 17:47:48 +0200 Subject: Refer to Apache templates in 8.0 update guide --- doc/update/7.14-to-8.0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 2c7003ed063..c2b06fe1ab1 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -153,6 +153,8 @@ 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 ``` +If you are using Apache instead of NGINX please see the updated [Apache templates](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache). + ### 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. -- cgit v1.2.1 From 5d69bc656a80889b6e7eae2edd717c7e963ec48a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 24 Sep 2015 17:47:57 +0200 Subject: Fix grack auth spec --- spec/lib/gitlab/backend/grack_auth_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index 9bed8f8ee5c..e903c15a4c8 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -179,7 +179,7 @@ describe Grack::Auth do before do gitlab_ci_service = project.build_gitlab_ci_service gitlab_ci_service.active = true - gitlab_ci_service.token = token + gitlab_ci_service.stub(:token).and_return(token) gitlab_ci_service.save env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) -- cgit v1.2.1 From 22db4398c69e75da8c56775a7c815b6e2cb38496 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 24 Sep 2015 12:33:11 -0400 Subject: api: expose note_events and enable_ssl_verification for hooks --- doc/api/projects.md | 2 ++ lib/api/entities.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 10533c73a31..9f9a274a7e8 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -515,6 +515,8 @@ Parameters: "push_events": "true", "issues_events": "true", "merge_requests_events": "true", + "note_events": "true", + "enable_ssl_verification": "true", "created_at": "2012-10-12T17:04:47Z" } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 33b6224a810..9620d36ac41 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -45,7 +45,7 @@ module API class ProjectHook < Hook expose :project_id, :push_events - expose :issues_events, :merge_requests_events, :tag_push_events + expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification end class ForkedFromProject < Grape::Entity -- cgit v1.2.1 From b07f48d52e5846fc266d19ef1291fd8167fb1d33 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 24 Sep 2015 12:33:58 -0400 Subject: doc: list note_events as a valid parameter for PUT/POST hooks --- doc/api/projects.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/projects.md b/doc/api/projects.md index 9f9a274a7e8..fc22f58acef 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -537,6 +537,7 @@ Parameters: - `issues_events` - Trigger hook on issues events - `merge_requests_events` - Trigger hook on merge_requests events - `tag_push_events` - Trigger hook on push_tag events +- `note_events` - Trigger hook on note events ### Edit project hook @@ -555,6 +556,7 @@ Parameters: - `issues_events` - Trigger hook on issues events - `merge_requests_events` - Trigger hook on merge_requests events - `tag_push_events` - Trigger hook on push_tag events +- `note_events` - Trigger hook on note events ### Delete project hook -- cgit v1.2.1 From eb912a534bee312d5d5e5bcc44767860c1c41864 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 24 Sep 2015 12:34:16 -0400 Subject: api: add enable_ssl_verification to PUT/POST hooks --- doc/api/projects.md | 2 ++ lib/api/project_hooks.rb | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index fc22f58acef..96485857035 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -538,6 +538,7 @@ Parameters: - `merge_requests_events` - Trigger hook on merge_requests events - `tag_push_events` - Trigger hook on push_tag events - `note_events` - Trigger hook on note events +- `enable_ssl_verification` - Do SSL verification when triggering the hook ### Edit project hook @@ -557,6 +558,7 @@ Parameters: - `merge_requests_events` - Trigger hook on merge_requests events - `tag_push_events` - Trigger hook on push_tag events - `note_events` - Trigger hook on note events +- `enable_ssl_verification` - Do SSL verification when triggering the hook ### Delete project hook diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index ad4d2e65dfd..882d1a083ad 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -44,7 +44,8 @@ module API :issues_events, :merge_requests_events, :tag_push_events, - :note_events + :note_events, + :enable_ssl_verification ] @hook = user_project.hooks.new(attrs) @@ -75,7 +76,8 @@ module API :issues_events, :merge_requests_events, :tag_push_events, - :note_events + :note_events, + :enable_ssl_verification ] if @hook.update_attributes attrs -- cgit v1.2.1 From 1f78c5d46053ad37c280f0bc5273522d76888e63 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 24 Sep 2015 18:36:15 +0200 Subject: UI changes to the project view, empty project and project list new project button --- app/assets/stylesheets/base/mixins.scss | 81 +++++-- app/assets/stylesheets/base/variables.scss | 4 +- app/assets/stylesheets/generic/avatar.scss | 2 + app/assets/stylesheets/generic/typography.scss | 22 +- app/assets/stylesheets/pages/projects.scss | 296 +++++++++++++++++++++++-- app/views/projects/_home_panel.html.haml | 29 +-- app/views/projects/buttons/_fork.html.haml | 1 - app/views/projects/buttons/_star.html.haml | 5 - app/views/shared/_clone_panel.html.haml | 2 +- 9 files changed, 373 insertions(+), 69 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index a2f6c3e21f4..cdebe498914 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -93,46 +93,89 @@ } h1 { - margin-top: 45px; - font-size: 2.5em; + font-size: 1.3em; + font-weight: 600; + margin: 24px 0 12px 0; + padding: 0 0 10px 0; + border-bottom: 1px solid #e7e9ed; + color: #313236; } h2 { - margin-top: 40px; - font-size: 2em; + font-size: 1.2em; + font-weight: 600; + margin: 24px 0 12px 0; + color: #313236; } h3 { - margin-top: 35px; - font-size: 1.5em; + margin: 24px 0 12px 0; + font-size: 1.25em; } h4 { - margin-top: 30px; - font-size: 1.2em; + margin: 24px 0 12px 0; + font-size: 1.1em; + } + + h5 { + margin: 24px 0 12px 0; + font-size: 1em; + } + + h6 { + margin: 24px 0 12px 0; + font-size: 0.90em; } blockquote { - color: #888; + padding: 8px 21px; + margin: 12px 0 12px; + border-left: 3px solid #e7e9ed; + } + + blockquote p { + color: #7f8fa4 !important; font-size: 15px; line-height: 1.5; } - + + p { + color:#5c5d5e; + margin:6px 0 0 0; + } + table { @extend .table; @extend .table-bordered; + margin: 12px 0 12px 0; + color: #5c5d5e; th { - background: #EEE; - } + background: #f8fafc; + } + } + + pre { + @include border-radius(2px); + + margin: 12px 0 12px 0 !important; + background-color: #f8fafc !important; + font-size: 13px !important; + color: #5b6169 !important; + line-height: 1.6em !important; } p > code { - font-size: inherit; font-weight: inherit; } + + ul { + color: #5c5d5e; + } + li { - line-height: 1.5; + line-height: 1.6em; } a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { @@ -152,6 +195,7 @@ } } + @mixin str-truncated($max_width: 82%) { display: inline-block; overflow: hidden; @@ -183,7 +227,7 @@ &.active { background: #f9f9f9; a { - font-weight: bold; + font-weight: 600; } } @@ -199,6 +243,11 @@ } } +.pull-right .light .fa-pencil { + top: 20px; + position: relative; +} + @mixin input-big { height: 36px; padding: 5px 10px; @@ -250,4 +299,4 @@ color: #78a; } } -} +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss index 2fc7bf1720a..f6bdea9a897 100644 --- a/app/assets/stylesheets/base/variables.scss +++ b/app/assets/stylesheets/base/variables.scss @@ -12,8 +12,8 @@ $sidebar_width: 230px; $avatar_radius: 50%; $code_font_size: 13px; $code_line_height: 1.5; -$border-color: #E7E9ED; -$background-color: #F8FAFC; +$border-color: #dce0e6; +$background-color: #F7F8FA; $header-height: 58px; $fixed-layout-width: 1200px; $gl-gray: #7f8fa4; diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss index 221cb6a04a5..36e582d4854 100644 --- a/app/assets/stylesheets/generic/avatar.scss +++ b/app/assets/stylesheets/generic/avatar.scss @@ -28,6 +28,7 @@ &.s48 { width: 48px; height: 48px; margin-right: 10px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; } + &.s110 { width: 110px; height: 110px; margin-right: 15px; } &.s140 { width: 140px; height: 140px; margin-right: 20px; } &.s160 { width: 160px; height: 160px; margin-right: 20px; } } @@ -42,6 +43,7 @@ &.s32 { font-size: 22px; line-height: 32px; } &.s60 { font-size: 32px; line-height: 60px; } &.s90 { font-size: 36px; line-height: 90px; } + &.s110 { font-size: 40px; line-height: 112px; font-weight: 300; } &.s140 { font-size: 72px; line-height: 140px; } &.s160 { font-size: 96px; line-height: 160px; } } diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index d5f0d86a307..bfb559c294b 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -2,11 +2,17 @@ * Headers * */ + + body { + text-rendering: optimizeLegibility + } + .page-title { margin-top: 0px; - line-height: 1.5; - font-weight: normal; - margin-bottom: 5px; + line-height: 1.3; + font-size: 1.25em; + font-weight: 600; + margin: 12px 7px 12px 7px; } h1, h2, h3, h4, h5, h6 { @@ -55,6 +61,7 @@ a > code { @include md-typography; word-wrap: break-word; + padding: 7px; /* Link to current header. */ h1, h2, h3, h4, h5, h6 { @@ -83,9 +90,12 @@ a > code { } } - ul { + ul,ol { padding: 0; - margin: 0 0 9px 25px !important; + margin: 6px 0 6px 18px !important; + } + ol { + color: #5c5d5e; } } @@ -111,4 +121,4 @@ textarea.js-gfm-input { .strikethrough { text-decoration: line-through; -} +} \ No newline at end of file diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index a986fafff07..621ba2fe2c8 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -19,10 +19,10 @@ background: #f7f8fa; margin: -$gl-padding; padding: $gl-padding; - padding-top: 40px; + padding: 44px 0 17px 0; .project-identicon-holder { - margin-bottom: 15px; + margin-bottom: 16px; .avatar, .identicon { margin: 0 auto; @@ -40,23 +40,26 @@ .project-home-desc { h1 { + color: #313236; margin: 0; - margin-bottom: 10px; + margin-bottom: 6px; font-size: 23px; font-weight: normal; } p { - color: #7f8fa4; + color: #5c5d5e; } } .git-clone-holder { - max-width: 600px; - margin: 20px auto; + max-width: 498px; .form-control { background: #FFF; + font-size: 14px; + height: 42px; + margin-left: -2px; } } @@ -66,30 +69,66 @@ color: inherit; } } - + .input-group { + display: inline-table; + position: relative; + top: 17px; + margin-bottom: 44px; + } .project-repo-buttons { - margin-top: $gl-padding; - margin-bottom: 25px; + margin-top: 12px; + margin-bottom: 0px; .btn { @extend .btn-info; - + @include border-radius(2px); + + background-color: #f0f2f5; + border: 1px solid #dce0e5; text-transform: uppercase; - font-size: 15px; - line-height: 20px; - padding: 8px 14px; - border-radius: 3px; - margin-left: 10px; + color: #313236; + font-size: 13px; + font-weight: 600; + line-height: 18px; + padding: 11px 16px; + margin-left: 12px; + + &:hover { + @include border-radius(2px); + color: #313236; + border: 1px solid #dce0e5; + background-color: #ebeef2; + } + + &:focus { + @include border-radius(2px); + color: #313236; + border: 1px solid #dce0e5; + background-color: #ebeef2; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + color: #313236 !important; + border: 1px solid #c6cacf !important; + background-color: #e4e7ed !important; + } .count { - padding-left: 7px; display: inline-block; - margin-left: 7px; } } } } +.split-one { + display: inline-table; + + a { + margin: -1px !important; + } +} + .git-clone-holder { .project-home-dropdown + & { margin-right: 45px; @@ -103,7 +142,7 @@ } .input-group-addon { - background: #FAFAFA; + background: #f7f8fa; &.git-protocols { padding: 0; @@ -111,11 +150,157 @@ .input-group-btn:last-child > .btn { @include border-radius-right(0); + + border-left: 1px solid #c6cacf; + margin-left: -2px !important; + } + input-group-btn:first-child > .btn { + @include border-radius-left(0); } } } } +.projects-search-form { + + .input-group .btn-success { + background-color: #2ebf6b !important; + border: 1px solid #28b061 !important; + color: #fff !important; + + &:hover { + background-color: #2eb867 !important; + } + + &:focus { + background-color: #2eb867 !important; + } + + &:active { + @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); + + background-color: #28b061 !important; + border: 1px solid #26a65c !important; + color: #fff !important; + } + } + + .input-group .form-control { + height: 39px !important; + } + +} + +.input-group-btn > .btn { + background-color: #f0f2f5; + border: 1px solid #dce0e5; + text-transform: uppercase; + color: #313236; + font-size: 13px; + font-weight: 600; + + &:focus { + outline: none; + color: #313236; + border: 1px solid #dce0e5; + background-color: #ebeef2; + } + + &:hover { + outline: none; + color: #313236; + border: 1px solid #dce0e5; + background-color: #ebeef2; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border: 1px solid #c6cacf !important; + background-color: #e4e7ed !important; + } + +} + +.input-group-btn > .btn.active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + color: #313236 !important; + border: 1px solid #c6cacf !important; + background-color: #e4e7ed !important; +} + +.split-repo-buttons { + display: inline-table; + margin-left: 12px; + + .btn { + margin: 0 !important; + } + + .dropdown { + margin: 0 !important; + } + + .dropdown-toggle { + margin: -5px !important; + } +} + +#notification-form { + margin-left: 5px; +} + +.open > .dropdown-toggle.btn { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + border: 1px solid #c6cacf !important; + background-color: #e4e7ed !important; + text-transform: uppercase; + color: #313236; + font-size: 13px; + font-weight: 600; + color: #313236 !important; +} + +.dropdown-menu { + @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); + @include border-radius (0px); + + border: none; + padding: 16px 0; + font-size: 14px; + font-weight: 100; + + li a { + color: #5f697a; + line-height: 30px; + + &:hover { + background-color: #3084bb !important; + } + } + + .fa-fw { + margin-right: 8px; + } +} + +.fa-bell { + margin-right: 6px; +} + +.fa-angle-down { + margin-left: 6px; +} + +.project-home-panel .project-home-dropdown { + margin: 13px 0px 0; +} + .project-visibility-level-holder { .radio { margin-bottom: 10px; @@ -232,15 +417,48 @@ table.table.protected-branches-list tr.no-border { .project-stats { text-align: center; - margin-top: 0; + margin-top: 15px; margin-bottom: 0; - padding-top: 5px; - padding-bottom: 0; + padding-top: 10px; + padding-bottom: 4px; ul.nav-pills { display:inline-block; } + + .nav-pills li { + display:inline; + } + .nav > li > a { + border: 1px solid transparent; + color: #313236; + font-size: 13px; + font-weight: 600; + text-decoration: none; + text-transform: uppercase; + margin: 0 8px 0 0; + padding: 10px 16px 10px 16px; + + &:hover { + @include border-radius(2px); + border: 1px solid #dce0e5; + background-color: #f0f2f5; + } + + &:focus { + @include border-radius(2px); + border: 1px solid #dce0e5; + background-color: #f0f2f5; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + border: 1px solid #c6cacf; + background-color: #e4e7ed; + } + } + li { display:inline; } @@ -251,11 +469,11 @@ table.table.protected-branches-list tr.no-border { } li.missing a { - color: #bbb; - border: 1px dashed #ccc; + color: #5a6069; + border: 1px dashed #dce0e5; &:hover { - background-color: #FAFAFA; + background-color: #f0f2f5; } } } @@ -273,6 +491,36 @@ pre.light-well { border-bottom: 1px solid #e7e9ed; } +.git-empty { + margin: 0 7px 0 7px; + + h5 { + color: #5c5d5e; + } + + .light-well { + @include border-radius (2px); + + color: #5b6169 !important; + font-size: 13px !important; + line-height: 1.6em !important; + } +} + +.prepend-top-20 { + margin: 20px 8px 20px 8px; + + .btn-remove { + @include border-radius (2px); + + font-size: 13px; + font-weight: 600px; + text-transform: uppercase; + float: left !important; + margin-bottom: 14px; + } +} + /* * Projects list rendered on dashboard and user page */ diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 6e53f55b0ab..8c0980369fd 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -16,18 +16,19 @@ .project-repo-buttons - = render 'projects/buttons/star' - - - unless empty_repo - = render 'projects/buttons/fork' - - - if can? current_user, :download_code, @project - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do - = icon('download fw') - Download - + .split-one + = render 'projects/buttons/star' + + - unless empty_repo + = render 'projects/buttons/fork' + + = render "shared/clone_panel" + .split-repo-buttons + - unless empty_repo + - if can? current_user, :download_code, @project + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do + = icon('download fw') + + = render 'projects/buttons/dropdown' = render 'projects/buttons/notifications' - - = render 'projects/buttons/dropdown' - - = render "shared/clone_panel" + diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 854c154824d..8f2f631eb7d 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -8,6 +8,5 @@ - else = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn' do = icon('code-fork fw') - Fork %span.count = @project.forks_count diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 5d7df5ae099..3501dddefbe 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,10 +1,6 @@ - if current_user = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star', method: :post, remote: true do = icon('star fw') - - if current_user.starred?(@project) - Unstar - - else - Star %span.count = @project.star_count @@ -17,6 +13,5 @@ - else = link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do = icon('star fw') - Star %span.count = @project.star_count diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 2cd422e772a..b23b2f0d5eb 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -24,4 +24,4 @@ .input-group-addon .visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(project.visibility_level)} project" } = visibility_level_icon(project.visibility_level) - = visibility_level_label(project.visibility_level).downcase + -- cgit v1.2.1 From 58c21dd77b5a6573d50a62dc17755a6519f4a9c8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 24 Sep 2015 12:55:00 -0400 Subject: Update Troubleshooting sections --- doc/install/installation.md | 8 ++++---- doc/update/7.14-to-8.0.md | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 282c84fd1e1..039bb3c2561 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -524,7 +524,7 @@ Apart from the always supported markdown style there are other rich text files t ### "You appear to have cloned an empty repository." If you see this message when attempting to clone a repository hosted by GitLab, -this is likely due to a missing or misconfigured `gitlab-git-http-server` -instance. Double-check that you've [installed Go](#3-go), [installed -gitlab-git-http-server](#install-gitlab-git-http-server), and correctly -[configured Nginx](#site-configuration). +this is likely due to an outdated Nginx or Apache configuration, or a missing or +misconfigured `gitlab-git-http-server` instance. Double-check that you've +[installed Go](#3-go), [installed gitlab-git-http-server](#install-gitlab-git-http-server), +and correctly [configured Nginx](#site-configuration). diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index e25b38a3533..30d1826b473 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -196,6 +196,7 @@ If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of ### "You appear to have cloned an empty repository." If you see this message when attempting to clone a repository hosted by GitLab, -this is likely due to a missing or misconfigured `gitlab-git-http-server` -instance. Double-check that you correctly completed [Step 5](#5-install-gitlab-git-http-server) -to install the daemon and [Step 8](#new-nginx-configuration) to reconfigure Nginx. +this is likely due to an outdated Nginx or Apache configuration, or a missing or +misconfigured `gitlab-git-http-server` instance. Double-check that you correctly +completed [Step 5](#5-install-gitlab-git-http-server) to install the daemon and +[Step 8](#new-nginx-configuration) to reconfigure Nginx. -- cgit v1.2.1 From 7d2655aae78604f8cc09e961868d520b23c102ce Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Thu, 24 Sep 2015 13:52:20 -0400 Subject: Remove 'kerberos' from auth_helper.rb for gitlab-CE. There is no Kerberos auth in gitlab-ce, so it shouldn't be noted as a form-driven auth mechanism in app/helpers/auth_helper.rb. This breaks using Kerberos as a custom omniauth provider. See issue #2510 --- 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 ce7e9b1db87..cd99a232403 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -1,6 +1,6 @@ module AuthHelper PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze - FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze + FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze def ldap_enabled? Gitlab.config.ldap.enabled -- cgit v1.2.1 From ce19e1739ef8bfbc192be6e5f7effe6d9feb777d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 24 Sep 2015 14:12:25 -0400 Subject: Update stub syntax --- spec/lib/gitlab/backend/grack_auth_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index e903c15a4c8..b0b2f3f72f1 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -179,7 +179,7 @@ describe Grack::Auth do before do gitlab_ci_service = project.build_gitlab_ci_service gitlab_ci_service.active = true - gitlab_ci_service.stub(:token).and_return(token) + allow(gitlab_ci_service).to receive(:token).and_return(token) gitlab_ci_service.save env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) -- cgit v1.2.1 From 04c4445ae088f2599659522c70a096811ecd0496 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 24 Sep 2015 20:18:26 +0200 Subject: Test grace auth against gitlab_ci_project with token --- spec/lib/gitlab/backend/grack_auth_spec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index b0b2f3f72f1..829a9c197ef 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -175,11 +175,14 @@ describe Grack::Auth do context "when a gitlab ci token is provided" do let(:token) { "123" } + let(:gitlab_ci_project) { FactoryGirl.create :ci_project, token: token } before do + project.gitlab_ci_project = gitlab_ci_project + project.save + gitlab_ci_service = project.build_gitlab_ci_service gitlab_ci_service.active = true - allow(gitlab_ci_service).to receive(:token).and_return(token) gitlab_ci_service.save env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) -- cgit v1.2.1 From 869bab9d68b2df9089fb8bfd42ca1bfee6e4baf3 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 24 Sep 2015 14:16:36 -0400 Subject: hooks: improve tests for hook API --- CHANGELOG | 1 + spec/requests/api/project_hooks_spec.rb | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 710f99cbf51..62fb2202569 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.1.0 (unreleased) - Show CI status on commit page - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard + - Add notes and SSL verification entries to hook APIs (Ben Boeckel) v 8.0.2 (unreleased) - Skip check_initd_configured_correctly on omnibus installs diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index 5037575d355..606b226ad77 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -5,7 +5,7 @@ describe API::API, 'ProjectHooks', api: true do let(:user) { create(:user) } let(:user3) { create(:user) } let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } + let!(:hook) { create(:project_hook, project: project, url: "http://example.com", push_events: true, merge_requests_events: true, tag_push_events: true, issues_events: true, note_events: true, enable_ssl_verification: true) } before do project.team << [user, :master] @@ -21,6 +21,12 @@ describe API::API, 'ProjectHooks', api: true do expect(json_response).to be_an Array expect(json_response.count).to eq(1) expect(json_response.first['url']).to eq("http://example.com") + expect(json_response.first['issues_events']).to eq(true) + expect(json_response.first['push_events']).to eq(true) + expect(json_response.first['merge_requests_events']).to eq(true) + expect(json_response.first['tag_push_events']).to eq(true) + expect(json_response.first['note_events']).to eq(true) + expect(json_response.first['enable_ssl_verification']).to eq(true) end end @@ -38,6 +44,12 @@ describe API::API, 'ProjectHooks', api: true do get api("/projects/#{project.id}/hooks/#{hook.id}", user) expect(response.status).to eq(200) expect(json_response['url']).to eq(hook.url) + expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['push_events']).to eq(hook.push_events) + expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) + expect(json_response['tag_push_events']).to eq(hook.tag_push_events) + expect(json_response['note_events']).to eq(hook.note_events) + expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) end it "should return a 404 error if hook id is not available" do @@ -65,6 +77,13 @@ describe API::API, 'ProjectHooks', api: true do post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true end.to change {project.hooks.count}.by(1) expect(response.status).to eq(201) + expect(json_response['url']).to eq('http://example.com') + expect(json_response['issues_events']).to eq(true) + expect(json_response['push_events']).to eq(true) + expect(json_response['merge_requests_events']).to eq(false) + expect(json_response['tag_push_events']).to eq(false) + expect(json_response['note_events']).to eq(false) + expect(json_response['enable_ssl_verification']).to eq(true) end it "should return a 400 error if url not given" do @@ -84,6 +103,12 @@ describe API::API, 'ProjectHooks', api: true do url: 'http://example.org', push_events: false expect(response.status).to eq(200) expect(json_response['url']).to eq('http://example.org') + expect(json_response['issues_events']).to eq(hook.issues_events) + expect(json_response['push_events']).to eq(false) + expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) + expect(json_response['tag_push_events']).to eq(hook.tag_push_events) + expect(json_response['note_events']).to eq(hook.note_events) + expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) end it "should return 404 error if hook id not found" do -- cgit v1.2.1 From 04de85a994feb9fe0503ccf1bfc184333d642cc3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 24 Sep 2015 22:48:50 -0700 Subject: Fix Error 500 occuring with repositories that have a bad HEAD A repository could have objects but no valid HEAD, causing `project.commit` to be `nil`. --- app/views/shared/projects/_project.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 1c529c91c51..e67e5a8a638 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -18,7 +18,7 @@ = project.name .project-controls - - if ci && !project.empty_repo? + - if ci && !project.empty_repo? && project.commit - if ci_commit = project.ci_commit(project.commit.sha) = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do -- cgit v1.2.1 From 1868c8b25842cf24c79b8abf62ce5c177e5b82e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 25 Sep 2015 10:04:09 +0200 Subject: Fix tests Signed-off-by: Dmitriy Zaporozhets --- features/steps/project/fork.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index 370960845cc..b0230add34f 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -5,8 +5,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps step 'I click link "Fork"' do expect(page).to have_content "Shop" - expect(page).to have_content "Fork" - click_link "Fork" + click_link "Fork project" end step 'I am a member of project "Shop"' do -- cgit v1.2.1 From fbe5bf762d616c8bbe6e824bd0713e20f0751b74 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 01:14:54 -0700 Subject: Fix bug where projects would appear to be stuck in the forked import state A race condition existed between when Rails committed the `import_status` to `started` and when the Sidekiq worker forked a project. If this fork was quick, it's possible that the worker would attempt to move into the `finished` state before the `started` state took effect. As mentioned in https://github.com/mperham/sidekiq/wiki/Problems-and-Troubleshooting#cannot-find-modelname-with-id12345, we should delay the worker to ensure the DB has a chance to update. Closes #2736 --- CHANGELOG | 1 + app/models/project.rb | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 20b6dce1764..256e445d7df 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.1.0 (unreleased) + - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository - Move CI charts to project graphs area diff --git a/app/models/project.rb b/app/models/project.rb index a7ea1236b86..5deddb2fbc4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -276,8 +276,10 @@ class Project < ActiveRecord::Base end def add_import_job + # Schedule these jobs after 2 seconds to ensure DB changes to import_status + # are saved by the time the workers start if forked? - unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) + unless RepositoryForkWorker.perform_in(2.seconds, id, forked_from_project.path_with_namespace, self.namespace.path) import_fail end else -- cgit v1.2.1 From bd2991b4618528a719b03c46ceb7befb230f0dd9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 01:46:13 -0700 Subject: Use after_commit_queue to schedule import job immediately --- Gemfile | 2 ++ Gemfile.lock | 3 +++ app/models/project.rb | 13 ++++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index ad9fef38da1..6950091b2b0 100644 --- a/Gemfile +++ b/Gemfile @@ -121,6 +121,8 @@ end # State machine gem "state_machine", '~> 1.2.0' +# Run events after state machine commits +gem 'after_commit_queue' # Issue tags gem 'acts-as-taggable-on', '~> 3.4' diff --git a/Gemfile.lock b/Gemfile.lock index e306c1f56e6..4386c6b9abb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,8 @@ GEM acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) addressable (2.3.8) + after_commit_queue (1.1.0) + rails (>= 3.0) annotate (2.6.10) activerecord (>= 3.2, <= 4.3) rake (~> 10.4) @@ -787,6 +789,7 @@ DEPENDENCIES activerecord-session_store (~> 0.1.0) acts-as-taggable-on (~> 3.4) addressable (~> 2.3.8) + after_commit_queue annotate (~> 2.6.0) asana (~> 0.0.6) asciidoctor (~> 1.5.2) diff --git a/app/models/project.rb b/app/models/project.rb index 5deddb2fbc4..e912c48467d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -39,6 +39,7 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel include Referable include Sortable + include AfterCommitQueue extend Gitlab::ConfigHelper extend Enumerize @@ -191,7 +192,7 @@ class Project < ActiveRecord::Base state :finished state :failed - after_transition any => :started, do: :add_import_job + after_transition any => :started, do: :schedule_add_import_job after_transition any => :finished, do: :clear_import_data end @@ -275,15 +276,17 @@ class Project < ActiveRecord::Base id && persisted? end + def schedule_add_import_job + run_after_commit(:add_import_job) + end + def add_import_job - # Schedule these jobs after 2 seconds to ensure DB changes to import_status - # are saved by the time the workers start if forked? - unless RepositoryForkWorker.perform_in(2.seconds, id, forked_from_project.path_with_namespace, self.namespace.path) + unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) import_fail end else - RepositoryImportWorker.perform_in(2.seconds, id) + RepositoryImportWorker.perform_async(id) end end -- cgit v1.2.1 From 1c9deece3de01cc547852f4d0cc16f1741c78403 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 10:59:36 +0200 Subject: Make sure Unicorn listens on localhost:8080 --- doc/update/7.14-to-8.0.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index ed5c6c99e78..86c0825dff9 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -87,6 +87,19 @@ cd gitlab-git-http-server sudo -u git -H make ``` +Make sure your unicorn.rb file contains a 'listen' line for +'127.0.0.1:8080' and that this line is not commented out. + +``` +cd /home/git/gitlab +grep ^listen config/unicorn.rb + +# If there is no 'listen' line for 127.0.0.1:8080, add it: +sudo -u git tee -a config/unicorn.rb < true +EOF +``` + 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. -- cgit v1.2.1 From a44ca15c1dd71381d6b11638003f60253c2d8964 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 02:39:34 -0700 Subject: Fix Error 500 in creating merge requests with > 1000 diffs Closes #2692 --- CHANGELOG | 1 + app/models/merge_request_diff.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 99e327472f4..68305b3b380 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.1.0 (unreleased) + - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository - Move CI charts to project graphs area diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index e317c8eac4d..f75f999b0d0 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -123,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base if new_diffs.any? if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES self.state = :overflow_diff_files_limit - new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] + new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES) end if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES self.state = :overflow_diff_lines_limit - new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] + new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES) end end -- cgit v1.2.1 From 255d5f7501b01f2e88d614bf38b362a31d6cddc6 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 14:38:42 +0200 Subject: Improve 'rake gitlab:cleanup:repos' documentation --- doc/raketasks/cleanup.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/raketasks/cleanup.md b/doc/raketasks/cleanup.md index 96d67f7b5d6..8fbcbb983e9 100644 --- a/doc/raketasks/cleanup.md +++ b/doc/raketasks/cleanup.md @@ -12,7 +12,8 @@ sudo gitlab-rake gitlab:cleanup:dirs bundle exec rake gitlab:cleanup:dirs RAILS_ENV=production ``` -Remove repositories (global only for now) from `/home/git/repositories` if they don't exist in GitLab database. +Rename repositories from `/home/git/repositories` if they don't exist in GitLab database. +The repositories get a `+orphaned+TIMESTAMP` suffix so that they cannot block new repositories from being created. ``` # omnibus-gitlab -- cgit v1.2.1 From 352f242d8ca491172db49d458f3eae31c70270c8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 25 Sep 2015 17:46:03 +0200 Subject: Fix admin runner page -> assign all button Signed-off-by: Dmitriy Zaporozhets --- app/models/ci/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index a52e28615f7..8fb54b90d61 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -100,7 +100,7 @@ module Ci 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) + where("#{Ci::RunnerProject.table_name}.project_id" => nil) end def ordered_by_last_commit_date -- cgit v1.2.1 From b328c76c063cfe3082316e4273cfd318bbbfe69b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 25 Sep 2015 18:03:41 +0200 Subject: Move runners page to project settings Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 3 +- app/controllers/ci/runner_projects_controller.rb | 8 ++- app/controllers/ci/runners_controller.rb | 73 ---------------------- app/controllers/projects/runners_controller.rb | 69 ++++++++++++++++++++ app/helpers/gitlab_routing_helper.rb | 8 +++ 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/layouts/ci/_nav_project.html.haml | 5 -- app/views/layouts/nav/_project_settings.html.haml | 6 ++ app/views/projects/runners/_runner.html.haml | 34 ++++++++++ .../projects/runners/_shared_runners.html.haml | 23 +++++++ .../projects/runners/_specific_runners.html.haml | 29 +++++++++ app/views/projects/runners/edit.html.haml | 27 ++++++++ app/views/projects/runners/index.html.haml | 25 ++++++++ app/views/projects/runners/show.html.haml | 64 +++++++++++++++++++ config/routes.rb | 15 +++-- 20 files changed, 299 insertions(+), 293 deletions(-) delete mode 100644 app/controllers/ci/runners_controller.rb create mode 100644 app/controllers/projects/runners_controller.rb delete mode 100644 app/views/ci/runners/_runner.html.haml delete mode 100644 app/views/ci/runners/_shared_runners.html.haml delete mode 100644 app/views/ci/runners/_specific_runners.html.haml delete mode 100644 app/views/ci/runners/edit.html.haml delete mode 100644 app/views/ci/runners/index.html.haml delete mode 100644 app/views/ci/runners/show.html.haml create mode 100644 app/views/projects/runners/_runner.html.haml create mode 100644 app/views/projects/runners/_shared_runners.html.haml create mode 100644 app/views/projects/runners/_specific_runners.html.haml create mode 100644 app/views/projects/runners/edit.html.haml create mode 100644 app/views/projects/runners/index.html.haml create mode 100644 app/views/projects/runners/show.html.haml diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 6111753a7fb..13766fb8f6f 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -55,7 +55,8 @@ module Ci def toggle_shared_runners project.toggle!(:shared_runners_enabled) - redirect_to :back + + redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project) end def dumped_yaml diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb index a8bdd5bb362..97f01d40af5 100644 --- a/app/controllers/ci/runner_projects_controller.rb +++ b/app/controllers/ci/runner_projects_controller.rb @@ -11,10 +11,12 @@ module Ci return head(403) unless current_user.ci_authorized_runners.include?(@runner) + path = runners_path(@project.gl_project) + if @runner.assign_to(project, current_user) - redirect_to ci_project_runners_path(project) + redirect_to path else - redirect_to ci_project_runners_path(project), alert: 'Failed adding runner to project' + redirect_to path, alert: 'Failed adding runner to project' end end @@ -22,7 +24,7 @@ module Ci runner_project = project.runner_projects.find(params[:id]) runner_project.destroy - redirect_to ci_project_runners_path(project) + redirect_to runners_path(@project.gl_project) end private diff --git a/app/controllers/ci/runners_controller.rb b/app/controllers/ci/runners_controller.rb deleted file mode 100644 index a672370302b..00000000000 --- a/app/controllers/ci/runners_controller.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Ci - class RunnersController < Ci::ApplicationController - 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' - - def index - @runners = @project.runners.order('id DESC') - @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) - 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/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb new file mode 100644 index 00000000000..d59884a1dd7 --- /dev/null +++ b/app/controllers/projects/runners_controller.rb @@ -0,0 +1,69 @@ +class Projects::RunnersController < Projects::ApplicationController + before_action :ci_project + before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :authorize_admin_project! + + layout 'project_settings' + + def index + @runners = @ci_project.runners.order('id DESC') + @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) + end + + def edit + end + + def update + if @runner.update_attributes(runner_params) + redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' + else + redirect_to runner_path(@runner), alert: 'Runner was not updated.' + end + end + + def destroy + if @runner.only_for?(@ci_project) + @runner.destroy + end + + redirect_to runners_path(@project) + end + + def resume + if @runner.update_attributes(active: true) + redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' + else + redirect_to runner_path(@runner), alert: 'Runner was not updated.' + end + end + + def pause + if @runner.update_attributes(active: false) + redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' + else + redirect_to runner_path(@runner), alert: 'Runner was not updated.' + end + end + + def show + end + + protected + + def ci_project + @ci_project = @project.gitlab_ci_project + end + + def set_runner + @runner ||= @ci_project.runners.find(params[:id]) + end + + def runner_params + params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) + end +end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index e0816f4e714..4d9da6ff837 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -33,6 +33,14 @@ module GitlabRoutingHelper edit_namespace_project_path(project.namespace, project, *args) end + def runners_path(project, *args) + namespace_project_runners_path(project.namespace, project, *args) + end + + def runner_path(runner, *args) + namespace_project_runner_path(@project.namespace, @project, runner, *args) + end + def issue_path(entity, *args) namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) end diff --git a/app/views/ci/runners/_runner.html.haml b/app/views/ci/runners/_runner.html.haml deleted file mode 100644 index ef8622e2807..00000000000 --- a/app/views/ci/runners/_runner.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -%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_path(@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 deleted file mode 100644 index 944b3fd930d..00000000000 --- a/app/views/ci/runners/_shared_runners.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -%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 deleted file mode 100644 index 0604e7a46c5..00000000000 --- a/app/views/ci/runners/_specific_runners.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -%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 deleted file mode 100644 index 81c8e58ae2b..00000000000 --- a/app/views/ci/runners/edit.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -%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 deleted file mode 100644 index 529fb9c296d..00000000000 --- a/app/views/ci/runners/index.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -.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 deleted file mode 100644 index ffec495f85a..00000000000 --- a/app/views/ci/runners/show.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -= 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/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 7daf9342e42..9ebe7eabd8e 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -11,11 +11,6 @@ %span Commits %span.count= @project.commits.count - = nav_link path: ['runners#index', 'runners#show', 'runners#edit'] do - = link_to ci_project_runners_path(@project) do - = icon('cog fw') - %span - Runners = nav_link path: 'variables#show' do = link_to ci_project_variables_path(@project) do = icon('code fw') diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 857fb199957..a85dd71126c 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -34,3 +34,9 @@ %span Protected Branches + - if @project.gitlab_ci? + = nav_link(controller: :runners) do + = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do + = icon('cog fw') + %span + Runners diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml new file mode 100644 index 00000000000..e6b8a2e6fe7 --- /dev/null +++ b/app/views/projects/runners/_runner.html.haml @@ -0,0 +1,34 @@ +%li.runner{id: dom_id(runner)} + %h4 + = runner_status_icon(runner) + %span.monospace + - if @runners.include?(runner) + = link_to runner.short_sha, runner_path(runner) + %small + =link_to edit_namespace_project_runner_path(@project.namespace, @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', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - else + - runner_project = @ci_project.runner_projects.find_by(runner_id: runner) + = link_to 'Disable for this project', [:ci, @ci_project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - elsif runner.specific? + = form_for [:ci, @ci_project, @ci_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/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml new file mode 100644 index 00000000000..316ea747b14 --- /dev/null +++ b/app/views/projects/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 @ci_project.shared_runners_enabled + = link_to toggle_shared_runners_ci_project_path(@ci_project), class: 'btn btn-warning', method: :post do + Disable shared runners + - else + = link_to toggle_shared_runners_ci_project_path(@ci_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 partial: 'runner', collection: @shared_runners, as: :runner + - if @shared_runners_count > 10 + .light + and #{@shared_runners_count - 10} more... diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml new file mode 100644 index 00000000000..c13625c7e49 --- /dev/null +++ b/app/views/projects/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 #{@ci_project.token} + %li + Start runner! + + +- if @runners.any? + %h4.underlined-title Runners activated for this project + %ul.bordered-list.activated-specific-runners + = render partial: 'runner', collection: @runners, as: :runner + +- if @specific_runners.any? + %h4.underlined-title Available specific runners + %ul.bordered-list.available-specific-runners + = render partial: 'runner', collection: @specific_runners, as: :runner + = paginate @specific_runners diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml new file mode 100644 index 00000000000..66851d38316 --- /dev/null +++ b/app/views/projects/runners/edit.html.haml @@ -0,0 +1,27 @@ +%h4 Runner ##{@runner.id} +%hr += form_for @runner, url: runner_path(@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/projects/runners/index.html.haml b/app/views/projects/runners/index.html.haml new file mode 100644 index 00000000000..529fb9c296d --- /dev/null +++ b/app/views/projects/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/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml new file mode 100644 index 00000000000..ffec495f85a --- /dev/null +++ b/app/views/projects/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/config/routes.rb b/config/routes.rb index 4a07c449b4e..a81ed1b95f5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -55,13 +55,6 @@ Gitlab::Application.routes.draw do 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] @@ -652,8 +645,14 @@ Gitlab::Application.routes.draw do get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ } end end - end + resources :runners, only: [:index, :edit, :update, :destroy, :show] do + member do + get :resume + get :pause + end + end + end end end -- cgit v1.2.1 From 253d2320ab3d3473509d6abe4a62be90428f20c4 Mon Sep 17 00:00:00 2001 From: Paul Beattie Date: Thu, 10 Sep 2015 16:57:43 +0100 Subject: Add support for AWS S3 Server-Side Encryption support This adds support for AWS S3 SSE with S3 managed keys, this means the data is encrypted at rest and the encryption is handled transparently to the end user as well as in the AWS Console. This is optional and not required to make S3 uploads work. --- CHANGELOG | 1 + config/gitlab.yml.example | 30 ++++++++++++++++-------------- config/initializers/1_settings.rb | 1 + doc/raketasks/backup_restore.md | 2 ++ lib/backup/manager.rb | 7 ++++--- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8ff61cd6e9f..4172d10c8f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 8.0.2 (unreleased) - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu) - Fix Reply by email for non-UTF-8 messages. - Add option to use StartTLS with Reply by email IMAP server. + - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie) v 8.0.1 - Remove git refs used internally by GitLab from network graph (Stan Hu) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 15930fc9079..c7174f86014 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -159,7 +159,7 @@ 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' - + # 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. @@ -204,13 +204,13 @@ production: &base # 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, + # 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 + # the full name is determined using the attributes specified for # `first_name` and `last_name`. name: 'cn' first_name: 'givenName' @@ -252,28 +252,28 @@ production: &base # arguments, followed by optional 'args' which can be either a hash or an array. # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html providers: - # - { name: 'google_oauth2', + # - { name: 'google_oauth2', # label: 'Google', - # app_id: 'YOUR_APP_ID', + # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', # args: { access_type: 'offline', approval_prompt: '' } } - # - { name: 'twitter', - # app_id: 'YOUR_APP_ID', + # - { name: 'twitter', + # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' } - # - { name: 'github', + # - { name: 'github', # label: 'GitHub', - # app_id: 'YOUR_APP_ID', + # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'user:email' } } - # - { name: 'gitlab', + # - { name: 'gitlab', # label: 'GitLab.com', - # app_id: 'YOUR_APP_ID', + # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'api' } } - # - { name: 'bitbucket', - # app_id: 'YOUR_APP_ID', + # - { name: 'bitbucket', + # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' } - # - { name: 'saml', + # - { name: 'saml', # label: 'Our SAML Provider', # args: { # assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', @@ -319,6 +319,8 @@ production: &base # # Use multipart uploads when file size reaches 100MB, see # # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html # multipart_chunk_size: 104857600 + # # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional + # # encryption: 'AES256' ## GitLab Shell settings gitlab_shell: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 48601b67335..4e4a8ecbdb3 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -229,6 +229,7 @@ if Settings.backup['upload']['connection'] Settings.backup['upload']['connection'] = Hash[Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }] end Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 +Settings.backup['upload']['encryption'] ||= nil # # Git diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 4ff5e74d438..b212964436f 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -95,6 +95,8 @@ For installations from source: 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' + # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional + # encryption: 'AES256' ``` If you are uploading your backups to S3 you will probably want to create a new diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index ac63f89c6ec..5c42f25f4a2 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -45,7 +45,8 @@ module Backup directory = connection.directories.get(remote_directory) if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, - multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size) + multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, + encryption: Gitlab.config.backup.upload.encryption) $progress.puts "done".green else puts "uploading backup to #{remote_directory} failed".red @@ -55,7 +56,7 @@ module Backup def cleanup $progress.print "Deleting tmp directories ... " - + backup_contents.each do |dir| next unless File.exist?(File.join(Gitlab.config.backup.path, dir)) @@ -75,7 +76,7 @@ module Backup if keep_time > 0 removed = 0 - + Dir.chdir(Gitlab.config.backup.path) do file_list = Dir.glob('*_gitlab_backup.tar') file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ } -- cgit v1.2.1 From 8a1bc6040b5781620888da2892093c107bb90fb8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 25 Sep 2015 14:05:25 -0400 Subject: Add unreleased 8.0.3 entry to CHANGELOG [ci skip] --- CHANGELOG | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8ff61cd6e9f..b495ccc9b9e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,7 +13,9 @@ v 8.1.0 (unreleased) - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) -v 8.0.2 (unreleased) +v 8.0.3 (unreleased) + +v 8.0.2 - Fix default avatar not rendering in network graph (Stan Hu) - Skip check_initd_configured_correctly on omnibus installs - Prevent double-prefixing of help page paths @@ -21,6 +23,7 @@ v 8.0.2 (unreleased) - Make commit graphs responsive to window width changes (Stan Hu) - Fix top margin for sign-in button on public pages - Fix LDAP attribute mapping + - Remove git refs used internally by GitLab from network graph (Stan Hu) - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu) - Fix Reply by email for non-UTF-8 messages. - Add option to use StartTLS with Reply by email IMAP server. -- cgit v1.2.1 From 8f1da76f409c53fcf3db8d83be97bebdc6ed4998 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 25 Sep 2015 14:05:51 -0400 Subject: Remove CHANGELOG-CI [ci skip] --- CHANGELOG-CI | 298 ----------------------------------------------------------- 1 file changed, 298 deletions(-) delete mode 100644 CHANGELOG-CI diff --git a/CHANGELOG-CI b/CHANGELOG-CI deleted file mode 100644 index d1ad661d88b..00000000000 --- a/CHANGELOG-CI +++ /dev/null @@ -1,298 +0,0 @@ -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 -- cgit v1.2.1 From 12acf15c90d25a22e706737dc54f17466fb30320 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 25 Sep 2015 20:33:05 +0200 Subject: Project page Update refactoring buttons, fixes for projects filter on the dashboard and group page --- app/assets/stylesheets/base/mixins.scss | 2 +- app/assets/stylesheets/generic/buttons.scss | 138 +++++++++++++ app/assets/stylesheets/generic/header.scss | 1 - app/assets/stylesheets/generic/sidebar.scss | 3 +- app/assets/stylesheets/generic/typography.scss | 8 +- app/assets/stylesheets/pages/groups.scss | 6 + app/assets/stylesheets/pages/projects.scss | 218 +++++++-------------- app/views/dashboard/projects/_projects.html.haml | 2 +- app/views/groups/_projects.html.haml | 2 +- app/views/projects/buttons/_dropdown.html.haml | 2 +- .../projects/buttons/_notifications.html.haml | 2 +- app/views/projects/empty.html.haml | 74 +++---- 12 files changed, 263 insertions(+), 195 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index cdebe498914..5a7e79ddecd 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -299,4 +299,4 @@ color: #78a; } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 46ef595ddf0..cf76f538e01 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -1,3 +1,6 @@ +body { + text-rendering: geometricPrecision; +} .btn { @extend .btn-default; @@ -88,3 +91,138 @@ } } } + +@mixin btn-info { + @include border-radius(2px); + + border-width: 1px; + border-style: solid; + text-transform: uppercase; + font-size: 13px; + font-weight: 600; + line-height: 18px; + padding: 11px 16px; + letter-spacing: .4px; + + &:hover { + border-width: 1px; + border-style: solid; + } + + &:focus { + border-width: 1px; + border-style: solid; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + border-width: 1px; + border-style: solid; + } +} + +@mixin btn-middle { + @include border-radius(2px); + + border-width: 1px; + border-style: solid; + text-transform: uppercase; + font-size: 13px; + font-weight: 600; + line-height: 18px; + padding: 11px 24px; + letter-spacing: .4px; + + &:hover { + border-width: 1px; + border-style: solid; + } + + &:focus { + border-width: 1px; + border-style: solid; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + border-width: 1px; + border-style: solid; + } +} + + +@mixin btn-green { + background-color: #28b061; + border: 1px solid #26a65c; + color: #fff; + + &:hover { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:focus { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:active { + @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); + + background-color: #23a158 !important; + border: 1px solid #229954 !important; + color: #fff !important; + } +} + +/*Butons*/ + +@mixin bnt-project { + background-color: #f0f2f5; + border-color: #dce0e5; + color: #313236; + + &:hover { + border-color:#dce0e5; + background-color: #ebeef2; + color: #313236; + } + + &:focus { + border-color: #dce0e5; + background-color: #ebeef2; + color: #313236; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border-color: #c6cacf !important; + background-color: #e4e7ed !important; + } +} + +@mixin btn-remove { + background-color: #f72e60; + border-color: #ee295a; + + &:hover { + background-color: #e82757; + border-color: #e32555; + } + + &:focus { + background-color: #e82757; + border-color: #e32555; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + background-color: #d42450 !important; + border-color: #e12554 !important; + } + +} \ No newline at end of file diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/generic/header.scss index b758a526fbb..543ce41ab52 100644 --- a/app/assets/stylesheets/generic/header.scss +++ b/app/assets/stylesheets/generic/header.scss @@ -26,7 +26,6 @@ header { min-height: $header-height; background-color: #fff; border: none; - border-bottom: 1px solid #EEE; .container-fluid { width: 100% !important; diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss index 3d055f0e66f..c5ea3aca7ca 100644 --- a/app/assets/stylesheets/generic/sidebar.scss +++ b/app/assets/stylesheets/generic/sidebar.scss @@ -21,12 +21,11 @@ min-height: 100vh; width: 100%; padding: 20px; - background: #f1f4f8; + background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; - border: 1px solid #e7e9ed; min-height: 90vh; &.container-blank { diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index bfb559c294b..0ccb21f3cd1 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -2,10 +2,10 @@ * Headers * */ - - body { - text-rendering: optimizeLegibility - } +body { + text-rendering:optimizeLegibility; + -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px; +} .page-title { margin-top: 0px; diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 2b1b747139a..07a38a19fad 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -10,3 +10,9 @@ .milestone-row { @include str-truncated(90%); } + +.dashboard .side .panel .panel-heading .input-group { + .form-control { + height: 42px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 621ba2fe2c8..a5940543a9d 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -1,3 +1,14 @@ +.alert_holder { + margin: -16px; + + .alert-link { + font-weight: normal; + } +} +.no-ssh-key-message { + background-color: #f28d35; + margin-bottom: 16px; +} .new_project, .edit_project { fieldset.features { @@ -20,20 +31,20 @@ margin: -$gl-padding; padding: $gl-padding; padding: 44px 0 17px 0; - + .project-identicon-holder { margin-bottom: 16px; - + .avatar, .identicon { margin: 0 auto; float: none; } - + .identicon { @include border-radius(50%); } } - + .project-home-dropdown { margin: 11px 3px 0; } @@ -59,7 +70,7 @@ background: #FFF; font-size: 14px; height: 42px; - margin-left: -2px; + margin-left: -1px; } } @@ -75,45 +86,15 @@ top: 17px; margin-bottom: 44px; } + .project-repo-buttons { margin-top: 12px; margin-bottom: 0px; - + .btn { - @extend .btn-info; - @include border-radius(2px); + @include bnt-project; + @include btn-info; - background-color: #f0f2f5; - border: 1px solid #dce0e5; - text-transform: uppercase; - color: #313236; - font-size: 13px; - font-weight: 600; - line-height: 18px; - padding: 11px 16px; - margin-left: 12px; - - &:hover { - @include border-radius(2px); - color: #313236; - border: 1px solid #dce0e5; - background-color: #ebeef2; - } - - &:focus { - @include border-radius(2px); - color: #313236; - border: 1px solid #dce0e5; - background-color: #ebeef2; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - color: #313236 !important; - border: 1px solid #c6cacf !important; - background-color: #e4e7ed !important; - } - .count { display: inline-block; } @@ -123,6 +104,7 @@ .split-one { display: inline-table; + margin-right: 12px; a { margin: -1px !important; @@ -138,7 +120,7 @@ cursor: auto; @extend .monospace; background: #FAFAFA; - width: 100%; + width: 101%; } .input-group-addon { @@ -147,106 +129,66 @@ &.git-protocols { padding: 0; border: none; - + .input-group-btn:last-child > .btn { @include border-radius-right(0); border-left: 1px solid #c6cacf; margin-left: -2px !important; } - input-group-btn:first-child > .btn { - @include border-radius-left(0); - } } } } .projects-search-form { - .input-group .btn-success { - background-color: #2ebf6b !important; - border: 1px solid #28b061 !important; - color: #fff !important; - - &:hover { - background-color: #2eb867 !important; - } - - &:focus { - background-color: #2eb867 !important; - } - - &:active { - @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); - - background-color: #28b061 !important; - border: 1px solid #26a65c !important; - color: #fff !important; - } - } - .input-group .form-control { - height: 39px !important; + height: 42px; } - } -.input-group-btn > .btn { - background-color: #f0f2f5; - border: 1px solid #dce0e5; - text-transform: uppercase; - color: #313236; - font-size: 13px; - font-weight: 600; - - &:focus { - outline: none; - color: #313236; - border: 1px solid #dce0e5; - background-color: #ebeef2; - } - - &:hover { - outline: none; - color: #313236; - border: 1px solid #dce0e5; - background-color: #ebeef2; +.input-group-btn { + .btn { + @include bnt-project; + @include btn-middle; + + &:hover { + outline: none; + } + + &:focus { + outline: none; + } + + &:active { + outline: none; + } } - &:active { + .active { @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - color: #313236 !important; border: 1px solid #c6cacf !important; background-color: #e4e7ed !important; } - -} - -.input-group-btn > .btn.active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; - color: #313236 !important; - border: 1px solid #c6cacf !important; - background-color: #e4e7ed !important; + .btn-green { + @include btn-green + } + } .split-repo-buttons { display: inline-table; - margin-left: 12px; + margin: 0 12px 0 12px; - .btn { - margin: 0 !important; - } - - .dropdown { - margin: 0 !important; + .btn{ + @include bnt-project; + @include btn-info; } .dropdown-toggle { - margin: -5px !important; + margin: -5px; } } @@ -254,22 +196,25 @@ margin-left: 5px; } -.open > .dropdown-toggle.btn { +.dropdown-new { + margin-left: -5px; +} + +.open > .dropdown-new.btn { @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); border: 1px solid #c6cacf !important; background-color: #e4e7ed !important; text-transform: uppercase; - color: #313236; + color: #313236 !important; font-size: 13px; font-weight: 600; - color: #313236 !important; } .dropdown-menu { @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include border-radius (0px); - + border: none; padding: 16px 0; font-size: 14px; @@ -298,7 +243,7 @@ } .project-home-panel .project-home-dropdown { - margin: 13px 0px 0; + margin: 13px 0px 0; } .project-visibility-level-holder { @@ -431,32 +376,12 @@ table.table.protected-branches-list tr.no-border { } .nav > li > a { - border: 1px solid transparent; - color: #313236; - font-size: 13px; - font-weight: 600; - text-decoration: none; - text-transform: uppercase; - margin: 0 8px 0 0; - padding: 10px 16px 10px 16px; - - &:hover { - @include border-radius(2px); - border: 1px solid #dce0e5; - background-color: #f0f2f5; - } - - &:focus { - @include border-radius(2px); - border: 1px solid #dce0e5; - background-color: #f0f2f5; - } + @include btn-info; + @include bnt-project; - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border: 1px solid #c6cacf; - background-color: #e4e7ed; - } + background-color: transparent; + border: 1px solid #f7f8fa; + margin-left: 12px; } li { @@ -501,29 +426,27 @@ pre.light-well { .light-well { @include border-radius (2px); - color: #5b6169 !important; - font-size: 13px !important; - line-height: 1.6em !important; + color: #5b6169; + font-size: 13px; + line-height: 1.6em; } } .prepend-top-20 { - margin: 20px 8px 20px 8px; + margin-top: 20px; .btn-remove { - @include border-radius (2px); + @include btn-middle; + @include btn-remove; - font-size: 13px; - font-weight: 600px; - text-transform: uppercase; float: left !important; - margin-bottom: 14px; } } /* * Projects list rendered on dashboard and user page */ + .projects-list { @include basic-list; @@ -581,3 +504,4 @@ pre.light-well { .inline-form { display: inline-block; } + diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index ef9b9ce756a..0afe4e651c7 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -4,7 +4,7 @@ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' - if current_user.can_create_project? %span.input-group-btn - = link_to new_project_path, class: 'btn btn-success' do + = link_to new_project_path, class: 'btn btn-green' do New project = render 'shared/projects/list', projects: @projects diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 9ac56b1e5fe..2b27a88794d 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -4,7 +4,7 @@ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' - if can? current_user, :create_projects, @group %span.input-group-btn - = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-success' do + = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do New project = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index bc7625e8989..4580c912692 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -1,6 +1,6 @@ - if current_user %span.dropdown - %a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-new.btn.btn-new{href: '#', "data-toggle" => "dropdown"} = icon('plus') %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - if can?(current_user, :create_issue, @project) diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 57f764178d5..4b69a6d7a6f 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -5,7 +5,7 @@ = hidden_field_tag :notification_id, @membership.id = hidden_field_tag :notification_level %span.dropdown - %a.dropdown-toggle.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} = icon('bell') = notification_label(@membership) = icon('angle-down') diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 185ebf23934..d8f9f692c0b 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,7 +1,8 @@ -- if current_user && can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - = render 'shared/no_password' - +.alert_holder + - if current_user && can?(current_user, :download_code, @project) + = render 'shared/no_ssh' + = render 'shared/no_password' + = render "home_panel" .gray-content-block.center @@ -15,38 +16,39 @@ file to this project. .prepend-top-20 -%h3.page-title - Command line instructions -%div.git-empty - %fieldset - %h5 Git global setup - %pre.light-well - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" +.empty_wrapper + %h3.page-title + Command line instructions + %div.git-empty + %fieldset + %h5 Git global setup + %pre.light-well + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" - %fieldset - %h5 Create a new repository - %pre.light-well - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} - cd #{h @project.path} - touch README.md - git add README.md - git commit -m "add README" - git push -u origin master + %fieldset + %h5 Create a new repository + %pre.light-well + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} + cd #{h @project.path} + touch README.md + git add README.md + git commit -m "add README" + git push -u origin master - %fieldset - %h5 Existing folder or Git repository - %pre.light-well - :preserve - cd existing_folder - git init - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git add . - git commit - git push -u origin master + %fieldset + %h5 Existing folder or Git repository + %pre.light-well + :preserve + cd existing_folder + git init + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git add . + git commit + git push -u origin master -- if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" + - if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" -- cgit v1.2.1 From 3453b79c9d57d07d6772bdebd37ab691bfd84723 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 25 Sep 2015 15:43:20 -0400 Subject: Correct an inaccuracy in the Markdown doc [ci skip] --- doc/markdown/markdown.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index d83838127c9..ac3851f8c95 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -274,7 +274,7 @@ The IDs are generated from the content of the header according to the following 1. All spaces are converted to hyphens 1. Two or more hyphens in a row are converted to one 1. If a header with the same ID has already been generated, a unique - incrementing number is appended. + incrementing number is appended, starting at 1. For example: @@ -291,8 +291,8 @@ Would generate the following link IDs: 1. `this-header-has-spaces-in-it` 1. `this-header-has-a-in-it` 1. `this-header-has-unicode-in-it-한글` +1. `this-header-has-spaces-in-it` 1. `this-header-has-spaces-in-it-1` -1. `this-header-has-spaces-in-it-2` Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID. -- cgit v1.2.1 From 0383afc66ab889afc6af02203902d1d515723a96 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 25 Sep 2015 17:04:20 -0700 Subject: Add user preference to view project activity and starred project activity as default dashboard Closes #2662 --- CHANGELOG | 1 + app/controllers/root_controller.rb | 4 ++++ app/helpers/preferences_helper.rb | 4 +++- app/models/user.rb | 2 +- spec/controllers/root_controller_spec.rb | 24 +++++++++++++++++++++++- spec/helpers/preferences_helper_spec.rb | 8 ++++++-- 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b495ccc9b9e..169812e8234 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.1.0 (unreleased) + - Add user preference to view activities as default dashboard (Stan Hu) - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) - Show CI status on all pages where commits list is rendered diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 54171ff67c5..ad04c646e1b 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -22,6 +22,10 @@ class RootController < Dashboard::ProjectsController when 'stars' flash.keep redirect_to starred_dashboard_projects_path + when 'project_activity' + redirect_to activity_dashboard_path + when 'starred_project_activity' + redirect_to activity_dashboard_path(filter: 'starred') else return end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 7f1b6a69926..1b1f4162df4 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -3,7 +3,9 @@ module PreferencesHelper # Maps `dashboard` values to more user-friendly option text DASHBOARD_CHOICES = { projects: 'Your Projects (default)', - stars: 'Starred Projects' + stars: 'Starred Projects', + project_activity: "Your Projects' Activity", + starred_project_activity: "Starred Projects' Activity" }.with_indifferent_access.freeze # Returns an Array usable by a select field for more user-friendly option text diff --git a/app/models/user.rb b/app/models/user.rb index 25371f9138a..3879f3fd381 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -172,7 +172,7 @@ class User < ActiveRecord::Base # User's Dashboard preference # Note: When adding an option, it MUST go on the end of the array. - enum dashboard: [:projects, :stars] + enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity] # User's Project preference # Note: When adding an option, it MUST go on the end of the array. diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 64dfe8f34e3..5a104ae7c99 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -10,7 +10,7 @@ describe RootController do allow(subject).to receive(:current_user).and_return(user) end - context 'who has customized their dashboard setting' do + context 'who has customized their dashboard setting for starred projects' do before do user.update_attribute(:dashboard, 'stars') end @@ -21,6 +21,28 @@ describe RootController do end end + context 'who has customized their dashboard setting for project activities' do + before do + user.update_attribute(:dashboard, 'project_activity') + end + + it 'redirects to the activity list' do + get :index + expect(response).to redirect_to activity_dashboard_path + end + end + + context 'who has customized their dashboard setting for starred project activities' do + before do + user.update_attribute(:dashboard, 'starred_project_activity') + end + + it 'redirects to the activity list' do + get :index + expect(response).to redirect_to activity_dashboard_path(filter: 'starred') + end + end + context 'who uses the default dashboard setting' do it 'renders the default dashboard' do get :index diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 06f69262b71..e5df59c4fba 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -8,14 +8,18 @@ describe PreferencesHelper do end it 'raises an exception when defined choices may be using the wrong key' do - expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar') + dashboards = User.dashboards.dup + dashboards[:projects_changed] = dashboards.delete :projects + expect(User).to receive(:dashboards).and_return(dashboards) expect { helper.dashboard_choices }.to raise_error(KeyError) end it 'provides better option descriptions' do expect(helper.dashboard_choices).to match_array [ ['Your Projects (default)', 'projects'], - ['Starred Projects', 'stars'] + ['Starred Projects', 'stars'], + ["Your Projects' Activity", 'project_activity'], + ["Starred Projects' Activity", 'starred_project_activity'] ] end end -- cgit v1.2.1 From 922c635b6737bbf13eab11ae74c10e590321002c Mon Sep 17 00:00:00 2001 From: Cyriac Thomas Date: Sat, 26 Sep 2015 18:15:58 +0000 Subject: Fixed grammar on Using Docker Build doc. --- doc/ci/docker/using_docker_build.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index a698fbc8184..caa6d0f1f57 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -1,6 +1,6 @@ # Using Docker Build -GitLab CI can allows you to use Docker Engine to build and test docker-based projects. +GitLab CI 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.** @@ -108,5 +108,4 @@ In order to do that follow the steps: ``` 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). - +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). \ No newline at end of file -- cgit v1.2.1 From a9cfa6c50819a959db9f62efe57803f667c92967 Mon Sep 17 00:00:00 2001 From: Aaron Snyder <=> Date: Fri, 25 Sep 2015 21:14:16 -0700 Subject: Fix grammar in admin area labels .nothing-here-block when no labels exist. updating admin area > "Labels" text to "There are no labels yet.", per Stan Hus suggestion. --- CHANGELOG | 1 + app/views/admin/labels/index.html.haml | 4 ++-- features/steps/admin/labels.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8ff61cd6e9f..9ea016e7cc2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.1.0 (unreleased) - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) + - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. v 8.0.2 (unreleased) - Fix default avatar not rendering in network graph (Stan Hu) diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index 8b11c28c56e..d67454c03e7 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -12,5 +12,5 @@ = paginate @labels, theme: 'gitlab' - else .light-well - .nothing-here-block There are no any labels yet - \ No newline at end of file + .nothing-here-block There are no labels yet + diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index d64380abf73..b45d98658bc 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -38,7 +38,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I should see labels help message' do page.within '.labels' do - expect(page).to have_content 'There are no any labels yet' + expect(page).to have_content 'There are no labels yet' end end -- cgit v1.2.1 From 05f29a5cb30d640455a2f4336599a145bc0d0af7 Mon Sep 17 00:00:00 2001 From: Cyriac Thomas Date: Sat, 26 Sep 2015 19:37:55 +0000 Subject: Simplified sentence as per @stanhu's note --- doc/ci/docker/using_docker_build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index caa6d0f1f57..5af27470d2f 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -108,4 +108,4 @@ In order to do that follow the steps: ``` 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). \ No newline at end of file +For more information, check out [Runtime privilege](https://docs.docker.com/reference/run/#runtime-privilege-linux-capabilities-and-lxc-configuration). \ No newline at end of file -- cgit v1.2.1 From 92f8e0fd7e1d39fb14b5a1849971ad74abf1280d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 28 Sep 2015 10:14:53 +0200 Subject: Finish move of runners page to project settings Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/ci/projects/show.html.haml | 2 +- app/views/ci/shared/_guide.html.haml | 2 +- spec/features/ci/runners_spec.rb | 96 ------------------------------------ 4 files changed, 3 insertions(+), 98 deletions(-) delete mode 100644 spec/features/ci/runners_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 8ff61cd6e9f..7c7e41db163 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.1.0 (unreleased) - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) + - Move CI runners page to project settings area v 8.0.2 (unreleased) - Fix default avatar not rendering in network graph (Stan Hu) diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml index 6443378af99..73e60795ba6 100644 --- a/app/views/ci/projects/show.html.haml +++ b/app/views/ci/projects/show.html.haml @@ -3,7 +3,7 @@ - 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) + = link_to "Runners page", runners_path(@project.gl_project) %ul.nav.nav-tabs.append-bottom-20 %li{class: ref_tab_class} diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml index 8a42f29b77c..db2d7f2f4b6 100644 --- a/app/views/ci/shared/_guide.html.haml +++ b/app/views/ci/shared/_guide.html.haml @@ -4,7 +4,7 @@ %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. + Go to #{link_to 'Runners page', runners_path(@project.gl_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} diff --git a/spec/features/ci/runners_spec.rb b/spec/features/ci/runners_spec.rb deleted file mode 100644 index 15147f15eb3..00000000000 --- a/spec/features/ci/runners_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'spec_helper' - -describe "Runners" do - let(:user) { create(:user) } - - before do - login_as(user) - end - - describe "specific runners" do - before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - - @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 - @specific_runner2 = FactoryGirl.create :ci_specific_runner - @project.runners << @specific_runner - @project2.runners << @specific_runner2 - end - - it "places runners in right places" do - 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 ci_project_runners_path(@project) - - within ".available-specific-runners" do - click_on "Enable for this project" - end - - expect(page.find(".activated-specific-runners")).to have_content(@specific_runner2.display_name) - end - - it "disables specific runner for project" do - @project2.runners << @specific_runner - - visit ci_project_runners_path(@project) - - within ".activated-specific-runners" do - click_on "Disable for this project" - end - - 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 - visit ci_project_runners_path(@project) - - within ".activated-specific-runners" do - click_on "Remove runner" - end - - expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey - end - end - - describe "shared runners" do - before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - end - - it "enables shared runners" do - visit ci_project_runners_path(@project) - - click_on "Enable shared runners" - - expect(@project.reload.shared_runners_enabled).to be_truthy - end - end - - describe "show page" do - before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - @specific_runner = FactoryGirl.create :ci_specific_runner - @project.runners << @specific_runner - end - - it "shows runner information" do - visit ci_project_runners_path(@project) - - click_on @specific_runner.short_sha - - expect(page).to have_content(@specific_runner.platform) - end - end -end -- cgit v1.2.1 From c876bfa0f28ddae7fbca314505f5b150b9cec194 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 28 Sep 2015 11:01:45 +0200 Subject: Add missing spec file Signed-off-by: Dmitriy Zaporozhets --- spec/features/runners_spec.rb | 87 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 spec/features/runners_spec.rb diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb new file mode 100644 index 00000000000..06adb7633b2 --- /dev/null +++ b/spec/features/runners_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe "Runners" do + include GitlabRoutingHelper + + let(:user) { create(:user) } + before { login_as(user) } + + describe "specific runners" do + before do + @project = FactoryGirl.create :ci_project + @project.gl_project.team << [user, :master] + + @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 + @specific_runner2 = FactoryGirl.create :ci_specific_runner + @project.runners << @specific_runner + @project2.runners << @specific_runner2 + + visit runners_path(@project.gl_project) + end + + it "places runners in right places" do + 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 + within ".available-specific-runners" do + click_on "Enable for this project" + end + + expect(page.find(".activated-specific-runners")).to have_content(@specific_runner2.display_name) + end + + it "disables specific runner for project" do + @project2.runners << @specific_runner + visit runners_path(@project.gl_project) + + within ".activated-specific-runners" do + click_on "Disable for this project" + end + + 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 + within ".activated-specific-runners" do + click_on "Remove runner" + end + + expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey + end + end + + describe "shared runners" do + before do + @project = FactoryGirl.create :ci_project + @project.gl_project.team << [user, :master] + visit runners_path(@project.gl_project) + end + + it "enables shared runners" do + click_on "Enable shared runners" + expect(@project.reload.shared_runners_enabled).to be_truthy + end + end + + describe "show page" do + before do + @project = FactoryGirl.create :ci_project + @project.gl_project.team << [user, :master] + @specific_runner = FactoryGirl.create :ci_specific_runner + @project.runners << @specific_runner + visit runners_path(@project.gl_project) + end + + it "shows runner information" do + click_on @specific_runner.short_sha + expect(page).to have_content(@specific_runner.platform) + end + end +end -- cgit v1.2.1 From 74bdd67f2104069e68e0fb7938ae91667d04fc85 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 28 Sep 2015 11:12:03 +0200 Subject: Update Source Sans Pro font: adds support for cyrillic characters Signed-off-by: Dmitriy Zaporozhets --- app/assets/fonts/SourceSansPro-Bold.ttf | Bin 148932 -> 291424 bytes app/assets/fonts/SourceSansPro-Light.ttf | Bin 150244 -> 293220 bytes app/assets/fonts/SourceSansPro-Regular.ttf | Bin 149972 -> 293956 bytes app/assets/fonts/SourceSansPro-Semibold.ttf | Bin 149636 -> 292404 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf b/app/assets/fonts/SourceSansPro-Bold.ttf index 50d81bdad58..5d65c93242f 100755 Binary files a/app/assets/fonts/SourceSansPro-Bold.ttf and b/app/assets/fonts/SourceSansPro-Bold.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Light.ttf b/app/assets/fonts/SourceSansPro-Light.ttf index 5f64679f6b9..83a0a336661 100755 Binary files a/app/assets/fonts/SourceSansPro-Light.ttf and b/app/assets/fonts/SourceSansPro-Light.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf b/app/assets/fonts/SourceSansPro-Regular.ttf index 91e9ea5757f..44486cdc670 100755 Binary files a/app/assets/fonts/SourceSansPro-Regular.ttf and b/app/assets/fonts/SourceSansPro-Regular.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf b/app/assets/fonts/SourceSansPro-Semibold.ttf index 5020594826b..86b00c067e0 100755 Binary files a/app/assets/fonts/SourceSansPro-Semibold.ttf and b/app/assets/fonts/SourceSansPro-Semibold.ttf differ -- cgit v1.2.1 From ce49e49bfb40872475b82b500f5e52f6ec36b81f Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 28 Sep 2015 11:26:42 +0200 Subject: edit action allign --- app/assets/stylesheets/base/mixins.scss | 10 +++++----- app/views/projects/_readme.html.haml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index 5a7e79ddecd..bba4728af36 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -243,11 +243,6 @@ } } -.pull-right .light .fa-pencil { - top: 20px; - position: relative; -} - @mixin input-big { height: 36px; padding: 5px 10px; @@ -300,3 +295,8 @@ } } } + +.fa-align { + top: 20px; + position: relative; +} \ No newline at end of file diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index 5038edb95ed..5bc1999ec9d 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -5,7 +5,7 @@   - if can?(current_user, :push_code, @project) = link_to namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light' do - %i.fa.fa-pencil + %i.fa-align.fa.fa-pencil .wiki = cache(readme_cache_key) do = render_readme(readme) -- cgit v1.2.1 From ad679127608ebb711bbd49268e3380a30aabb336 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 28 Sep 2015 12:03:41 +0200 Subject: !important removed from pre --- app/assets/stylesheets/base/mixins.scss | 6 ------ app/assets/stylesheets/generic/common.scss | 2 +- app/assets/stylesheets/highlight/white.scss | 7 ++++--- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss index bba4728af36..20c62250bc1 100644 --- a/app/assets/stylesheets/base/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -157,12 +157,6 @@ pre { @include border-radius(2px); - - margin: 12px 0 12px 0 !important; - background-color: #f8fafc !important; - font-size: 13px !important; - color: #5b6169 !important; - line-height: 1.6em !important; } p > code { diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 48fad7701ef..91dbe1ec244 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -313,7 +313,7 @@ table { } .wiki .highlight, .note-body .highlight { - margin-bottom: 9px; + margin: 12px 0 12px 0; } .wiki .code { diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 5de589109bd..20a144ef952 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -1,9 +1,10 @@ /* https://github.com/aahan/pygments-github-style */ pre.code.highlight.white, .code.white { - - background-color: #fff; - color: #333; + background-color: #f8fafc; + font-size: 13px; + color: #5b6169; + line-height: 1.6em; .line-numbers, .line-numbers a { -- cgit v1.2.1 From 037defc7def3e2d0f2de4930516149d7567362fc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 28 Sep 2015 17:19:20 +0200 Subject: Move CI variables page to project settings Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/variables_controller.rb | 33 ------------------ app/controllers/projects/application_controller.rb | 4 +++ app/controllers/projects/runners_controller.rb | 4 --- app/controllers/projects/variables_controller.rb | 25 ++++++++++++++ app/views/ci/variables/show.html.haml | 39 ---------------------- app/views/layouts/ci/_nav_project.html.haml | 5 --- app/views/layouts/nav/_project_settings.html.haml | 5 +++ app/views/projects/variables/show.html.haml | 39 ++++++++++++++++++++++ config/routes.rb | 2 +- spec/features/ci/variables_spec.rb | 28 ---------------- spec/features/variables_spec.rb | 25 ++++++++++++++ 12 files changed, 100 insertions(+), 110 deletions(-) delete mode 100644 app/controllers/ci/variables_controller.rb create mode 100644 app/controllers/projects/variables_controller.rb delete mode 100644 app/views/ci/variables/show.html.haml create mode 100644 app/views/projects/variables/show.html.haml delete mode 100644 spec/features/ci/variables_spec.rb create mode 100644 spec/features/variables_spec.rb diff --git a/CHANGELOG b/CHANGELOG index a0f1fa12082..7e31c2f57b6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.1.0 (unreleased) - Add notes and SSL verification entries to hook APIs (Ben Boeckel) - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area + - Move CI variables page to project settings area v 8.0.3 (unreleased) diff --git a/app/controllers/ci/variables_controller.rb b/app/controllers/ci/variables_controller.rb deleted file mode 100644 index 9c6c775fde8..00000000000 --- a/app/controllers/ci/variables_controller.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Ci - class VariablesController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_access_project! - before_action :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/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 48c922f450c..56a63ce9758 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -31,4 +31,8 @@ class Projects::ApplicationController < ApplicationController def ci_enabled return render_404 unless @project.gitlab_ci? end + + def ci_project + @ci_project ||= @project.gitlab_ci_project + end end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index d59884a1dd7..6cb6e3ef6d4 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -55,10 +55,6 @@ class Projects::RunnersController < Projects::ApplicationController protected - def ci_project - @ci_project = @project.gitlab_ci_project - end - def set_runner @runner ||= @ci_project.runners.find(params[:id]) end diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb new file mode 100644 index 00000000000..d6561a45a70 --- /dev/null +++ b/app/controllers/projects/variables_controller.rb @@ -0,0 +1,25 @@ +class Projects::VariablesController < Projects::ApplicationController + before_action :ci_project + before_action :authorize_admin_project! + + layout 'project_settings' + + def show + end + + def update + if ci_project.update_attributes(project_params) + Ci::EventService.new.change_project_settings(current_user, ci_project) + + redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.' + else + render action: 'show' + end + end + + private + + def project_params + params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] }) + end +end diff --git a/app/views/ci/variables/show.html.haml b/app/views/ci/variables/show.html.haml deleted file mode 100644 index ebf68341e08..00000000000 --- a/app/views/ci/variables/show.html.haml +++ /dev/null @@ -1,39 +0,0 @@ -%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 - 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/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 9ebe7eabd8e..4b0dc4fc2f5 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -11,11 +11,6 @@ %span Commits %span.count= @project.commits.count - = nav_link path: 'variables#show' do - = link_to ci_project_variables_path(@project) do - = icon('code fw') - %span - Variables = nav_link path: 'web_hooks#index' do = link_to ci_project_web_hooks_path(@project) do = icon('link fw') diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index a85dd71126c..c8975fb8492 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -40,3 +40,8 @@ = icon('cog fw') %span Runners + = nav_link(controller: :variables) do + = link_to namespace_project_variables_path(@project.namespace, @project) do + = icon('code fw') + %span + Variables diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml new file mode 100644 index 00000000000..29416a94ff6 --- /dev/null +++ b/app/views/projects/variables/show.html.haml @@ -0,0 +1,39 @@ +%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 + So you can use them for passwords, secret keys or whatever you want. + +%hr + + += nested_form_for @ci_project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| + - if @project.errors.any? + #error_explanation + %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" + .alert.alert-error + %ul + - @ci_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/config/routes.rb b/config/routes.rb index 201add02335..776b606bf7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -58,7 +58,6 @@ Gitlab::Application.routes.draw do resources :runner_projects, only: [:create, :destroy] resources :events, only: [:index] - resource :variables, only: [:show, :update] end resource :user_sessions do @@ -591,6 +590,7 @@ Gitlab::Application.routes.draw do resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + resource :variables, only: [:show, :update] resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do member do diff --git a/spec/features/ci/variables_spec.rb b/spec/features/ci/variables_spec.rb deleted file mode 100644 index e387b3be555..00000000000 --- a/spec/features/ci/variables_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe "Variables" do - let(:user) { create(:user) } - - before do - login_as(user) - end - - describe "specific runners" do - before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - end - - it "creates variable", js: true do - visit ci_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" - - expect(page).to have_content("Variables were successfully updated.") - expect(@project.variables.count).to eq(1) - end - - end -end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb new file mode 100644 index 00000000000..adb602f3edd --- /dev/null +++ b/spec/features/variables_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe "Variables" do + let(:user) { create(:user) } + before { login_as(user) } + + describe "specific runners" do + before do + @project = FactoryGirl.create :ci_project + @gl_project = @project.gl_project + @gl_project.team << [user, :master] + end + + it "creates variable", js: true do + visit namespace_project_variables_path(@gl_project.namespace, @gl_project) + click_on "Add a variable" + fill_in "Key", with: "SECRET_KEY" + fill_in "Value", with: "SECRET_VALUE" + click_on "Save changes" + + expect(page).to have_content("Variables were successfully updated.") + expect(@project.variables.count).to eq(1) + end + end +end -- cgit v1.2.1 From 59582aae578453a7f9888e2380a7412c4bf14f4f Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 28 Sep 2015 17:58:30 +0200 Subject: page-title-fix --- app/assets/stylesheets/generic/typography.scss | 7 +++++++ app/views/projects/empty.html.haml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 0ccb21f3cd1..9bd2ed0aefe 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -12,6 +12,13 @@ body { line-height: 1.3; font-size: 1.25em; font-weight: 600; +} + +.page-title-empty { + margin-top: 0px; + line-height: 1.3; + font-size: 1.25em; + font-weight: 600; margin: 12px 7px 12px 7px; } diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index d8f9f692c0b..e06454fd148 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -17,7 +17,7 @@ .prepend-top-20 .empty_wrapper - %h3.page-title + %h3.page-title-empty Command line instructions %div.git-empty %fieldset -- cgit v1.2.1 From 44b8844122029fceb45db89b3af554b77523f005 Mon Sep 17 00:00:00 2001 From: karen Carias Date: Mon, 28 Sep 2015 19:04:32 -0700 Subject: changed words --- doc/hooks/custom_hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md index dd3be70c900..548c484bc08 100644 --- a/doc/hooks/custom_hooks.md +++ b/doc/hooks/custom_hooks.md @@ -2,7 +2,7 @@ **Note: Custom git hooks must be configured on the filesystem of the GitLab server. Only GitLab server administrators will be able to complete these tasks. -Please explore webhooks as an option if you do not have filesystem access. For a user-friendly Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** +Please explore webhooks as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** Git natively supports hooks that are executed on different actions. Examples of server-side git hooks include pre-receive, post-receive, and update. -- cgit v1.2.1 From 5b7e491c393f3ba436e45454a58709f6d70bbc5c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 28 Sep 2015 22:57:52 -0400 Subject: Update CHANGELOG for 8.0.3 [ci skip] --- CHANGELOG | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a0f1fa12082..fdb30633046 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - Add user preference to view activities as default dashboard (Stan Hu) - - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) - Add option to admin area to sign in as a specific user (Pavel Forkert) - Show CI status on all pages where commits list is rendered - Automatically enable CI when push .gitlab-ci.yml file to repository @@ -17,7 +15,10 @@ v 8.1.0 (unreleased) - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area -v 8.0.3 (unreleased) +v 8.0.3 + - Fix URL shown in Slack notifications + - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) + - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) v 8.0.2 - Fix default avatar not rendering in network graph (Stan Hu) -- cgit v1.2.1 From abae352e59b3fe8574249041508afd68bff8bab2 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 28 Sep 2015 21:21:41 -0700 Subject: Link directly to the projects page instead of the root controller If "Your Projects' Activity" dashboard setting were selected, it would be impossible to navigate to the projects page since the redirection would always bounce back to the activity dashboard. Fixes bug introduced by !1446. --- app/views/layouts/nav/_dashboard.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 21d8655131f..b1a1d531846 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do - = link_to root_path, title: 'Projects', data: {placement: 'right'} do + = link_to dashboard_projects_path, title: 'Projects', data: {placement: 'right'} do = icon('home fw') %span Projects -- cgit v1.2.1 From 2a0d4e7200d3e985552d887b9c9e14db073c70ab Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 29 Sep 2015 10:37:31 +0200 Subject: Move CI triggers page to project settings area Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/triggers_controller.rb | 43 --------------- app/controllers/projects/triggers_controller.rb | 35 ++++++++++++ app/views/ci/triggers/_trigger.html.haml | 14 ----- app/views/ci/triggers/index.html.haml | 67 ----------------------- app/views/layouts/ci/_nav_project.html.haml | 5 -- app/views/layouts/nav/_project_settings.html.haml | 5 ++ app/views/projects/triggers/_trigger.html.haml | 14 +++++ app/views/projects/triggers/index.html.haml | 67 +++++++++++++++++++++++ config/routes.rb | 3 +- spec/features/ci/triggers_spec.rb | 28 ---------- spec/features/triggers_spec.rb | 29 ++++++++++ 12 files changed, 152 insertions(+), 159 deletions(-) delete mode 100644 app/controllers/ci/triggers_controller.rb create mode 100644 app/controllers/projects/triggers_controller.rb delete mode 100644 app/views/ci/triggers/_trigger.html.haml delete mode 100644 app/views/ci/triggers/index.html.haml create mode 100644 app/views/projects/triggers/_trigger.html.haml create mode 100644 app/views/projects/triggers/index.html.haml delete mode 100644 spec/features/ci/triggers_spec.rb create mode 100644 spec/features/triggers_spec.rb diff --git a/CHANGELOG b/CHANGELOG index f34f79d18dd..181baf24889 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.1.0 (unreleased) - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area - Move CI variables page to project settings area + - Move CI triggers page to project settings area v 8.0.3 - Fix URL shown in Slack notifications diff --git a/app/controllers/ci/triggers_controller.rb b/app/controllers/ci/triggers_controller.rb deleted file mode 100644 index a39cc5d3a56..00000000000 --- a/app/controllers/ci/triggers_controller.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Ci - class TriggersController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_access_project! - before_action :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/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb new file mode 100644 index 00000000000..782ebd01b05 --- /dev/null +++ b/app/controllers/projects/triggers_controller.rb @@ -0,0 +1,35 @@ +class Projects::TriggersController < Projects::ApplicationController + before_action :ci_project + before_action :authorize_admin_project! + + layout 'project_settings' + + def index + @triggers = @ci_project.triggers + @trigger = Ci::Trigger.new + end + + def create + @trigger = @ci_project.triggers.new + @trigger.save + + if @trigger.valid? + redirect_to namespace_project_triggers_path(@project.namespace, @project) + else + @triggers = @ci_project.triggers.select(&:persisted?) + render :index + end + end + + def destroy + trigger.destroy + + redirect_to namespace_project_triggers_path(@project.namespace, @project) + end + + private + + def trigger + @trigger ||= @ci_project.triggers.find(params[:id]) + end +end diff --git a/app/views/ci/triggers/_trigger.html.haml b/app/views/ci/triggers/_trigger.html.haml deleted file mode 100644 index addfbfcb0d4..00000000000 --- a/app/views/ci/triggers/_trigger.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -%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 deleted file mode 100644 index 44374a1a4d5..00000000000 --- a/app/views/ci/triggers/index.html.haml +++ /dev/null @@ -1,67 +0,0 @@ -%h3.page-title - 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/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 4b0dc4fc2f5..2d3cc3cf983 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -16,11 +16,6 @@ = icon('link fw') %span Web Hooks - = nav_link path: 'triggers#index' do - = link_to ci_project_triggers_path(@project) do - = icon('retweet fw') - %span - Triggers = nav_link path: ['services#index', 'services#edit'] do = link_to ci_project_services_path(@project) do = icon('share fw') diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index c8975fb8492..28efb035d09 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -45,3 +45,8 @@ = icon('code fw') %span Variables + = nav_link path: 'triggers#index' do + = link_to namespace_project_triggers_path(@project.namespace, @project) do + = icon('retweet fw') + %span + Triggers diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml new file mode 100644 index 00000000000..48b3b5c9920 --- /dev/null +++ b/app/views/projects/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', namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-danger btn-sm btn-grouped" diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml new file mode 100644 index 00000000000..17dcb78e256 --- /dev/null +++ b/app/views/projects/triggers/index.html.haml @@ -0,0 +1,67 @@ +%h3.page-title + 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 partial: 'trigger', collection: @triggers, as: :trigger +- else + %h4 No triggers + += form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create'), 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(@ci_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(@ci_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(@ci_project.id, 'REF_NAME')} diff --git a/config/routes.rb b/config/routes.rb index 776b606bf7d..f7317fb5d9f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -53,8 +53,6 @@ Gitlab::Application.routes.draw do end end - resources :triggers, only: [:index, :create, :destroy] - resources :runner_projects, only: [:create, :destroy] resources :events, only: [:index] @@ -591,6 +589,7 @@ Gitlab::Application.routes.draw do resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] + resources :triggers, only: [:index, :create, :destroy] resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do member do diff --git a/spec/features/ci/triggers_spec.rb b/spec/features/ci/triggers_spec.rb deleted file mode 100644 index c6afeb74628..00000000000 --- a/spec/features/ci/triggers_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe 'Triggers' do - let(:user) { create(:user) } - - before do - login_as(user) - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - visit ci_project_triggers_path(@project) - end - - context 'create a trigger' do - before do - click_on 'Add Trigger' - expect(@project.triggers.count).to eq(1) - end - - it 'contains trigger token' do - expect(page).to have_content(@project.triggers.first.token) - end - - it 'revokes the trigger' do - click_on 'Revoke' - expect(@project.triggers.count).to eq(0) - end - end -end diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb new file mode 100644 index 00000000000..69492d58878 --- /dev/null +++ b/spec/features/triggers_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe 'Triggers' do + let(:user) { create(:user) } + before { login_as(user) } + + before do + @project = FactoryGirl.create :ci_project + @gl_project = @project.gl_project + @gl_project.team << [user, :master] + visit namespace_project_triggers_path(@gl_project.namespace, @gl_project) + end + + context 'create a trigger' do + before do + click_on 'Add Trigger' + expect(@project.triggers.count).to eq(1) + end + + it 'contains trigger token' do + expect(page).to have_content(@project.triggers.first.token) + end + + it 'revokes the trigger' do + click_on 'Revoke' + expect(@project.triggers.count).to eq(0) + end + end +end -- cgit v1.2.1 From 1530f68c9876bc8376bf4aa199c8abda570f5214 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 24 Sep 2015 17:09:33 +0200 Subject: WIP --- app/models/ci/build.rb | 3 +-- app/models/ci/commit.rb | 8 ++++++-- app/models/ci/project.rb | 4 ++-- app/models/project.rb | 2 ++ db/migrate/20150924125150_add_project_id_to_ci_tables.rb | 12 ++++++++++++ .../20150924125436_migrate_project_id_for_ci_tables.rb | 15 +++++++++++++++ .../20150924131004_add_ci_fields_to_projects_table.rb | 5 +++++ 7 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20150924125150_add_project_id_to_ci_tables.rb create mode 100644 db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb create mode 100644 db/migrate/20150924131004_add_ci_fields_to_projects_table.rb diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 8096d4fa5ae..16ff6e38630 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -30,7 +30,6 @@ module Ci 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' @@ -137,7 +136,7 @@ module Ci state :canceled, value: 'canceled' end - delegate :sha, :short_sha, :before_sha, :ref, + delegate :sha, :short_sha, :before_sha, :ref, :project, to: :commit, prefix: false def trace_html diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index f102d0a7679..31638c7e1dc 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -18,8 +18,8 @@ module Ci class Commit < ActiveRecord::Base extend Ci::Model - - belongs_to :project, class_name: 'Ci::Project' + + belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id has_many :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' @@ -32,6 +32,10 @@ module Ci sha[0...8] end + def project + @project ||= gl_project.gitlab_ci_project + end + def to_param sha end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 8fb54b90d61..ea7547f5d43 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 ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit' + has_many :commits, through: :gl_project, class_name: 'Ci::Commit', foreign_key: :gl_project_id 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' @@ -41,7 +41,7 @@ module Ci 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' - has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit' + has_one :last_commit, through: :gl_project, class_name: 'Ci::Commit' # Project services has_many :services, dependent: :destroy, class_name: 'Ci::Service' diff --git a/app/models/project.rb b/app/models/project.rb index e912c48467d..efa573c82b9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -118,6 +118,8 @@ class Project < ActiveRecord::Base has_many :deploy_keys, through: :deploy_keys_projects has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user + 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', foreign_key: :gl_project_id + has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id diff --git a/db/migrate/20150924125150_add_project_id_to_ci_tables.rb b/db/migrate/20150924125150_add_project_id_to_ci_tables.rb new file mode 100644 index 00000000000..8b45bcf5fe1 --- /dev/null +++ b/db/migrate/20150924125150_add_project_id_to_ci_tables.rb @@ -0,0 +1,12 @@ +class AddProjectIdToCiTables < ActiveRecord::Migration + def up + add_column :ci_builds, :gl_project_id, :integer + add_column :ci_commits, :gl_project_id, :integer + add_column :ci_events, :gl_project_id, :integer + add_column :ci_runner_projects, :gl_project_id, :integer + add_column :ci_services, :gl_project_id, :integer + add_column :ci_triggers, :gl_project_id, :integer + add_column :ci_variables, :gl_project_id, :integer + add_column :ci_web_hooks, :gl_project_id, :integer + end +end diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb new file mode 100644 index 00000000000..28d63c8c840 --- /dev/null +++ b/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb @@ -0,0 +1,15 @@ +class MigrateProjectIdForCiTables < ActiveRecord::Migration + TABLES = %w(ci_builds ci_commits ci_events ci_runner_projects + ci_services ci_triggers ci_variables ci_web_hooks) + + def up + TABLES.each do |table| + execute( + "UPDATE #{table} " + + "JOIN ci_projects ON ci_projects.id = #{table}.project_id " + + "SET gl_project_id=ci_projects.gitlab_id " + + "WHERE gl_project_id IS NULL" + ) + end + end +end diff --git a/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb b/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb new file mode 100644 index 00000000000..bb97ec30051 --- /dev/null +++ b/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb @@ -0,0 +1,5 @@ +class AddCiFieldsToProjectsTable < ActiveRecord::Migration + def up + add_column :projects, :shared_runners_enabled, :boolean, default: false + end +end -- cgit v1.2.1 From 30c78e70cba395c1336611c58891a75473f8a037 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 13:14:34 +0200 Subject: WIP --- app/controllers/ci/application_controller.rb | 8 +-- app/models/ci/build.rb | 3 +- app/models/ci/commit.rb | 10 +-- app/models/ci/project.rb | 29 ++++---- app/models/project.rb | 1 + db/schema.rb | 80 ++++++++++++---------- spec/controllers/ci/commits_controller_spec.rb | 12 ++-- spec/factories/ci/commits.rb | 2 + spec/factories/ci/projects.rb | 2 +- spec/features/ci/admin/builds_spec.rb | 3 +- spec/features/ci/builds_spec.rb | 19 ++--- spec/features/ci/commits_spec.rb | 11 +-- spec/mailers/ci/notify_spec.rb | 3 +- spec/models/ci/build_spec.rb | 3 +- spec/models/ci/commit_spec.rb | 23 ++++--- spec/models/ci/mail_service_spec.rb | 18 +++-- .../ci/project_services/hip_chat_service_spec.rb | 3 +- .../ci/project_services/slack_service_spec.rb | 3 +- spec/models/ci/project_spec.rb | 3 +- spec/models/ci/service_spec.rb | 3 +- spec/requests/ci/api/builds_spec.rb | 16 +++-- spec/requests/ci/api/commits_spec.rb | 3 +- spec/requests/ci/api/triggers_spec.rb | 3 +- spec/requests/ci/builds_spec.rb | 3 +- spec/requests/ci/commits_spec.rb | 3 +- .../ci/create_trigger_request_service_spec.rb | 11 +-- spec/services/ci/image_for_build_service_spec.rb | 3 +- spec/services/ci/register_build_service_spec.rb | 5 +- spec/services/ci/web_hook_service_spec.rb | 3 +- 29 files changed, 154 insertions(+), 135 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index d8227e632e4..da77e2b94e8 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -11,10 +11,10 @@ module Ci private def check_enable_flag! - unless current_application_settings.ci_enabled - redirect_to(disabled_ci_projects_path) - return - end + # unless current_application_settings.ci_enabled + # redirect_to(disabled_ci_projects_path) + # return + # end end def authenticate_public_page! diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 16ff6e38630..9ac47ccfe4a 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -79,7 +79,6 @@ module Ci 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 @@ -187,7 +186,7 @@ module Ci end def project_id - commit.project_id + commit.gl_project.gitlab_id end def project_name diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 31638c7e1dc..9a719787649 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -32,14 +32,15 @@ module Ci sha[0...8] end - def project - @project ||= gl_project.gitlab_ci_project - end - def to_param sha end + def project + @project ||= gl_project.gitlab_ci_project + @project ||= gl_project.create_gitlab_ci_project + end + def last_build builds.order(:id).last end @@ -115,7 +116,6 @@ module Ci 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], diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index ea7547f5d43..89bbbea5c5a 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -33,15 +33,12 @@ module Ci belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id - has_many :commits, through: :gl_project, class_name: 'Ci::Commit', foreign_key: :gl_project_id - 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' - has_one :last_commit, through: :gl_project, class_name: 'Ci::Commit' # Project services has_many :services, dependent: :destroy, class_name: 'Ci::Service' @@ -51,17 +48,19 @@ module Ci accepts_nested_attributes_for :variables, allow_destroy: true + delegate :commits, :builds, :last_commit, to: :gl_project + # # Validations # validates_presence_of :name, :timeout, :token, :default_ref, - :path, :ssh_url_to_repo, :gitlab_id + :path, :ssh_url_to_repo, :gitlab_id validates_uniqueness_of :gitlab_id validates :polling_interval, - presence: true, - if: ->(project) { project.always_build.present? } + presence: true, + if: ->(project) { project.always_build.present? } scope :public_only, ->() { where(public: true) } @@ -79,12 +78,12 @@ module Ci 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, + 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, } @@ -125,10 +124,14 @@ module Ci def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? + self.default_ref ||= 'master' + self.name ||= gl_project.name_with_namespace + self.path ||= gl_project.path_with_namespace + self.ssh_url_to_repo ||= gl_project.ssh_url_to_repo end def tracked_refs - @tracked_refs ||= default_ref.split(",").map{|ref| ref.strip} + @tracked_refs ||= default_ref.split(",").map { |ref| ref.strip } end def valid_token? token diff --git a/app/models/project.rb b/app/models/project.rb index efa573c82b9..9ecf16d9812 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -119,6 +119,7 @@ class Project < ActiveRecord::Base has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user 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', foreign_key: :gl_project_id + has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" diff --git a/db/schema.rb b/db/schema.rb index 01ccda7a75e..89aafba511b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,10 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150920161119) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" +ActiveRecord::Schema.define(version: 20150924131004) do create_table "abuse_reports", force: true do |t| t.integer "reporter_id" @@ -45,8 +42,8 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" - t.text "help_page_text" t.boolean "ci_enabled", default: true, null: false + t.text "help_page_text" end create_table "audit_events", force: true do |t| @@ -85,21 +82,22 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.integer "project_id" t.string "status" t.datetime "finished_at" - t.text "trace" + t.text "trace", limit: 2147483647 t.datetime "created_at" t.datetime "updated_at" t.datetime "started_at" t.integer "runner_id" - t.float "coverage" + t.float "coverage", limit: 24 t.integer "commit_id" t.text "commands" t.integer "job_id" t.string "name" - t.boolean "deploy", default: false + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false + t.boolean "allow_failure", default: false, null: false t.string "stage" t.integer "trigger_request_id" + t.integer "gl_project_id" end add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree @@ -112,12 +110,13 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.string "ref" t.string "sha" t.string "before_sha" - t.text "push_data" + t.text "push_data", limit: 16777215 t.datetime "created_at" t.datetime "updated_at" - t.boolean "tag", default: false + t.boolean "tag", default: false t.text "yaml_errors" t.datetime "committed_at" + t.integer "gl_project_id" end add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree @@ -133,6 +132,7 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.text "description" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end add_index "ci_events", ["created_at"], name: "index_ci_events_on_created_at", using: :btree @@ -180,10 +180,11 @@ ActiveRecord::Schema.define(version: 20150920161119) do end create_table "ci_runner_projects", force: true do |t| - t.integer "runner_id", null: false - t.integer "project_id", null: false + t.integer "runner_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree @@ -207,11 +208,12 @@ ActiveRecord::Schema.define(version: 20150920161119) do create_table "ci_services", force: true do |t| t.string "type" t.string "title" - t.integer "project_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" + t.integer "gl_project_id" end add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree @@ -256,10 +258,11 @@ ActiveRecord::Schema.define(version: 20150920161119) do create_table "ci_triggers", force: true do |t| t.string "token" - t.integer "project_id", null: false + t.integer "project_id", null: false t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree @@ -271,15 +274,17 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" + t.integer "gl_project_id" end add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree create_table "ci_web_hooks", force: true do |t| - t.string "url", null: false - t.integer "project_id", null: false + t.string "url", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end create_table "deploy_keys_projects", force: true do |t| @@ -425,9 +430,9 @@ ActiveRecord::Schema.define(version: 20150920161119) do create_table "merge_request_diffs", force: true do |t| t.string "state" - t.text "st_commits" - t.text "st_diffs" - t.integer "merge_request_id", null: false + t.text "st_commits", limit: 2147483647 + t.text "st_diffs", limit: 2147483647 + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -509,8 +514,8 @@ ActiveRecord::Schema.define(version: 20150920161119) do t.string "line_code" t.string "commit_id" t.integer "noteable_id" - t.boolean "system", default: false, null: false - t.text "st_diff" + t.boolean "system", default: false, null: false + t.text "st_diff", limit: 2147483647 t.integer "updated_by_id" end @@ -579,25 +584,26 @@ ActiveRecord::Schema.define(version: 20150920161119) 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", limit: 24, default: 0.0 + t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 + t.integer "commit_count", default: 0 + t.boolean "shared_runners_enabled", default: false end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree @@ -649,15 +655,15 @@ ActiveRecord::Schema.define(version: 20150920161119) do create_table "snippets", force: true do |t| t.string "title" - t.text "content" - t.integer "author_id", null: false + t.text "content", limit: 2147483647 + t.integer "author_id", null: false t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" t.string "file_name" t.datetime "expires_at" t.string "type" - t.integer "visibility_level", default: 0, null: false + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree diff --git a/spec/controllers/ci/commits_controller_spec.rb b/spec/controllers/ci/commits_controller_spec.rb index b71e7505731..cc39ce7687c 100644 --- a/spec/controllers/ci/commits_controller_spec.rb +++ b/spec/controllers/ci/commits_controller_spec.rb @@ -1,14 +1,10 @@ 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 + commit = FactoryGirl.create :ci_commit + get :status, id: commit.sha, ref_id: commit.ref, project_id: commit.project.id expect(response).to be_success expect(response.code).to eq('200') @@ -16,8 +12,8 @@ describe Ci::CommitsController do 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 + commit = FactoryGirl.create :ci_commit + get :status, id: commit.sha, ref_id: "deploy", project_id: commit.project.id expect(response).to be_success expect(response.code).to eq('200') diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index 70930c789c3..9c7a0e9cbe0 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -51,6 +51,8 @@ FactoryGirl.define do } end + gl_project factory: :empty_project + factory :ci_commit_without_jobs do after(:create) do |commit, evaluator| commit.push_data[:ci_yaml_file] = YAML.dump({}) diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb index e6bd0685f8d..d492fe8209e 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 - gl_project factory: :project + gl_project factory: :empty_project factory :ci_project do token 'iPWx6WM4lhHNedGfBpPJNP' diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/ci/admin/builds_spec.rb index 88ef9c144af..ee757206a03 100644 --- a/spec/features/ci/admin/builds_spec.rb +++ b/spec/features/ci/admin/builds_spec.rb @@ -1,8 +1,7 @@ require 'spec_helper' describe "Admin Builds" do - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit } before do diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb index 2f020e524e2..d65699dbefa 100644 --- a/spec/features/ci/builds_spec.rb +++ b/spec/features/ci/builds_spec.rb @@ -3,16 +3,15 @@ require 'spec_helper' describe "Builds" do context :private_project do before do - @project = FactoryGirl.create :ci_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit @build = FactoryGirl.create :ci_build, commit: @commit login_as :user - @project.gl_project.team << [@user, :master] + @commit.project.gl_project.team << [@user, :master] end describe "GET /:project/builds/:id" do before do - visit ci_project_build_path(@project, @build) + visit ci_project_build_path(@commit.project, @build) end it { expect(page).to have_content @commit.sha[0..7] } @@ -23,7 +22,7 @@ describe "Builds" do describe "GET /:project/builds/:id/cancel" do before do @build.run! - visit cancel_ci_project_build_path(@project, @build) + visit cancel_ci_project_build_path(@commit.project, @build) end it { expect(page).to have_content 'canceled' } @@ -33,7 +32,7 @@ describe "Builds" do describe "POST /:project/builds/:id/retry" do before do @build.cancel! - visit ci_project_build_path(@project, @build) + visit ci_project_build_path(@commit.project, @build) click_link 'Retry' end @@ -45,13 +44,15 @@ describe "Builds" do 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 + @commit = FactoryGirl.create :ci_commit + @commit.project.public = true + @commit.project.save + @runner = FactoryGirl.create :ci_specific_runner @build = FactoryGirl.create :ci_build, commit: @commit, runner: @runner stub_gitlab_calls - visit ci_project_build_path(@project, @build) + visit ci_project_build_path(@commit.project, @build) end it { expect(page).to have_content @commit.sha[0..7] } diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb index 40a62ca4574..657a9dabe9e 100644 --- a/spec/features/ci/commits_spec.rb +++ b/spec/features/ci/commits_spec.rb @@ -5,11 +5,10 @@ describe "Commits" do context "Authenticated user" do before do - @project = FactoryGirl.create :ci_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit @build = FactoryGirl.create :ci_build, commit: @commit login_as :user - @project.gl_project.team << [@user, :master] + @commit.project.gl_project.team << [@user, :master] end describe "GET /:project/commits/:sha" do @@ -51,8 +50,10 @@ describe "Commits" do context "Public pages" do before do - @project = FactoryGirl.create :ci_public_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit + @commit.project.public = true + @commit.project.save + @build = FactoryGirl.create :ci_build, commit: @commit end diff --git a/spec/mailers/ci/notify_spec.rb b/spec/mailers/ci/notify_spec.rb index 20d8ddcd135..b83fb41603b 100644 --- a/spec/mailers/ci/notify_spec.rb +++ b/spec/mailers/ci/notify_spec.rb @@ -5,8 +5,7 @@ describe Ci::Notify do include EmailSpec::Matchers before do - @project = FactoryGirl.create :ci_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit @build = FactoryGirl.create :ci_build, commit: @commit end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index ce801152042..82623bd8190 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -27,7 +27,8 @@ require 'spec_helper' describe Ci::Build do let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let(:build) { FactoryGirl.create :ci_build, commit: commit } it { is_expected.to belong_to(:commit) } diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 586c9dc23a7..c277cbd8bc0 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -18,9 +18,8 @@ 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(:commit) { FactoryGirl.create :ci_commit } + let(:commit_with_project) { FactoryGirl.create :ci_commit } let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) } it { is_expected.to belong_to(:project) } @@ -65,7 +64,8 @@ describe Ci::Commit do project = FactoryGirl.create :ci_project, email_add_pusher: true, email_recipients: '' - commit = FactoryGirl.create :ci_commit, project: project + gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project + commit = FactoryGirl.create :ci_commit, gl_project: gl_project expected = 'commit_pusher_email' allow(commit).to receive(:push_data) { { user_email: expected } } expect(commit.project_recipients).to eq([expected]) @@ -75,7 +75,8 @@ describe Ci::Commit do project = FactoryGirl.create :ci_project, email_add_pusher: true, email_recipients: 'rec1 rec2' - commit = FactoryGirl.create :ci_commit, project: project + gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project + commit = FactoryGirl.create :ci_commit, gl_project: gl_project expected = 'commit_pusher_email' allow(commit).to receive(:push_data) { { user_email: expected } } expect(commit.project_recipients).to eq(['rec1', 'rec2', expected]) @@ -85,7 +86,8 @@ describe Ci::Commit do project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'rec1 rec2' - commit = FactoryGirl.create :ci_commit, project: project + gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project + commit = FactoryGirl.create :ci_commit, project: gl_project expect(commit.project_recipients).to eq(['rec1', 'rec2']) end @@ -93,7 +95,8 @@ describe Ci::Commit do project = FactoryGirl.create :ci_project, email_add_pusher: true, email_recipients: 'rec1 rec1 rec2' - commit = FactoryGirl.create :ci_commit, project: project + gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project + commit = FactoryGirl.create :ci_commit, project: gl_project expected = 'rec2' allow(commit).to receive(:push_data) { { user_email: expected } } expect(commit.project_recipients).to eq(['rec1', 'rec2']) @@ -219,8 +222,7 @@ describe Ci::Commit do end describe "#finished_at" do - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } it "returns finished_at of latest build" do build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60 @@ -238,7 +240,8 @@ describe Ci::Commit do describe "coverage" do let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } it "calculates average when there are two builds with coverage" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb index b5f37b349db..0d9f85959ba 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/mail_service_spec.rb @@ -32,7 +32,8 @@ describe Ci::MailService do describe 'failed build' do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } before do @@ -54,7 +55,8 @@ describe Ci::MailService do 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(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do @@ -81,7 +83,8 @@ describe Ci::MailService do email_only_broken_builds: false, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do @@ -109,7 +112,8 @@ describe Ci::MailService do email_only_broken_builds: true, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do @@ -137,7 +141,8 @@ describe Ci::MailService do email_only_broken_builds: false, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } before do @@ -159,7 +164,8 @@ describe Ci::MailService do email_only_broken_builds: true, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } before do 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 063d46b84d4..8f91a986b3a 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -33,8 +33,7 @@ describe Ci::HipChatService do describe "Execute" do let(:service) { Ci::HipChatService.new } - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index 0524f472432..80adadc591b 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -31,8 +31,7 @@ describe Ci::SlackService do describe "Execute" do let(:slack) { Ci::SlackService.new } - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } 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/project_spec.rb b/spec/models/ci/project_spec.rb index 261ea69f5b4..a45f1094fd6 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -81,10 +81,11 @@ describe Ci::Project do context :valid_project do let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } context :project_with_commit_and_builds do before do - commit = FactoryGirl.create(:ci_commit, project: project) FactoryGirl.create(:ci_build, commit: commit) end diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 2c575056b08..f6354f3cbca 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -29,8 +29,7 @@ describe Ci::Service do end describe "Testable" do - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit } before do diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c25d1823306..bad250fbf48 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -5,10 +5,12 @@ describe Ci::API::API do let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } let(:project) { FactoryGirl.create(:ci_project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } describe "Builds API for runners" do let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } + let(:shared_gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: shared_project) } before do FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id @@ -16,7 +18,7 @@ describe Ci::API::API do describe "POST /builds/register" do it "should start a build" do - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) commit.create_builds build = commit.builds.first @@ -34,7 +36,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for specific runner" do - commit = FactoryGirl.create(:ci_commit, project: shared_project) + commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) post ci_api("/builds/register"), token: runner.token @@ -43,7 +45,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for shared runner" do - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) post ci_api("/builds/register"), token: shared_runner.token @@ -52,7 +54,7 @@ describe Ci::API::API do end it "returns options" do - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) commit.create_builds post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -62,7 +64,7 @@ describe Ci::API::API do end it "returns variables" do - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) commit.create_builds project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") @@ -77,7 +79,7 @@ describe Ci::API::API do it "returns variables for triggers" do trigger = FactoryGirl.create(:ci_trigger, project: project) - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds(trigger_request) @@ -95,7 +97,7 @@ describe Ci::API::API do end describe "PUT /builds/:id" do - let(:commit) { FactoryGirl.create(:ci_commit, project: project)} + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project)} let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } it "should update a running build" do diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index e89b6651499..a41c321a300 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -4,7 +4,8 @@ describe Ci::API::API, 'Commits' do include ApiHelpers let(:project) { FactoryGirl.create(:ci_project) } - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } + let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:options) do { diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index ff6fdbdd6f1..bbe98e7dacd 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -6,6 +6,7 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } let!(:project) { FactoryGirl.create(:ci_project) } + let!(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let!(:project2) { FactoryGirl.create(:ci_project) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do @@ -33,7 +34,7 @@ describe Ci::API::API do context 'Have a commit' do before do - @commit = FactoryGirl.create(:ci_commit, project: project) + @commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) end it 'should create builds' do diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb index 998c386ead4..32ca52686a9 100644 --- a/spec/requests/ci/builds_spec.rb +++ b/spec/requests/ci/builds_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe "Builds" do before do - @project = FactoryGirl.create :ci_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit @build = FactoryGirl.create :ci_build, commit: @commit end diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb index fb317670339..4d7f132023b 100644 --- a/spec/requests/ci/commits_spec.rb +++ b/spec/requests/ci/commits_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe "Commits" do before do - @project = FactoryGirl.create :ci_project - @commit = FactoryGirl.create :ci_commit, project: @project + @commit = FactoryGirl.create :ci_commit end describe "GET /:project/refs/:ref_name/commits/:id/status.json" do diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index d12cd9773dc..525a24cc200 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService do let(:service) { Ci::CreateTriggerRequestService.new } let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:trigger) { FactoryGirl.create :ci_trigger, project: project } describe :execute do @@ -10,7 +11,7 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - @commit = FactoryGirl.create :ci_commit, project: project + @commit = FactoryGirl.create :ci_commit, gl_project: gl_project end it { expect(subject).to be_kind_of(Ci::TriggerRequest) } @@ -27,7 +28,7 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - FactoryGirl.create :ci_commit_without_jobs, project: project + FactoryGirl.create :ci_commit_without_jobs, gl_project: gl_project end it { expect(subject).to be_nil } @@ -37,9 +38,9 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - @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 + @commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: gl_project + @commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project + @commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, gl_project: gl_project end context 'retries latest one' do diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index 7565eb8f032..d7242d684c6 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -4,7 +4,8 @@ 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(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project, ref: 'master') } let(:build) { FactoryGirl.create(:ci_build, commit: commit) } describe :execute do diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 7b5af6c3dd0..96b7e2db3ed 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -3,9 +3,8 @@ 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!(:commit) { FactoryGirl.create :ci_commit } + let!(:pending_build) { FactoryGirl.create :ci_build, commit: commit } let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index cebdd145e40..aa48fcbcbfd 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Ci::WebHookService do let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let(:build) { FactoryGirl.create :ci_build, commit: commit } let(:hook) { FactoryGirl.create :ci_web_hook, project: project } -- cgit v1.2.1 From 0e3381470870732dff69c9298131062f786d55e7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 13:35:26 +0200 Subject: Fix tests --- app/models/ci/build.rb | 2 +- app/models/ci/commit.rb | 27 +++++++----- app/models/project.rb | 6 +++ spec/models/ci/commit_spec.rb | 12 +++--- .../ci/project_services/hip_chat_message_spec.rb | 6 +-- .../ci/project_services/hip_chat_service_spec.rb | 4 +- .../ci/project_services/slack_message_spec.rb | 6 +-- .../ci/project_services/slack_service_spec.rb | 4 +- spec/models/ci/project_spec.rb | 49 +++++++++++++--------- spec/models/ci/service_spec.rb | 2 +- spec/services/ci/register_build_service_spec.rb | 4 +- 11 files changed, 73 insertions(+), 49 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 9ac47ccfe4a..cda4fdd4982 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -186,7 +186,7 @@ module Ci end def project_id - commit.gl_project.gitlab_id + commit.project.id end def project_name diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 9a719787649..a6556690b9a 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -37,8 +37,15 @@ module Ci end def project - @project ||= gl_project.gitlab_ci_project - @project ||= gl_project.create_gitlab_ci_project + unless @project + gl_project.ensure_ci_project + @project = gl_project.gitlab_ci_project + end + @project + end + + def project_id + project.id end def last_build @@ -116,14 +123,14 @@ module Ci builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag) builds_attrs.map do |build_attrs| builds.create!({ - 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, - }) + 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 diff --git a/app/models/project.rb b/app/models/project.rb index 9ecf16d9812..ddf8526d6c2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -748,6 +748,12 @@ class Project < ActiveRecord::Base gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci? end + def ensure_ci_project + unless gitlab_ci_project + create_gitlab_ci_project + end + end + def enable_ci(user) # Enable service service = gitlab_ci_service || create_gitlab_ci_service diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index c277cbd8bc0..5429151c8d9 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -18,11 +18,13 @@ require 'spec_helper' describe Ci::Commit do - let(:commit) { FactoryGirl.create :ci_commit } - let(:commit_with_project) { FactoryGirl.create :ci_commit } + let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:commit_with_project) { FactoryGirl.create :ci_commit, gl_project: gl_project } let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) } - it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:gl_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 } @@ -87,7 +89,7 @@ describe Ci::Commit do email_add_pusher: false, email_recipients: 'rec1 rec2' gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, project: gl_project + commit = FactoryGirl.create :ci_commit, gl_project: gl_project expect(commit.project_recipients).to eq(['rec1', 'rec2']) end @@ -96,7 +98,7 @@ describe Ci::Commit do email_add_pusher: true, email_recipients: 'rec1 rec1 rec2' gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, project: gl_project + commit = FactoryGirl.create :ci_commit, gl_project: gl_project expected = 'rec2' allow(commit).to receive(:push_data) { { user_email: expected } } expect(commit.project_recipients).to eq(['rec1', 'rec2']) 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 49ac0860259..1903c036924 100644 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -3,10 +3,8 @@ require 'spec_helper' describe Ci::HipChatMessage do subject { Ci::HipChatMessage.new(build) } - let(:project) { FactoryGirl.create(:ci_project) } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) } let(:build) do commit.create_builds @@ -37,7 +35,7 @@ describe Ci::HipChatMessage do end context "Several builds" do - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } 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 8f91a986b3a..d9ccc855edf 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -39,8 +39,8 @@ describe Ci::HipChatService do before do allow(service).to receive_messages( - project: project, - project_id: project.id, + project: commit.project, + project_id: commit.project_id, notify_only_broken_builds: false, hipchat_room: 123, hipchat_token: 'a1b2c3d4e5f6' diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index f5335903728..7b541802d7d 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -3,10 +3,8 @@ require 'spec_helper' describe Ci::SlackMessage do subject { Ci::SlackMessage.new(commit) } - let(:project) { FactoryGirl.create :ci_project } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) } let(:build) do commit.create_builds @@ -43,7 +41,7 @@ describe Ci::SlackMessage do end context "Several builds" do - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs, project: project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } 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 80adadc591b..1ac7dfe568d 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -38,8 +38,8 @@ describe Ci::SlackService do before do allow(slack).to receive_messages( - project: project, - project_id: project.id, + project: commit.project, + project_id: commit.project_id, webhook: webhook_url, notify_only_broken_builds: notify_only_broken_builds ) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index a45f1094fd6..b83eccb94e2 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -28,9 +28,17 @@ require 'spec_helper' describe Ci::Project do + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let (:gl_project) { } subject { FactoryGirl.build :ci_project } - it { is_expected.to have_many(:commits) } + it { is_expected.to have_many(:runner_projects) } + it { is_expected.to have_many(:runners) } + it { is_expected.to have_many(:web_hooks) } + it { is_expected.to have_many(:events) } + it { is_expected.to have_many(:variables) } + it { is_expected.to have_many(:triggers) } + it { is_expected.to have_many(:services) } it { is_expected.to validate_presence_of :name } it { is_expected.to validate_presence_of :timeout } @@ -50,41 +58,44 @@ describe Ci::Project do describe "ordered_by_last_commit_date" do it "returns ordered projects" do - newest_project = FactoryGirl.create :ci_project - oldest_project = FactoryGirl.create :ci_project - project_without_commits = FactoryGirl.create :ci_project + newest_project = FactoryGirl.create :empty_project + newest_project.ensure_ci_project + oldest_project = FactoryGirl.create :empty_project + oldest_project.ensure_ci_project + project_without_commits = FactoryGirl.create :empty_project + project_without_commits.ensure_ci_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 + FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project + FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project - expect(Ci::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.gitlab_ci_project, oldest_project.gitlab_ci_project, project_without_commits.gitlab_ci_project]) end end describe 'ordered commits' do - let(:project) { FactoryGirl.create :ci_project } + let(:project) { FactoryGirl.create :empty_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 - expect(project.commits).to eq([commit2, commit1]) + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + expect(project.commits).to eq([commit2.project, commit1.project]) 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 - expect(project.commits).to eq([commit2, commit4, commit3, commit1]) + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + expect(project.commits).to eq([commit2.project, commit4.project, commit3.project, commit1.project]) end end context :valid_project do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } + let(:commit) { FactoryGirl.create(:ci_commit) } context :project_with_commit_and_builds do + let(:project) { commit.project } + before do FactoryGirl.create(:ci_build, commit: commit) end diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index f6354f3cbca..2df70e88888 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -34,7 +34,7 @@ describe Ci::Service do before do allow(@service).to receive_messages( - project: project + project: commit.project ) build @testable = @service.can_test? diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 96b7e2db3ed..9057791ca43 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' module Ci describe RegisterBuildService do let!(:service) { RegisterBuildService.new } - let!(:commit) { FactoryGirl.create :ci_commit } + let!(:project) { FactoryGirl.create :ci_project } + let!(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let!(:pending_build) { FactoryGirl.create :ci_build, commit: commit } let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } -- cgit v1.2.1 From f88d9cee0fe7fc6436a14051eb20562b6b4d7d86 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 13:39:13 +0200 Subject: Fix migrations --- db/migrate/20150924125150_add_project_id_to_ci_commit.rb | 5 +++++ db/migrate/20150924125150_add_project_id_to_ci_tables.rb | 12 ------------ .../20150924125436_migrate_project_id_for_ci_commits.rb | 10 ++++++++++ .../20150924125436_migrate_project_id_for_ci_tables.rb | 15 --------------- .../20150924131004_add_ci_fields_to_projects_table.rb | 5 ----- 5 files changed, 15 insertions(+), 32 deletions(-) create mode 100644 db/migrate/20150924125150_add_project_id_to_ci_commit.rb delete mode 100644 db/migrate/20150924125150_add_project_id_to_ci_tables.rb create mode 100644 db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb delete mode 100644 db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb delete mode 100644 db/migrate/20150924131004_add_ci_fields_to_projects_table.rb diff --git a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb new file mode 100644 index 00000000000..1a761fe0f86 --- /dev/null +++ b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb @@ -0,0 +1,5 @@ +class AddProjectIdToCiCommit < ActiveRecord::Migration + def up + add_column :ci_commits, :gl_project_id, :integer + end +end diff --git a/db/migrate/20150924125150_add_project_id_to_ci_tables.rb b/db/migrate/20150924125150_add_project_id_to_ci_tables.rb deleted file mode 100644 index 8b45bcf5fe1..00000000000 --- a/db/migrate/20150924125150_add_project_id_to_ci_tables.rb +++ /dev/null @@ -1,12 +0,0 @@ -class AddProjectIdToCiTables < ActiveRecord::Migration - def up - add_column :ci_builds, :gl_project_id, :integer - add_column :ci_commits, :gl_project_id, :integer - add_column :ci_events, :gl_project_id, :integer - add_column :ci_runner_projects, :gl_project_id, :integer - add_column :ci_services, :gl_project_id, :integer - add_column :ci_triggers, :gl_project_id, :integer - add_column :ci_variables, :gl_project_id, :integer - add_column :ci_web_hooks, :gl_project_id, :integer - end -end diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb new file mode 100644 index 00000000000..cd449806717 --- /dev/null +++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb @@ -0,0 +1,10 @@ +class MigrateProjectIdForCiCommits < ActiveRecord::Migration + def up + execute( + "UPDATE ci_commits " + + "JOIN ci_projects ON ci_projects.id = ci_commits.project_id " + + "SET gl_project_id=ci_projects.gitlab_id " + + "WHERE gl_project_id IS NULL" + ) + end +end diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb deleted file mode 100644 index 28d63c8c840..00000000000 --- a/db/migrate/20150924125436_migrate_project_id_for_ci_tables.rb +++ /dev/null @@ -1,15 +0,0 @@ -class MigrateProjectIdForCiTables < ActiveRecord::Migration - TABLES = %w(ci_builds ci_commits ci_events ci_runner_projects - ci_services ci_triggers ci_variables ci_web_hooks) - - def up - TABLES.each do |table| - execute( - "UPDATE #{table} " + - "JOIN ci_projects ON ci_projects.id = #{table}.project_id " + - "SET gl_project_id=ci_projects.gitlab_id " + - "WHERE gl_project_id IS NULL" - ) - end - end -end diff --git a/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb b/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb deleted file mode 100644 index bb97ec30051..00000000000 --- a/db/migrate/20150924131004_add_ci_fields_to_projects_table.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddCiFieldsToProjectsTable < ActiveRecord::Migration - def up - add_column :projects, :shared_runners_enabled, :boolean, default: false - end -end -- cgit v1.2.1 From 3031209b07c1797fe837b0eb871c56bc1f0276bb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 13:40:09 +0200 Subject: Fix db/schema.rb --- db/schema.rb | 55 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 89aafba511b..990035fb59b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,6 +13,9 @@ ActiveRecord::Schema.define(version: 20150924131004) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + create_table "abuse_reports", force: true do |t| t.integer "reporter_id" t.integer "user_id" @@ -82,19 +85,19 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.integer "project_id" t.string "status" t.datetime "finished_at" - t.text "trace", limit: 2147483647 + t.text "trace" t.datetime "created_at" t.datetime "updated_at" t.datetime "started_at" t.integer "runner_id" - t.float "coverage", limit: 24 + t.float "coverage" t.integer "commit_id" t.text "commands" t.integer "job_id" t.string "name" - t.boolean "deploy", default: false + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false + t.boolean "allow_failure", default: false, null: false t.string "stage" t.integer "trigger_request_id" t.integer "gl_project_id" @@ -110,10 +113,10 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.string "ref" t.string "sha" t.string "before_sha" - t.text "push_data", limit: 16777215 + t.text "push_data" t.datetime "created_at" t.datetime "updated_at" - t.boolean "tag", default: false + t.boolean "tag", default: false t.text "yaml_errors" t.datetime "committed_at" t.integer "gl_project_id" @@ -430,9 +433,9 @@ ActiveRecord::Schema.define(version: 20150924131004) do create_table "merge_request_diffs", force: true do |t| t.string "state" - t.text "st_commits", limit: 2147483647 - t.text "st_diffs", limit: 2147483647 - t.integer "merge_request_id", null: false + t.text "st_commits" + t.text "st_diffs" + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -514,8 +517,8 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.string "line_code" t.string "commit_id" t.integer "noteable_id" - t.boolean "system", default: false, null: false - t.text "st_diff", limit: 2147483647 + t.boolean "system", default: false, null: false + t.text "st_diff" t.integer "updated_by_id" end @@ -584,26 +587,26 @@ ActiveRecord::Schema.define(version: 20150924131004) 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", limit: 24, 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.integer "commit_count", default: 0 - t.boolean "shared_runners_enabled", default: false + t.integer "commit_count", default: 0 + t.boolean "shared_runners_enabled", default: false end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree @@ -655,15 +658,15 @@ ActiveRecord::Schema.define(version: 20150924131004) do create_table "snippets", force: true do |t| t.string "title" - t.text "content", limit: 2147483647 - t.integer "author_id", null: false + t.text "content" + t.integer "author_id", null: false t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" t.string "file_name" t.datetime "expires_at" t.string "type" - t.integer "visibility_level", default: 0, null: false + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree -- cgit v1.2.1 From 6abca1284791475a8240e4b25b5e6e7175533101 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 13:41:21 +0200 Subject: Revert check_enable_flag! changes --- app/controllers/ci/application_controller.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index da77e2b94e8..d8227e632e4 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -11,10 +11,10 @@ module Ci private def check_enable_flag! - # unless current_application_settings.ci_enabled - # redirect_to(disabled_ci_projects_path) - # return - # end + unless current_application_settings.ci_enabled + redirect_to(disabled_ci_projects_path) + return + end end def authenticate_public_page! -- cgit v1.2.1 From 2c1f7ccac8180a49e45fb3cf79e03318420d1037 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 14:44:07 +0200 Subject: Fix register_build_service tests --- app/models/ci/runner.rb | 4 ++++ app/services/ci/register_build_service.rb | 6 +++--- spec/services/ci/register_build_service_spec.rb | 9 ++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 1e9f78a3748..6838ccfaaab 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -41,6 +41,10 @@ module Ci query: "%#{query.try(:downcase)}%") end + def gl_projects_ids + projects.select(:gitlab_id) + end + def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? end diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 33f1c1e918d..78cc51d31bb 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,10 +8,10 @@ module Ci builds = if current_runner.shared? # don't run projects which have not enables shared runners - builds.includes(:project).where(ci_projects: { shared_runners_enabled: true }) + builds.joins(commit: {gl_project: :gitlab_ci_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) + builds.joins(:commit).where(ci_commits: {gl_project_id: current_runner.gl_projects_ids}) end builds = builds.order('created_at ASC') @@ -19,7 +19,7 @@ module Ci 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 diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 9057791ca43..ae4239be821 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -3,15 +3,15 @@ require 'spec_helper' module Ci describe RegisterBuildService do let!(:service) { RegisterBuildService.new } - let!(:project) { FactoryGirl.create :ci_project } - let!(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let!(:gl_project) { FactoryGirl.create :empty_project } let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let!(:pending_build) { FactoryGirl.create :ci_build, 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) + gl_project.ensure_ci_project + specific_runner.assign_to(gl_project.gitlab_ci_project) end describe :execute do @@ -48,8 +48,7 @@ module Ci context 'allow shared runners' do before do - project.shared_runners_enabled = true - project.save + gl_project.gitlab_ci_project.update(shared_runners_enabled: true) end context 'shared runner' do -- cgit v1.2.1 From 8f8efcfa00c40beacae9886d7be51e82e7a67989 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 15:18:25 +0200 Subject: Fix tests --- app/models/ci/project.rb | 6 +++--- app/models/project.rb | 1 - app/views/ci/admin/projects/_project.html.haml | 2 +- spec/models/ci/project_spec.rb | 11 ++++------- spec/requests/ci/builds_spec.rb | 2 +- spec/requests/ci/commits_spec.rb | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 89bbbea5c5a..ba6e320426c 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -48,7 +48,7 @@ module Ci accepts_nested_attributes_for :variables, allow_destroy: true - delegate :commits, :builds, :last_commit, to: :gl_project + delegate :commits, :builds, to: :gl_project # # Validations @@ -103,8 +103,8 @@ module Ci 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"). + last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)" + joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") end diff --git a/app/models/project.rb b/app/models/project.rb index ddf8526d6c2..a5393d396f8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -120,7 +120,6 @@ class Project < ActiveRecord::Base has_many :starrers, through: :users_star_projects, source: :user 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', foreign_key: :gl_project_id has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' - has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml index c461206c72a..a342d6e1cf0 100644 --- a/app/views/ci/admin/projects/_project.html.haml +++ b/app/views/ci/admin/projects/_project.html.haml @@ -1,4 +1,4 @@ -- last_commit = project.last_commit +- last_commit = project.commits.last %tr %td = project.id diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index b83eccb94e2..fe12160659c 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -28,9 +28,8 @@ require 'spec_helper' describe Ci::Project do - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let (:gl_project) { } - subject { FactoryGirl.build :ci_project } + let(:gl_project) { FactoryGirl.create :empty_project } + subject { FactoryGirl.create :ci_project, gl_project: gl_project } it { is_expected.to have_many(:runner_projects) } it { is_expected.to have_many(:runners) } @@ -40,9 +39,7 @@ describe Ci::Project do it { is_expected.to have_many(:triggers) } it { is_expected.to have_many(:services) } - 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 @@ -78,7 +75,7 @@ describe Ci::Project do it 'returns ordered list of commits' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - expect(project.commits).to eq([commit2.project, commit1.project]) + expect(project.commits).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do @@ -86,7 +83,7 @@ describe Ci::Project do commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - expect(project.commits).to eq([commit2.project, commit4.project, commit3.project, commit1.project]) + expect(project.commits).to eq([commit2, commit4, commit3, commit1]) end end diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb index 32ca52686a9..f68116c52aa 100644 --- a/spec/requests/ci/builds_spec.rb +++ b/spec/requests/ci/builds_spec.rb @@ -8,7 +8,7 @@ describe "Builds" do describe "GET /:project/builds/:id/status.json" do before do - get status_ci_project_build_path(@project, @build), format: :json + get status_ci_project_build_path(@commit.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 4d7f132023b..3ab8c915dfd 100644 --- a/spec/requests/ci/commits_spec.rb +++ b/spec/requests/ci/commits_spec.rb @@ -7,7 +7,7 @@ describe "Commits" do describe "GET /:project/refs/:ref_name/commits/:id/status.json" do before do - get status_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), format: :json + get status_ci_project_ref_commits_path(@commit.project, @commit.ref, @commit.sha), format: :json end it { expect(response.status).to eq(200) } -- cgit v1.2.1 From e60647f011e30c215dfe596e079c616545071732 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 15:20:08 +0200 Subject: Fix rubocop --- app/services/ci/register_build_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 78cc51d31bb..71b61bbe389 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,10 +8,10 @@ module Ci builds = if current_runner.shared? # don't run projects which have not enables shared runners - builds.joins(commit: {gl_project: :gitlab_ci_project}).where(ci_projects: {shared_runners_enabled: true}) + builds.joins(commit: { gl_project: :gitlab_ci_project }).where(ci_projects: { shared_runners_enabled: true }) else # do run projects which are only assigned to this runner - builds.joins(:commit).where(ci_commits: {gl_project_id: current_runner.gl_projects_ids}) + builds.joins(:commit).where(ci_commits: { gl_project_id: current_runner.gl_projects_ids }) end builds = builds.order('created_at ASC') -- cgit v1.2.1 From 0fa4ab5fd8e0cb775ff4fe75d011ff717f0b3945 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 15:33:19 +0200 Subject: Rename commits to ci_commits --- app/models/ci/project.rb | 10 ++++++++-- app/models/project.rb | 4 ++-- spec/models/ci/project_spec.rb | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index ba6e320426c..77cce261fc8 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -48,8 +48,6 @@ module Ci accepts_nested_attributes_for :variables, allow_destroy: true - delegate :commits, :builds, to: :gl_project - # # Validations # @@ -210,5 +208,13 @@ module Ci def setup_finished? commits.any? end + + def commits + gl_project.ci_commits + end + + def builds + gl_project.ci_builds + end end end diff --git a/app/models/project.rb b/app/models/project.rb index a5393d396f8..d9334adef78 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -118,8 +118,8 @@ class Project < ActiveRecord::Base has_many :deploy_keys, through: :deploy_keys_projects has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user - 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', foreign_key: :gl_project_id - has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build' + has_many :ci_commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id + has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index fe12160659c..6ccd399e079 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -75,7 +75,7 @@ describe Ci::Project do it 'returns ordered list of commits' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - expect(project.commits).to eq([commit2, commit1]) + expect(project.ci_commits).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do @@ -83,7 +83,7 @@ describe Ci::Project do commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - expect(project.commits).to eq([commit2, commit4, commit3, commit1]) + expect(project.ci_commits).to eq([commit2, commit4, commit3, commit1]) end end -- cgit v1.2.1 From 0f3deac362cf2800ceaf17f4cded765f6c9d577c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 16:49:31 +0200 Subject: Fix tests --- features/steps/project/commits/commits.rb | 2 +- features/steps/shared/project.rb | 2 +- spec/lib/ci/charts_spec.rb | 5 ++--- spec/models/project_spec.rb | 8 +++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 56f1f06fb06..47f58091b93 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -104,7 +104,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'commit has ci status' do @project.enable_ci(@user) - create :ci_commit, project: @project.gitlab_ci_project, sha: sample_commit.id + create :ci_commit, gl_project: @project, sha: sample_commit.id end step 'I see commit ci info' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index fa841f67510..fc51cec150e 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -204,6 +204,6 @@ module SharedProject step 'project "Shop" has CI build' do project = Project.find_by(name: "Shop") - create :ci_commit, project: project.gitlab_ci_project, sha: project.commit.sha + create :ci_commit, gl_project: project, sha: project.commit.sha end end diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb index 24894e81983..83e2ad220b8 100644 --- a/spec/lib/ci/charts_spec.rb +++ b/spec/lib/ci/charts_spec.rb @@ -4,13 +4,12 @@ describe "Charts" do context "build_times" do before do - @project = FactoryGirl.create(:ci_project) - @commit = FactoryGirl.create(:ci_commit, project: @project) + @commit = FactoryGirl.create(:ci_commit) FactoryGirl.create(:ci_build, commit: @commit) end it 'should return build times in minutes' do - chart = Ci::Charts::BuildTime.new(@project) + chart = Ci::Charts::BuildTime.new(@commit.project) expect(chart.build_times).to eq([2]) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9e7b6f5cb30..cbb49044cd1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -404,10 +404,12 @@ describe Project do describe :ci_commit do let(:project) { create :project } - let(:ci_project) { create :ci_project, gl_project: project } - let(:commit) { create :ci_commit, project: ci_project } + let(:commit) { create :ci_commit, gl_project: project } - before { project.create_gitlab_ci_service(active: true) } + before do + project.ensure_ci_project + project.create_gitlab_ci_service(active: true) + end it { expect(project.ci_commit(commit.sha)).to eq(commit) } end -- cgit v1.2.1 From 9498a40052603a0687a0fa6370e50e97bc078301 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 28 Sep 2015 16:49:40 +0200 Subject: Fix migrations --- ...0924125436_migrate_project_id_for_ci_commits.rb | 8 ++----- db/schema.rb | 26 ++++++++-------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb index cd449806717..2be57b6062e 100644 --- a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb +++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb @@ -1,10 +1,6 @@ class MigrateProjectIdForCiCommits < ActiveRecord::Migration def up - execute( - "UPDATE ci_commits " + - "JOIN ci_projects ON ci_projects.id = ci_commits.project_id " + - "SET gl_project_id=ci_projects.gitlab_id " + - "WHERE gl_project_id IS NULL" - ) + subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id' + execute("UPDATE ci_commits SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") end end diff --git a/db/schema.rb b/db/schema.rb index 990035fb59b..0302da66599 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: 20150924131004) do +ActiveRecord::Schema.define(version: 20150924125436) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -45,8 +45,8 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" - t.boolean "ci_enabled", default: true, null: false t.text "help_page_text" + t.boolean "ci_enabled", default: true, null: false end create_table "audit_events", force: true do |t| @@ -100,7 +100,6 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.boolean "allow_failure", default: false, null: false t.string "stage" t.integer "trigger_request_id" - t.integer "gl_project_id" end add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree @@ -135,7 +134,6 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" end add_index "ci_events", ["created_at"], name: "index_ci_events_on_created_at", using: :btree @@ -183,11 +181,10 @@ ActiveRecord::Schema.define(version: 20150924131004) do end create_table "ci_runner_projects", force: true do |t| - t.integer "runner_id", null: false - t.integer "project_id", null: false + t.integer "runner_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" end add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree @@ -211,12 +208,11 @@ ActiveRecord::Schema.define(version: 20150924131004) do create_table "ci_services", force: true do |t| t.string "type" t.string "title" - t.integer "project_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" - t.integer "gl_project_id" end add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree @@ -261,11 +257,10 @@ ActiveRecord::Schema.define(version: 20150924131004) do create_table "ci_triggers", force: true do |t| t.string "token" - t.integer "project_id", null: false + t.integer "project_id", null: false t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" end add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree @@ -277,17 +272,15 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" - t.integer "gl_project_id" end add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree create_table "ci_web_hooks", force: true do |t| - t.string "url", null: false - t.integer "project_id", null: false + t.string "url", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" end create_table "deploy_keys_projects", force: true do |t| @@ -606,7 +599,6 @@ ActiveRecord::Schema.define(version: 20150924131004) do t.string "import_type" t.string "import_source" t.integer "commit_count", default: 0 - t.boolean "shared_runners_enabled", default: false end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree -- cgit v1.2.1 From 0d877d91e7556edfcdc29ad77491740da3cc7661 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 29 Sep 2015 10:44:53 +0200 Subject: Make ensure_gitlab_ci_project return ci_project or create a new one --- app/models/ci/commit.rb | 6 +----- app/models/project.rb | 6 ++---- spec/models/ci/project_spec.rb | 8 ++++---- spec/models/project_spec.rb | 2 +- spec/services/ci/register_build_service_spec.rb | 3 +-- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index a6556690b9a..6d048779cde 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -37,11 +37,7 @@ module Ci end def project - unless @project - gl_project.ensure_ci_project - @project = gl_project.gitlab_ci_project - end - @project + @project ||= gl_project.ensure_gitlab_ci_project end def project_id diff --git a/app/models/project.rb b/app/models/project.rb index d9334adef78..953b37e3f7a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -747,10 +747,8 @@ class Project < ActiveRecord::Base gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci? end - def ensure_ci_project - unless gitlab_ci_project - create_gitlab_ci_project - end + def ensure_gitlab_ci_project + gitlab_ci_project || create_gitlab_ci_project end def enable_ci(user) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 6ccd399e079..466c7afaf1e 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -56,16 +56,16 @@ describe Ci::Project do describe "ordered_by_last_commit_date" do it "returns ordered projects" do newest_project = FactoryGirl.create :empty_project - newest_project.ensure_ci_project + newest_ci_project = newest_project.ensure_gitlab_ci_project oldest_project = FactoryGirl.create :empty_project - oldest_project.ensure_ci_project + oldest_ci_project = oldest_project.ensure_gitlab_ci_project project_without_commits = FactoryGirl.create :empty_project - project_without_commits.ensure_ci_project + ci_project_without_commits = project_without_commits.ensure_gitlab_ci_project FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project - expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_project.gitlab_ci_project, oldest_project.gitlab_ci_project, project_without_commits.gitlab_ci_project]) + expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_ci_project, oldest_ci_project, ci_project_without_commits]) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cbb49044cd1..ba8897b95d9 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -407,7 +407,7 @@ describe Project do let(:commit) { create :ci_commit, gl_project: project } before do - project.ensure_ci_project + project.ensure_gitlab_ci_project project.create_gitlab_ci_service(active: true) end diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index ae4239be821..781764627ac 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -10,8 +10,7 @@ module Ci let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } before do - gl_project.ensure_ci_project - specific_runner.assign_to(gl_project.gitlab_ci_project) + specific_runner.assign_to(gl_project.ensure_gitlab_ci_project) end describe :execute do -- cgit v1.2.1 From 3591afeef0da33429b9fd08bbf066120f1653af3 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 29 Sep 2015 13:37:07 +0200 Subject: Apache needs gitlab-git-http-server on TCP --- 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 86c0825dff9..552216be932 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -168,6 +168,7 @@ git diff origin/7-14-stable:lib/support/nginx/gitlab origin/8-0-stable:lib/suppo ``` If you are using Apache instead of NGINX please see the updated [Apache templates](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache). +Also note that because Apache does not support upstreams behind Unix sockets you will need to let gitlab-git-http-server listen on a TCP port. You can do this via [/etc/default/gitlab](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-0-stable/lib/support/init.d/gitlab.default.example#L34). ### 9. Migrate GitLab CI to GitLab CE/EE -- cgit v1.2.1 From 87240e989ba913bad787d8bc81da1a9b87f1da53 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 29 Sep 2015 16:07:44 +0200 Subject: Move CI project settings page to CE project settings area Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/projects_controller.rb | 33 +------ app/controllers/projects/application_controller.rb | 2 +- app/controllers/projects/ci_settings_controller.rb | 36 +++++++ app/helpers/ci/gitlab_helper.rb | 4 +- app/views/ci/projects/_form.html.haml | 100 -------------------- app/views/ci/projects/edit.html.haml | 21 ----- app/views/layouts/ci/_nav_project.html.haml | 6 -- app/views/layouts/nav/_project_settings.html.haml | 5 + app/views/projects/ci_settings/_form.html.haml | 103 +++++++++++++++++++++ app/views/projects/ci_settings/edit.html.haml | 21 +++++ config/routes.rb | 1 + spec/features/ci/projects_spec.rb | 18 ---- spec/features/ci_settings_spec.rb | 22 +++++ 14 files changed, 194 insertions(+), 179 deletions(-) create mode 100644 app/controllers/projects/ci_settings_controller.rb delete mode 100644 app/views/ci/projects/_form.html.haml delete mode 100644 app/views/ci/projects/edit.html.haml create mode 100644 app/views/projects/ci_settings/_form.html.haml create mode 100644 app/views/projects/ci_settings/edit.html.haml create mode 100644 spec/features/ci_settings_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 458758b3205..a499dda2bb3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.1.0 (unreleased) - Move CI runners page to project settings area - Move CI variables page to project settings area - Move CI triggers page to project settings area + - Move CI project settings page to CE project settings area v 8.0.3 - Fix URL shown in Slack notifications diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 13766fb8f6f..e8788955eba 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -2,9 +2,9 @@ module Ci class ProjectsController < Ci::ApplicationController before_action :authenticate_user!, except: [:build, :badge, :show] before_action :authenticate_public_page!, only: :show - before_action :project, only: [:build, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_action :project, only: [:build, :show, :badge, :toggle_shared_runners, :dumped_yaml] before_action :authorize_access_project!, except: [:build, :badge, :show, :new, :disabled] - before_action :authorize_manage_project!, only: [:edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] + before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] before_action :authenticate_token!, only: [:build] before_action :no_cache, only: [:badge] skip_before_action :check_enable_flag!, only: [:disabled] @@ -23,28 +23,6 @@ module Ci @commits = @commits.page(params[:page]).per(20) 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.gl_project.gitlab_ci_service.update_attributes(active: false) - project.destroy - - Ci::EventService.new.remove_project(current_user, project) - - redirect_to ci_projects_url - end - # Project status badge # Image with build status for sha or ref def badge @@ -74,12 +52,5 @@ module Ci 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/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 56a63ce9758..519d6d6127e 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -33,6 +33,6 @@ class Projects::ApplicationController < ApplicationController end def ci_project - @ci_project ||= @project.gitlab_ci_project + @ci_project ||= @project.ensure_gitlab_ci_project end end diff --git a/app/controllers/projects/ci_settings_controller.rb b/app/controllers/projects/ci_settings_controller.rb new file mode 100644 index 00000000000..a263242a850 --- /dev/null +++ b/app/controllers/projects/ci_settings_controller.rb @@ -0,0 +1,36 @@ +class Projects::CiSettingsController < Projects::ApplicationController + before_action :ci_project + before_action :authorize_admin_project! + + layout "project_settings" + + def edit + end + + def update + if ci_project.update_attributes(project_params) + Ci::EventService.new.change_project_settings(current_user, ci_project) + + redirect_to edit_namespace_project_ci_settings_path(project.namespace, project), notice: 'Project was successfully updated.' + else + render action: "edit" + end + end + + def destroy + ci_project.destroy + Ci::EventService.new.remove_project(current_user, ci_project) + project.gitlab_ci_service.update_attributes(active: false) + + redirect_to project_path(project), notice: "CI was disabled for this project" + end + + protected + + 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 diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb index 2b89a0ce93e..13e4d0fd9c3 100644 --- a/app/helpers/ci/gitlab_helper.rb +++ b/app/helpers/ci/gitlab_helper.rb @@ -27,9 +27,9 @@ module Ci commits = project.commits if commits.any? && commits.last.push_data[:ci_yaml_file] - "#{@project.gitlab_url}/edit/master/.gitlab-ci.yml" + "#{project.gitlab_url}/edit/master/.gitlab-ci.yml" else - "#{@project.gitlab_url}/new/master" + "#{project.gitlab_url}/new/master" end end end diff --git a/app/views/ci/projects/_form.html.haml b/app/views/ci/projects/_form.html.haml deleted file mode 100644 index e782fd8a0f7..00000000000 --- a/app/views/ci/projects/_form.html.haml +++ /dev/null @@ -1,100 +0,0 @@ -.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' - - 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/edit.html.haml b/app/views/ci/projects/edit.html.haml deleted file mode 100644 index 876ae5182d4..00000000000 --- a/app/views/ci/projects/edit.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -- if @project.generated_yaml_config - %p.alert.alert-danger - CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_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/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 2d3cc3cf983..3a2741367c1 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -26,9 +26,3 @@ = icon('book fw') %span Events - %li.separate-item - = nav_link path: 'projects#edit' do - = link_to edit_ci_project_path(@project) do - = icon('cogs fw') - %span - Settings diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 28efb035d09..26cccb48f68 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -50,3 +50,8 @@ = icon('retweet fw') %span Triggers + = nav_link path: 'ci_settings#edit' do + = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project) do + = icon('building fw') + %span + CI Settings diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml new file mode 100644 index 00000000000..9f891f557a9 --- /dev/null +++ b/app/views/projects/ci_settings/_form.html.haml @@ -0,0 +1,103 @@ +%h3.page-title + CI settings +%hr +.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(@ci_project)} + += nested_form_for @ci_project, url: namespace_project_ci_settings_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| + - if @ci_project.errors.any? + #error_explanation + %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" + .alert.alert-error + %ul + - @ci_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' + - unless @ci_project.new_record? + = link_to 'Remove Project', ci_project_path(@ci_project), method: :delete, data: { confirm: 'Project will be removed. Are you sure?' }, class: 'btn btn-danger pull-right' diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml new file mode 100644 index 00000000000..e9040fe4337 --- /dev/null +++ b/app/views/projects/ci_settings/edit.html.haml @@ -0,0 +1,21 @@ +- if @ci_project.generated_yaml_config + %p.alert.alert-danger + CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_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 @ci_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, @ci_project.generated_yaml_config, size: "70x25", class: "form-control" + .modal-footer + %button.btn.btn-default{"data-dismiss" => "modal", :type => "button"} Close diff --git a/config/routes.rb b/config/routes.rb index f7317fb5d9f..6d96d8801cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -590,6 +590,7 @@ Gitlab::Application.routes.draw do resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] + resource :ci_settings, only: [:edit, :update, :destroy] resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do member do diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb index 7c8cd1ce5c7..c633acf85fb 100644 --- a/spec/features/ci/projects_spec.rb +++ b/spec/features/ci/projects_spec.rb @@ -17,22 +17,4 @@ describe "Projects" do it { expect(page).to have_content @project.name } it { expect(page).to have_content 'All commits' } end - - describe "GET /ci/projects/:id/edit" do - before do - visit edit_ci_project_path(@project) - end - - 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' - - expect(page).to have_content 'was successfully updated' - - expect(find_field('Timeout').value).to eq '70' - end - end end diff --git a/spec/features/ci_settings_spec.rb b/spec/features/ci_settings_spec.rb new file mode 100644 index 00000000000..7e25e883018 --- /dev/null +++ b/spec/features/ci_settings_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe "CI settings" do + let(:user) { create(:user) } + before { login_as(user) } + + before do + @project = FactoryGirl.create :ci_project + @gl_project = @project.gl_project + @gl_project.team << [user, :master] + visit edit_namespace_project_ci_settings_path(@gl_project.namespace, @gl_project) + end + + it { expect(page).to have_content 'Build Schedule' } + + it "updates configuration" do + fill_in 'Timeout', with: '70' + click_button 'Save changes' + expect(page).to have_content 'was successfully updated' + expect(find_field('Timeout').value).to eq '70' + end +end -- cgit v1.2.1 From 3d9a7cdb4be6d6d059dfd7ccc8fa2e4bd3ab12e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 29 Sep 2015 16:26:40 +0200 Subject: Refactor CI helpers Signed-off-by: Dmitriy Zaporozhets --- app/helpers/builds_helper.rb | 17 ++++++++++ app/helpers/ci/application_helper.rb | 54 ------------------------------ app/helpers/ci/builds_helper.rb | 19 ----------- app/helpers/ci/icons_helper.rb | 11 ------ app/helpers/ci/runners_helper.rb | 22 ------------ app/helpers/ci/triggers_helper.rb | 7 ---- app/helpers/ci/user_helper.rb | 15 --------- app/helpers/ci_status_helper.rb | 36 +++++++++++++------- app/helpers/runners_helper.rb | 20 +++++++++++ app/helpers/time_helper.rb | 27 +++++++++++++++ app/helpers/triggers_helper.rb | 5 +++ spec/helpers/ci/application_helper_spec.rb | 37 -------------------- spec/helpers/ci/runners_helper_spec.rb | 18 ---------- spec/helpers/runners_helper_spec.rb | 18 ++++++++++ spec/helpers/time_helper_spec.rb | 37 ++++++++++++++++++++ 15 files changed, 147 insertions(+), 196 deletions(-) create mode 100644 app/helpers/builds_helper.rb delete mode 100644 app/helpers/ci/application_helper.rb delete mode 100644 app/helpers/ci/builds_helper.rb delete mode 100644 app/helpers/ci/icons_helper.rb delete mode 100644 app/helpers/ci/runners_helper.rb delete mode 100644 app/helpers/ci/triggers_helper.rb delete mode 100644 app/helpers/ci/user_helper.rb create mode 100644 app/helpers/runners_helper.rb create mode 100644 app/helpers/time_helper.rb create mode 100644 app/helpers/triggers_helper.rb delete mode 100644 spec/helpers/ci/application_helper_spec.rb delete mode 100644 spec/helpers/ci/runners_helper_spec.rb create mode 100644 spec/helpers/runners_helper_spec.rb create mode 100644 spec/helpers/time_helper_spec.rb diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb new file mode 100644 index 00000000000..b6658e52d09 --- /dev/null +++ b/app/helpers/builds_helper.rb @@ -0,0 +1,17 @@ +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 +end diff --git a/app/helpers/ci/application_helper.rb b/app/helpers/ci/application_helper.rb deleted file mode 100644 index 9fe6282bb81..00000000000 --- a/app/helpers/ci/application_helper.rb +++ /dev/null @@ -1,54 +0,0 @@ -module Ci - module ApplicationHelper - def loader_html - image_tag 'ci/loader.gif', alt: 'Loading' - end - - def date_from_to(from, to) - "#{from.to_s(:short)} - #{to.to_s(:short)}" - 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 - - def ci_icon_for_status(status) - icon_name = - case status - when 'success' - 'check-square' - when 'failed' - 'close' - when 'running', 'pending' - 'clock-o' - else - 'circle' - end - - icon(icon_name) - end - - def ci_status_with_icon(status) - content_tag :span, class: "ci-status ci-#{status}" do - ci_icon_for_status(status) + ' '.html_safe + status - end - end - end -end diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb deleted file mode 100644 index 5d6e785d951..00000000000 --- a/app/helpers/ci/builds_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -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 - end -end diff --git a/app/helpers/ci/icons_helper.rb b/app/helpers/ci/icons_helper.rb deleted file mode 100644 index be40f79e880..00000000000 --- a/app/helpers/ci/icons_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Ci - module IconsHelper - def boolean_to_icon(value) - if value.to_s == "true" - content_tag :i, nil, class: 'fa fa-circle cgreen' - else - content_tag :i, nil, class: 'fa fa-power-off clgray' - end - end - end -end diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb deleted file mode 100644 index 03c9914641e..00000000000 --- a/app/helpers/ci/runners_helper.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Ci - module RunnersHelper - def runner_status_icon(runner) - unless runner.contacted_at - return content_tag :i, nil, - class: "fa 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 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 deleted file mode 100644 index 0d2438928ce..00000000000 --- a/app/helpers/ci/triggers_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Ci - module TriggersHelper - 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 -end diff --git a/app/helpers/ci/user_helper.rb b/app/helpers/ci/user_helper.rb deleted file mode 100644 index c332d6ed9cf..00000000000 --- a/app/helpers/ci/user_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -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_status_helper.rb b/app/helpers/ci_status_helper.rb index 18c30ddb281..3a88ed7107e 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -4,19 +4,7 @@ module CiStatusHelper end def ci_status_icon(ci_commit) - icon_name = - case ci_commit.status - when 'success' - 'check' - when 'failed' - 'close' - when 'running', 'pending' - 'clock-o' - else - 'circle' - end - - icon(icon_name) + ci_icon_for_status(ci_commit.status) end def ci_status_color(ci_commit) @@ -31,4 +19,26 @@ module CiStatusHelper 'gray' end end + + def ci_status_with_icon(status) + content_tag :span, class: "ci-status ci-#{status}" do + ci_icon_for_status(status) + ' '.html_safe + status + end + end + + def ci_icon_for_status(status) + icon_name = + case status + when 'success' + 'check' + when 'failed' + 'close' + when 'running', 'pending' + 'clock-o' + else + 'circle' + end + + icon(icon_name) + end end diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb new file mode 100644 index 00000000000..5d7d06c8490 --- /dev/null +++ b/app/helpers/runners_helper.rb @@ -0,0 +1,20 @@ +module RunnersHelper + def runner_status_icon(runner) + unless runner.contacted_at + return content_tag :i, nil, + class: "fa 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 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/helpers/time_helper.rb b/app/helpers/time_helper.rb new file mode 100644 index 00000000000..8142f733e76 --- /dev/null +++ b/app/helpers/time_helper.rb @@ -0,0 +1,27 @@ +module TimeHelper + 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 + + + def date_from_to(from, to) + "#{from.to_s(:short)} - #{to.to_s(:short)}" + end +end diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb new file mode 100644 index 00000000000..2a3a7e80fca --- /dev/null +++ b/app/helpers/triggers_helper.rb @@ -0,0 +1,5 @@ +module TriggersHelper + 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 diff --git a/spec/helpers/ci/application_helper_spec.rb b/spec/helpers/ci/application_helper_spec.rb deleted file mode 100644 index 6a216715b7f..00000000000 --- a/spec/helpers/ci/application_helper_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -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| - 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 - expect(duration_in_words(nil, Time.now - 5)).to eq("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| - expect(time_interval_in_words(interval)).to eq(expectation) - end - end - end -end diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb deleted file mode 100644 index 6d0e2d3d1e1..00000000000 --- a/spec/helpers/ci/runners_helper_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'spec_helper' - -describe Ci::RunnersHelper do - it "returns - not contacted yet" do - runner = FactoryGirl.build :ci_runner - expect(runner_status_icon(runner)).to include("not connected yet") - end - - it "returns offline text" do - 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(:ci_runner, contacted_at: 1.hour.ago, active: true) - expect(runner_status_icon(runner)).to include("Runner is online") - end -end diff --git a/spec/helpers/runners_helper_spec.rb b/spec/helpers/runners_helper_spec.rb new file mode 100644 index 00000000000..b3d635a1932 --- /dev/null +++ b/spec/helpers/runners_helper_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe RunnersHelper do + it "returns - not contacted yet" do + runner = FactoryGirl.build :ci_runner + expect(runner_status_icon(runner)).to include("not connected yet") + end + + it "returns offline text" do + 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(:ci_runner, contacted_at: 1.hour.ago, active: true) + expect(runner_status_icon(runner)).to include("Runner is online") + end +end diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb new file mode 100644 index 00000000000..3f62527c5bb --- /dev/null +++ b/spec/helpers/time_helper_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe TimeHelper 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| + 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 + expect(duration_in_words(nil, Time.now - 5)).to eq("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| + expect(time_interval_in_words(interval)).to eq(expectation) + end + end + end +end -- cgit v1.2.1 From 70460f7989739fe06a0c2fbef8dc35bd31e18322 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 29 Sep 2015 17:20:14 +0200 Subject: Use Golang 1.5.1 for new installs from source --- 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 039bb3c2561..6d3b318737c 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -131,11 +131,11 @@ Since GitLab 8.0, Git HTTP requests are handled by gitlab-git-http-server. This is a small daemon written in Go. To install gitlab-git-http-server we need a Go compiler. - 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 + curl -O --progress https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz + echo '46eecd290d8803887dec718c691cc243f2175fe0 go1.5.1.linux-amd64.tar.gz' | shasum -c - && \ + sudo tar -C /usr/local -xzf go1.5.1.linux-amd64.tar.gz sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ - rm go1.5.linux-amd64.tar.gz + rm go1.5.1.linux-amd64.tar.gz ## 4. System Users -- cgit v1.2.1 From 60b3055c0c3061ed0eb2ff40913b6b9d85c6cbae Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 29 Sep 2015 17:24:22 +0200 Subject: Use Ruby 2.1.7 for new installs from source --- doc/install/installation.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 6d3b318737c..518b914fe67 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -115,8 +115,9 @@ Remove the old Ruby 1.8 if present Download Ruby and compile it: mkdir /tmp/ruby && cd /tmp/ruby - curl -L --progress http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.6.tar.gz | tar xz - cd ruby-2.1.6 + curl -O --progress https://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.7.tar.gz + echo 'e2e195a4a58133e3ad33b955c829bb536fa3c075 ruby-2.1.7.tar.gz' | shasum -c - && tar xzf ruby-2.1.7.tar.gz + cd ruby-2.1.7 ./configure --disable-install-rdoc make sudo make install -- cgit v1.2.1 From e471356f76ca2561cf26fc01d6527c6b90e4bf9e Mon Sep 17 00:00:00 2001 From: Diogo Resende Date: Wed, 30 Sep 2015 08:34:00 +0000 Subject: Fixes english information paragraph in admin/applications view --- app/views/admin/applications/index.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index fc921a966f3..6ac45a3db1a 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -2,7 +2,7 @@ %h3.page-title System OAuth applications %p.light - System OAuth application does not belong to certain user and can be managed only by admins + System OAuth application don't belong to any user and can only be managed by admins %hr %p= link_to 'New Application', new_admin_application_path, class: 'btn btn-success' %table.table.table-striped @@ -20,4 +20,4 @@ %td= application.redirect_uri %td= application.access_tokens.map(&:resource_owner_id).uniq.count %td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link' - %td= render 'delete_form', application: application + %td= render 'delete_form', application: application \ No newline at end of file -- cgit v1.2.1 From f71f165f994bbdc5b98bf610f8306e7cad7f1740 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 30 Sep 2015 11:16:00 +0200 Subject: Fix CI mailer Signed-off-by: Dmitriy Zaporozhets --- app/mailers/ci/notify.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb index 4462da0d7d2..404842cf213 100644 --- a/app/mailers/ci/notify.rb +++ b/app/mailers/ci/notify.rb @@ -2,7 +2,6 @@ module Ci class Notify < ActionMailer::Base include Ci::Emails::Builds - add_template_helper Ci::ApplicationHelper add_template_helper Ci::GitlabHelper default_url_options[:host] = Gitlab.config.gitlab.host -- cgit v1.2.1 From fe7e97320c83df771e027a4d1576986e58065a97 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 30 Sep 2015 12:31:02 +0200 Subject: Fix bug when removed file was not appearing in merge request diff Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/helpers/diff_helper.rb | 19 +++++++++++++++++++ app/views/projects/diffs/_diffs.html.haml | 7 ++++++- app/views/projects/diffs/_file.html.haml | 22 ++++++++-------------- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a499dda2bb3..492e4b9aebf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.1.0 (unreleased) - Move CI variables page to project settings area - Move CI triggers page to project settings area - Move CI project settings page to CE project settings area + - Fix bug when removed file was not appearing in merge request diff v 8.0.3 - Fix URL shown in Slack notifications diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 6ffa1a7121d..9d718e13b85 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -167,4 +167,23 @@ module DiffHelper content_tag(:span, commit_id, class: 'monospace'), ].join(' ').html_safe end + + def commit_for_diff(diff) + if diff.deleted_file + @merge_request ? @merge_request.commits.last : @commit.parent_id + else + @commit + end + end + + def diff_file_html_data(project, diff_commit, diff_file) + { + blob_diff_path: namespace_project_blob_diff_path(project.namespace, project, + tree_join(diff_commit.id, diff_file.file_path)) + } + end + + def editable_diff?(diff) + !diff.deleted_file && @merge_request && @merge_request.source_project + end end diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 2f24dc7c909..c5acafa2630 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -15,7 +15,12 @@ .files - diff_files.each_with_index do |diff_file, index| - = render 'projects/diffs/file', diff_file: diff_file, i: index, project: project + - diff_commit = commit_for_diff(diff_file.diff) + - blob = project.repository.blob_for_diff(diff_commit, diff_file.diff) + - next unless blob + + = render 'projects/diffs/file', i: index, project: project, + diff_file: diff_file, diff_commit: diff_commit, blob: blob - if @diff_timeout .alert.alert-danger diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 99ee23a1ddc..4617b188150 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,24 +1,18 @@ -- blob = project.repository.blob_for_diff(@commit, diff_file.diff) -- return unless blob -- blob_diff_path = namespace_project_blob_diff_path(project.namespace, project, tree_join(@commit.id, diff_file.file_path)) -.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }} +.diff-file{id: "diff-#{i}", data: diff_file_html_data(project, diff_commit, diff_file)} .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"} - - if diff_file.deleted_file - %span="#{diff_file.old_path} deleted" - - .diff-btn-group - - if @commit.parent_ids.present? - = view_file_btn(@commit.parent_id, diff_file, project) - - elsif diff_file.diff.submodule? + - if diff_file.diff.submodule? %span - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) = submodule_link(submodule_item, @commit.id, project.repository) - else %span - - if diff_file.renamed_file + - if diff_file.deleted_file + = "#{diff_file.old_path} deleted" + - elsif diff_file.renamed_file = "#{diff_file.old_path} renamed to #{diff_file.new_path}" - else = diff_file.new_path + - if diff_file.mode_changed? %span.file-mode= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" @@ -28,12 +22,12 @@ %i.fa.fa-comments   - - if @merge_request && @merge_request.source_project + - if editable_diff?(diff_file) = edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path, after: ' ', from_merge_request_id: @merge_request.id) - = view_file_btn(@commit.id, diff_file, project) + = view_file_btn(diff_commit.id, diff_file, project) .diff-content.diff-wrap-lines -# Skipp all non non-supported blobs -- cgit v1.2.1 From b9ccc79cb5d67e356edce3778b6a17def985ed22 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 23 Sep 2015 12:47:29 +0200 Subject: Delegate ci_project parameters to projects - It delegates name, path, gitlab_url, ssh_url_to_repo - Remove ability to set this parameters using CI API This fixes GitLab project rename, namespace change, repository rename, etc. --- app/controllers/ci/admin/runners_controller.rb | 5 +- app/models/ci/project.rb | 40 ++++++------ app/models/project_services/gitlab_ci_service.rb | 6 +- ...50930095736_add_null_to_name_for_ci_projects.rb | 9 +++ db/schema.rb | 4 +- doc/ci/api/projects.md | 5 -- lib/ci/api/projects.rb | 23 ++----- spec/factories/ci/projects.rb | 12 ---- spec/features/ci/admin/runners_spec.rb | 17 +++--- spec/models/ci/project_spec.rb | 71 +++++++++++++++++++--- spec/requests/ci/api/projects_spec.rb | 12 ++-- spec/services/ci/event_service_spec.rb | 6 +- 12 files changed, 117 insertions(+), 93 deletions(-) create mode 100644 db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb index dc3508b49dd..9a68add9083 100644 --- a/app/controllers/ci/admin/runners_controller.rb +++ b/app/controllers/ci/admin/runners_controller.rb @@ -12,7 +12,10 @@ module Ci def show @builds = @runner.builds.order('id DESC').first(30) @projects = Ci::Project.all - @projects = @projects.search(params[:search]) if params[:search].present? + if params[:search].present? + @gl_projects = ::Project.search(params[:search]) + @projects = @projects.where(gitlab_id: @gl_projects.select(:id)) + end @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? @projects = @projects.page(params[:page]).per(30) end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 77cce261fc8..f0a8fc703b5 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -48,11 +48,12 @@ module Ci accepts_nested_attributes_for :variables, allow_destroy: true + delegate :name_with_namespace, :path_with_namespace, :web_url, :http_url_to_repo, :ssh_url_to_repo, to: :gl_project + # # Validations # - validates_presence_of :name, :timeout, :token, :default_ref, - :path, :ssh_url_to_repo, :gitlab_id + validates_presence_of :timeout, :token, :default_ref, :gitlab_id validates_uniqueness_of :gitlab_id @@ -60,8 +61,6 @@ module Ci presence: true, if: ->(project) { project.always_build.present? } - scope :public_only, ->() { where(public: true) } - before_validation :set_default_values class << self @@ -76,12 +75,9 @@ module Ci 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, + gitlab_id: project.id, + default_ref: project.default_branch || 'master', + email_add_pusher: current_application_settings.add_pusher, email_only_broken_builds: current_application_settings.all_broken_builds, } @@ -105,11 +101,18 @@ module Ci joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") end + end - def search(query) - where("LOWER(#{Ci::Project.table_name}.name) LIKE :query", - query: "%#{query.try(:downcase)}%") - end + def name + name_with_namespace + end + + def path + path_with_namespace + end + + def gitlab_url + web_url end def any_runners? @@ -123,9 +126,6 @@ module Ci def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? self.default_ref ||= 'master' - self.name ||= gl_project.name_with_namespace - self.path ||= gl_project.path_with_namespace - self.ssh_url_to_repo ||= gl_project.ssh_url_to_repo end def tracked_refs @@ -169,7 +169,7 @@ module Ci # using http and basic auth def repo_url_with_auth auth = "gitlab-ci-token:#{token}@" - url = gitlab_url + ".git" + url = http_url_to_repo + ".git" url.sub(/^https?:\/\//) do |prefix| prefix + auth end @@ -201,10 +201,6 @@ module Ci end end - def gitlab_url - File.join(Gitlab.config.gitlab.url, path) - end - def setup_finished? commits.any? end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 23ab206efba..436d4cfed81 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -70,11 +70,7 @@ class GitlabCiService < CiService def fork_registration(new_project, current_user) params = OpenStruct.new({ id: new_project.id, - name_with_namespace: new_project.name_with_namespace, - path_with_namespace: new_project.path_with_namespace, - web_url: new_project.web_url, - default_branch: new_project.default_branch, - ssh_url_to_repo: new_project.ssh_url_to_repo + default_branch: new_project.default_branch }) ci_project = Ci::Project.find_by!(gitlab_id: project.id) diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb new file mode 100644 index 00000000000..a978fcda3ba --- /dev/null +++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb @@ -0,0 +1,9 @@ +class ChangeNameOfCiProjects < ActiveRecord::Migration + def up + change_column_null :ci_projects, :name, true + end + + def down + change_column_null :ci_projects, :name, false + end +end diff --git a/db/schema.rb b/db/schema.rb index 0302da66599..4ce6cee86e5 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: 20150924125436) do +ActiveRecord::Schema.define(version: 20150930095736) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -158,7 +158,7 @@ ActiveRecord::Schema.define(version: 20150924125436) do add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree create_table "ci_projects", force: true do |t| - t.string "name", null: false + t.string "name" t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md index 54584db0938..5585191e826 100644 --- a/doc/ci/api/projects.md +++ b/doc/ci/api/projects.md @@ -100,8 +100,6 @@ Parameters: * `name` (required) - The name of the project * `gitlab_id` (required) - The ID of the project on the Gitlab instance - * `path` (required) - The gitlab project path - * `ssh_url_to_repo` (required) - The gitlab SSH url to the repo * `default_ref` (optional) - The branch to run on (default to `master`) ### Update Project @@ -114,9 +112,6 @@ authenticated user has access to. Parameters: * `name` - The name of the project - * `gitlab_id` - The ID of the project on the Gitlab instance - * `path` - The gitlab project path - * `ssh_url_to_repo` - The gitlab SSH url to the repo * `default_ref` - The branch to run on (default to `master`) ### Remove Project diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index 66bcf65e8c4..d719ad9e8d5 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -75,23 +75,17 @@ module Ci # 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] + required_attributes! [:gitlab_id] 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] + default_ref: params[:default_ref] || 'master' } project = Ci::Project.new(filtered_params) @@ -109,11 +103,7 @@ module Ci # # 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`) + # default_ref - The branch to run against (defaults to `master`) # Example Request: # PUT /projects/:id put ":id" do @@ -121,12 +111,7 @@ module Ci 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] - - # 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 + attrs = attributes_for_keys [:default_ref] if project.update_attributes(attrs) present project, with: Entities::Project diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb index d492fe8209e..111e1a82816 100644 --- a/spec/factories/ci/projects.rb +++ b/spec/factories/ci/projects.rb @@ -29,20 +29,8 @@ 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 - gl_project factory: :empty_project factory :ci_project do diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index b25121f0806..5fb3b93525b 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe "Admin Runners" do before do - skip_ci_admin_auth - login_as :user + login_as :admin end describe "Runners page" do @@ -37,8 +36,8 @@ describe "Admin Runners" do let(:runner) { FactoryGirl.create :ci_runner } before do - FactoryGirl.create(:ci_project, name: "foo") - FactoryGirl.create(:ci_project, name: "bar") + @project1 = FactoryGirl.create(:ci_project) + @project2 = FactoryGirl.create(:ci_project) visit ci_admin_runner_path(runner) end @@ -47,19 +46,19 @@ describe "Admin Runners" do end describe 'projects' do - it { expect(page).to have_content("foo") } - it { expect(page).to have_content("bar") } + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).to have_content(@project2.name_with_namespace) } end describe 'search' do before do search_form = find('#runner-projects-search') - search_form.fill_in 'search', with: 'foo' + search_form.fill_in 'search', with: @project1.gl_project.name search_form.click_button 'Search' end - it { expect(page).to have_content("foo") } - it { expect(page).not_to have_content("bar") } + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).not_to have_content(@project2.name_with_namespace) } end end end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 466c7afaf1e..dec4720a711 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -29,7 +29,8 @@ require 'spec_helper' describe Ci::Project do let(:gl_project) { FactoryGirl.create :empty_project } - subject { FactoryGirl.create :ci_project, gl_project: gl_project } + let(:project) { FactoryGirl.create :ci_project, gl_project: gl_project } + subject { project } it { is_expected.to have_many(:runner_projects) } it { is_expected.to have_many(:runners) } @@ -40,6 +41,7 @@ describe Ci::Project do it { is_expected.to have_many(:services) } it { is_expected.to validate_presence_of :timeout } + it { is_expected.to validate_presence_of :gitlab_id } describe 'before_validation' do it 'should set an random token if none provided' do @@ -53,6 +55,66 @@ describe Ci::Project do end end + describe :name_with_namespace do + subject { project.name_with_namespace } + + it { is_expected.to eq(project.name) } + it { is_expected.to eq(gl_project.name_with_namespace) } + end + + describe :path_with_namespace do + subject { project.path_with_namespace } + + it { is_expected.to eq(project.path) } + it { is_expected.to eq(gl_project.path_with_namespace) } + end + + describe :path_with_namespace do + subject { project.web_url } + + it { is_expected.to eq(gl_project.web_url) } + end + + describe :web_url do + subject { project.web_url } + + it { is_expected.to eq(project.gitlab_url) } + it { is_expected.to eq(gl_project.web_url) } + end + + describe :http_url_to_repo do + subject { project.http_url_to_repo } + + it { is_expected.to eq(gl_project.http_url_to_repo) } + end + + describe :ssh_url_to_repo do + subject { project.ssh_url_to_repo } + + it { is_expected.to eq(gl_project.ssh_url_to_repo) } + end + + describe :commits do + subject { project.commits } + + before do + FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project + end + + it { is_expected.to eq(gl_project.ci_commits) } + end + + describe :builds do + subject { project.builds } + + before do + commit = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project + FactoryGirl.create :ci_build, commit: commit + end + + it { is_expected.to eq(gl_project.ci_builds) } + end + describe "ordered_by_last_commit_date" do it "returns ordered projects" do newest_project = FactoryGirl.create :empty_project @@ -174,13 +236,6 @@ describe Ci::Project do it { is_expected.to include(project.gitlab_url[7..-1]) } end - describe :search do - let!(:project) { FactoryGirl.create(:ci_project, name: "foo") } - - it { expect(Ci::Project.search('fo')).to include(project) } - it { expect(Ci::Project.search('bar')).to be_empty } - end - describe :any_runners do it "there are no runners available" do project = FactoryGirl.create(:ci_project) diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 409f47fa448..53f7f91cc1f 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -134,7 +134,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) { { default_ref: "develop" } } before do options.merge!(project_info) @@ -144,7 +144,7 @@ describe Ci::API::API 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]) + expect(json_response["default_ref"]).to eq(project_info[:default_ref]) end it "fails to update a non-existing project" do @@ -181,12 +181,10 @@ describe Ci::API::API do end describe "POST /projects" do + let(:gl_project) { FactoryGirl.create :empty_project } let(:project_info) do { - name: "My project", - gitlab_id: 1, - path: "testing/testing", - ssh_url_to_repo: "ssh://example.com/testing/testing.git" + gitlab_id: gl_project.id } end @@ -200,7 +198,7 @@ describe Ci::API::API do it "should create a project with valid data" do post ci_api("/projects"), options expect(response.status).to eq(201) - expect(json_response['name']).to eq(project_info[:name]) + expect(json_response['name']).to eq(gl_project.name_with_namespace) end end diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index 9b330a90ae2..1264e17ff5e 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 :ci_project, name: "GitLab / gitlab-shell" } + let(:project) { FactoryGirl.create :ci_project } let(:user) { double(username: "root", id: 1) } before do @@ -12,7 +12,7 @@ describe Ci::EventService do it "creates event" do Ci::EventService.new.remove_project(user, project) - expect(Ci::Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been removed by root") + expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been removed by root") end end @@ -20,7 +20,7 @@ describe Ci::EventService do it "creates event" do Ci::EventService.new.create_project(user, project) - expect(Ci::Event.admin.last.description).to eq("Project \"GitLab / gitlab-shell\" has been created by root") + expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been created by root") end end -- cgit v1.2.1 From ad765353f6f62028c0b5331f90210480127d1551 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 30 Sep 2015 12:51:03 +0200 Subject: Fix sometimes failing tests --- spec/features/ci/admin/runners_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index 5fb3b93525b..b83744f53a8 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -19,16 +19,16 @@ describe "Admin Runners" do describe 'search' do before do - FactoryGirl.create :ci_runner, description: 'foo' - FactoryGirl.create :ci_runner, description: 'bar' + FactoryGirl.create :ci_runner, description: 'runner-foo' + FactoryGirl.create :ci_runner, description: 'runner-bar' search_form = find('#runners-search') - search_form.fill_in 'search', with: 'foo' + search_form.fill_in 'search', with: 'runner-foo' search_form.click_button 'Search' end - it { expect(page).to have_content("foo") } - it { expect(page).not_to have_content("bar") } + it { expect(page).to have_content("runner-foo") } + it { expect(page).not_to have_content("runner-bar") } end end -- cgit v1.2.1 From 46503b789def6a0bb82a7bf559e8488a056e3f1f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 30 Sep 2015 15:29:52 +0200 Subject: Fix invalid tests Signed-off-by: Dmitriy Zaporozhets --- features/project/merge_requests.feature | 36 ++++++++++++++++---------------- features/steps/project/merge_requests.rb | 36 ++++++++++++++++---------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 947f668e432..83055188bac 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -115,40 +115,40 @@ Feature: Project Merge Requests Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" And I click on the Changes tab - And I leave a comment like "Line is wrong" on line 39 of the second file - And I click link "Hide inline discussion" of the second file - Then I should not see a comment like "Line is wrong here" in the second file + And I leave a comment like "Line is wrong" on line 39 of the third file + And I click link "Hide inline discussion" of the third file + Then I should not see a comment like "Line is wrong here" in the third file @javascript Scenario: I show comments on a merge request diff with comments in a single file Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" And I click on the Changes tab - And I leave a comment like "Line is wrong" on line 39 of the second file - Then I should see a comment like "Line is wrong" in the second file + And I leave a comment like "Line is wrong" on line 39 of the third file + Then I should see a comment like "Line is wrong" in the third file @javascript Scenario: I hide comments on a merge request diff with comments in multiple files Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" And I click on the Changes tab - And I leave a comment like "Line is correct" on line 12 of the first file - And I leave a comment like "Line is wrong" on line 39 of the second file - And I click link "Hide inline discussion" of the second file - Then I should not see a comment like "Line is wrong here" in the second file - And I should still see a comment like "Line is correct" in the first file + And I leave a comment like "Line is correct" on line 12 of the second file + And I leave a comment like "Line is wrong" on line 39 of the third file + And I click link "Hide inline discussion" of the third file + Then I should not see a comment like "Line is wrong here" in the third file + And I should still see a comment like "Line is correct" in the second file @javascript Scenario: I show comments on a merge request diff with comments in multiple files Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" And I click on the Changes tab - And I leave a comment like "Line is correct" on line 12 of the first file - And I leave a comment like "Line is wrong" on line 39 of the second file - And I click link "Hide inline discussion" of the second file - And I click link "Show inline discussion" of the second file - Then I should see a comment like "Line is wrong" in the second file - And I should still see a comment like "Line is correct" in the first file + And I leave a comment like "Line is correct" on line 12 of the second file + And I leave a comment like "Line is wrong" on line 39 of the third file + And I click link "Hide inline discussion" of the third file + And I click link "Show inline discussion" of the third file + Then I should see a comment like "Line is wrong" in the third file + And I should still see a comment like "Line is correct" in the second file @javascript Scenario: I unfold diff @@ -163,8 +163,8 @@ Feature: Project Merge Requests Given project "Shop" have "Bug NS-05" open merge request with diffs inside And I visit merge request page "Bug NS-05" And I click on the Changes tab - And I leave a comment like "Line is correct" on line 12 of the first file - And I leave a comment like "Line is wrong" on line 39 of the second file + And I leave a comment like "Line is correct" on line 12 of the second file + And I leave a comment like "Line is wrong" on line 39 of the third file And I click Side-by-side Diff tab Then I should see comments on the side-by-side diff page diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index c92998631ff..875bf6c4676 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -224,43 +224,43 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end - step 'I click link "Hide inline discussion" of the second file' do - page.within '.files [id^=diff]:nth-child(2)' do + step 'I click link "Hide inline discussion" of the third file' do + page.within '.files [id^=diff]:nth-child(3)' do find('.js-toggle-diff-comments').trigger('click') end end - step 'I click link "Show inline discussion" of the second file' do - page.within '.files [id^=diff]:nth-child(2)' do + step 'I click link "Show inline discussion" of the third file' do + page.within '.files [id^=diff]:nth-child(3)' do find('.js-toggle-diff-comments').trigger('click') end end - step 'I should not see a comment like "Line is wrong" in the second file' do - page.within '.files [id^=diff]:nth-child(2)' do + step 'I should not see a comment like "Line is wrong" in the third file' do + page.within '.files [id^=diff]:nth-child(3)' do expect(page).not_to have_visible_content "Line is wrong" end end - step 'I should see a comment like "Line is wrong" in the second file' do - page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do + step 'I should see a comment like "Line is wrong" in the third file' do + page.within '.files [id^=diff]:nth-child(3) .note-body > .note-text' do expect(page).to have_visible_content "Line is wrong" end end - step 'I should not see a comment like "Line is wrong here" in the second file' do - page.within '.files [id^=diff]:nth-child(2)' do + step 'I should not see a comment like "Line is wrong here" in the third file' do + page.within '.files [id^=diff]:nth-child(3)' do expect(page).not_to have_visible_content "Line is wrong here" end end - step 'I should see a comment like "Line is wrong here" in the second file' do - page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do + step 'I should see a comment like "Line is wrong here" in the third file' do + page.within '.files [id^=diff]:nth-child(3) .note-body > .note-text' do expect(page).to have_visible_content "Line is wrong here" end end - step 'I leave a comment like "Line is correct" on line 12 of the first file' do + step 'I leave a comment like "Line is correct" on line 12 of the second file' do init_diff_note_first_file page.within(".js-discussion-note-form") do @@ -268,12 +268,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps click_button "Add Comment" end - page.within ".files [id^=diff]:nth-child(1) .note-body > .note-text" do + page.within ".files [id^=diff]:nth-child(2) .note-body > .note-text" do expect(page).to have_content "Line is correct" end end - step 'I leave a comment like "Line is wrong" on line 39 of the second file' do + step 'I leave a comment like "Line is wrong" on line 39 of the third file' do init_diff_note_second_file page.within(".js-discussion-note-form") do @@ -282,8 +282,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end - step 'I should still see a comment like "Line is correct" in the first file' do - page.within '.files [id^=diff]:nth-child(1) .note-body > .note-text' do + step 'I should still see a comment like "Line is correct" in the second file' do + page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do expect(page).to have_visible_content "Line is correct" end end @@ -303,7 +303,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see comments on the side-by-side diff page' do - page.within '.files [id^=diff]:nth-child(1) .parallel .note-body > .note-text' do + page.within '.files [id^=diff]:nth-child(2) .parallel .note-body > .note-text' do expect(page).to have_visible_content "Line is correct" end end -- cgit v1.2.1 From f4eaae57679287b3e92a943b50a66ddb641350d9 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Wed, 30 Sep 2015 16:30:13 +0300 Subject: Add a missing RAILS_ENV=production to crontab clearing All other commands in the guide set this env var, but it's missing here and this causes whenever not to clear the crontab file of gitab_ci properly. --- doc/migrate_ci_to_ce/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md index 2725bf343ee..56bf7a14182 100644 --- a/doc/migrate_ci_to_ce/README.md +++ b/doc/migrate_ci_to_ce/README.md @@ -143,7 +143,7 @@ sudo gitlab-ctl stop ci-sidekiq # Source sudo service gitlab_ci stop cd /home/gitlab_ci/gitlab-ci -sudo -u gitlab_ci -H bundle exec whenever --clear-crontab +sudo -u gitlab_ci -H bundle exec whenever --clear-crontab RAILS_ENV=production ``` ### II. Moving data -- cgit v1.2.1 From 63a6259328b602b0392f3b2eb1cd2adf4d275bc5 Mon Sep 17 00:00:00 2001 From: Diogo Resende Date: Wed, 30 Sep 2015 17:26:55 +0000 Subject: Adds missing plural form of applications of previous commit --- app/views/admin/applications/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index 6ac45a3db1a..db8d61ec6dc 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -2,7 +2,7 @@ %h3.page-title System OAuth applications %p.light - System OAuth application don't belong to any user and can only be managed by admins + System OAuth applications don't belong to any user and can only be managed by admins %hr %p= link_to 'New Application', new_admin_application_path, class: 'btn btn-success' %table.table.table-striped -- cgit v1.2.1 From bbb845b20b7b6cced50e28d532d13ec979a0db66 Mon Sep 17 00:00:00 2001 From: Diogo Resende Date: Wed, 30 Sep 2015 17:28:06 +0000 Subject: Adds newline at end of file --- app/views/admin/applications/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index db8d61ec6dc..f8cd98f0ec4 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -20,4 +20,4 @@ %td= application.redirect_uri %td= application.access_tokens.map(&:resource_owner_id).uniq.count %td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link' - %td= render 'delete_form', application: application \ No newline at end of file + %td= render 'delete_form', application: application -- cgit v1.2.1 From 5a401523ebb756faf28b74301fae3901ef929537 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 30 Sep 2015 19:43:29 +0200 Subject: Fix migration --- db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb index a978fcda3ba..8d47dac6441 100644 --- a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb +++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb @@ -1,4 +1,4 @@ -class ChangeNameOfCiProjects < ActiveRecord::Migration +class AddNullToNameForCiProjects < ActiveRecord::Migration def up change_column_null :ci_projects, :name, true end -- cgit v1.2.1