From 893ad50b9cce75842a6593411003efaec63aacbd Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 19 Oct 2016 22:43:05 +1100 Subject: [PATCH 001/129] Updated schema.rb --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index 5ce855fe08f..21fd8494ccc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -832,7 +832,7 @@ ActiveRecord::Schema.define(version: 20161017095000) do t.integer "builds_access_level" t.datetime "created_at" t.datetime "updated_at" - t.integer "repository_access_level", default: 20, null: false + t.integer "repository_access_level", default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree From ccdaa8abc8ef6033148b6a0151d5d13e4df8a9d4 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Thu, 20 Oct 2016 15:44:10 +1100 Subject: [PATCH 002/129] Add hover to trash icon in notes --- app/views/projects/notes/_note.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 73fe6a715fa..ab719e38904 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -57,7 +57,7 @@ = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do = icon('pencil', class: 'link-highlight') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do - = icon('trash-o') + = icon('trash-o', class: 'danger-highlight') .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text.md = preserve do From 5b2200777231367cb402898af4f31cef2e63b1a1 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 14 Oct 2016 10:57:10 -0500 Subject: [PATCH 003/129] Smaller min-width for MR pipeline table --- app/assets/stylesheets/pages/pipelines.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 247339648fa..dfaf1ab732d 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -643,6 +643,10 @@ &.pipelines { + .ci-table { + min-width: 900px; + } + .content-list.pipelines { overflow: auto; } From 16fffa9061f72971f37ef13ed0656712e20084f4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 18 Oct 2016 17:03:36 +0100 Subject: [PATCH 004/129] Fixed height of issue board blank state --- app/assets/stylesheets/pages/boards.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 6e81c12aa55..d8fabbdcebe 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -1,4 +1,3 @@ -lex [v-cloak] { display: none; } @@ -132,7 +131,7 @@ lex } .board-blank-state { - height: 100%; + height: calc(100% - 49px); padding: $gl-padding; background-color: #fff; } From f488b9f765346d9f7e918c31cb5bd07717b5989a Mon Sep 17 00:00:00 2001 From: Callum Dryden Date: Thu, 6 Oct 2016 15:19:27 +0000 Subject: [PATCH 005/129] Differentiate the expire from leave event At the moment we cannot see weather a user left a project due to their membership expiring of if they themselves opted to leave the project. This adds a new event type that allows us to make this differentiation. Note that is not really feasable to go back and reliably fix up the previous events. As a result the events for previous expire removals will remain the same however events of this nature going forward will be correctly represented. --- CHANGELOG.md | 1 + app/models/concerns/expirable.rb | 6 ++- app/models/event.rb | 9 +++- app/models/members/project_member.rb | 6 ++- app/services/event_create_service.rb | 4 ++ features/dashboard/dashboard.feature | 13 ------ features/steps/dashboard/dashboard.rb | 27 ------------ lib/event_filter.rb | 11 ++++- .../project_member_activity_index_spec.rb | 41 +++++++++++++++++++ spec/models/concerns/expirable_spec.rb | 31 ++++++++++++++ spec/models/event_spec.rb | 27 ++++++++++++ spec/models/members/project_member_spec.rb | 11 +++++ spec/services/event_create_service_spec.rb | 19 +++++++++ 13 files changed, 161 insertions(+), 45 deletions(-) create mode 100644 spec/features/dashboard/project_member_activity_index_spec.rb create mode 100644 spec/models/concerns/expirable_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..8cb848c3957 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) + - Adds user project membership expired event to clarify why user was removed (Callum Dryden) ## 8.13.0 (2016-10-22) diff --git a/app/models/concerns/expirable.rb b/app/models/concerns/expirable.rb index be93435453b..b66ba08dc59 100644 --- a/app/models/concerns/expirable.rb +++ b/app/models/concerns/expirable.rb @@ -5,11 +5,15 @@ module Expirable scope :expired, -> { where('expires_at <= ?', Time.current) } end + def expired? + expires? && expires_at <= Time.current + end + def expires? expires_at.present? end def expires_soon? - expires_at < 7.days.from_now + expires? && expires_at < 7.days.from_now end end diff --git a/app/models/event.rb b/app/models/event.rb index 0764cb8cabd..3993b35f96d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -12,6 +12,7 @@ class Event < ActiveRecord::Base JOINED = 8 # User joined project LEFT = 9 # User left project DESTROYED = 10 + EXPIRED = 11 # User left project due to expiry RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour @@ -115,6 +116,10 @@ class Event < ActiveRecord::Base action == LEFT end + def expired? + action == EXPIRED + end + def destroyed? action == DESTROYED end @@ -124,7 +129,7 @@ class Event < ActiveRecord::Base end def membership_changed? - joined? || left? + joined? || left? || expired? end def created_project? @@ -184,6 +189,8 @@ class Event < ActiveRecord::Base 'joined' elsif left? 'left' + elsif expired? + 'removed due to membership expiration from' elsif destroyed? 'destroyed' elsif commented? diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 125f26369d7..e4880973117 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -121,7 +121,11 @@ class ProjectMember < Member end def post_destroy_hook - event_service.leave_project(self.project, self.user) + if expired? + event_service.expired_leave_project(self.project, self.user) + else + event_service.leave_project(self.project, self.user) + end super end diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 07fc77001a5..e24cc66e0fe 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -62,6 +62,10 @@ class EventCreateService create_event(project, current_user, Event::LEFT) end + def expired_leave_project(project, current_user) + create_event(project, current_user, Event::EXPIRED) + end + def create_project(project, current_user) create_event(project, current_user, Event::CREATED) end diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index 1f4c9020731..b1d5e4a7acb 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -31,19 +31,6 @@ Feature: Dashboard And I click "Create Merge Request" link Then I see prefilled new Merge Request page - @javascript - Scenario: I should see User joined Project event - Given user with name "John Doe" joined project "Shop" - When I visit dashboard activity page - Then I should see "John Doe joined project Shop" event - - @javascript - Scenario: I should see User left Project event - Given user with name "John Doe" joined project "Shop" - And user with name "John Doe" left project "Shop" - When I visit dashboard activity page - Then I should see "John Doe left project Shop" event - @javascript Scenario: Sorting Issues Given I visit dashboard issues page diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index a7d61bc28e0..b2bec369e0f 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -33,33 +33,6 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps expect(find("input#merge_request_target_branch").value).to eq "master" end - step 'user with name "John Doe" joined project "Shop"' do - user = create(:user, { name: "John Doe" }) - project.team << [user, :master] - Event.create( - project: project, - author_id: user.id, - action: Event::JOINED - ) - end - - step 'I should see "John Doe joined project Shop" event' do - expect(page).to have_content "John Doe joined project #{project.name_with_namespace}" - end - - step 'user with name "John Doe" left project "Shop"' do - user = User.find_by(name: "John Doe") - Event.create( - project: project, - author_id: user.id, - action: Event::LEFT - ) - end - - step 'I should see "John Doe left project Shop" event' do - expect(page).to have_content "John Doe left project #{project.name_with_namespace}" - end - step 'I have group with projects' do @group = create(:group) @project = create(:project, namespace: @group) diff --git a/lib/event_filter.rb b/lib/event_filter.rb index 96e70e37e8f..21f6a9a762b 100644 --- a/lib/event_filter.rb +++ b/lib/event_filter.rb @@ -45,9 +45,16 @@ class EventFilter when EventFilter.comments actions = [Event::COMMENTED] when EventFilter.team - actions = [Event::JOINED, Event::LEFT] + actions = [Event::JOINED, Event::LEFT, Event::EXPIRED] when EventFilter.all - actions = [Event::PUSHED, Event::MERGED, Event::COMMENTED, Event::JOINED, Event::LEFT] + actions = [ + Event::PUSHED, + Event::MERGED, + Event::COMMENTED, + Event::JOINED, + Event::LEFT, + Event::EXPIRED + ] end events.where(action: actions) diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb new file mode 100644 index 00000000000..ba77093a6d4 --- /dev/null +++ b/spec/features/dashboard/project_member_activity_index_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +feature 'Project member activity', feature: true, js: true do + include WaitForAjax + + let(:user) { create(:user) } + let(:project) { create(:empty_project, :public, name: 'x', namespace: user.namespace) } + + before do + project.team << [user, :master] + end + + def visit_activities_and_wait_with_event(event_type) + Event.create(project: project, author_id: user.id, action: event_type) + visit activity_namespace_project_path(project.namespace.path, project.path) + wait_for_ajax + end + + subject { page.find(".event-title").text } + + context 'when a user joins the project' do + before { visit_activities_and_wait_with_event(Event::JOINED) } + + it { is_expected.to eq("#{user.name} joined project") } + end + + context 'when a user leaves the project' do + before { visit_activities_and_wait_with_event(Event::LEFT) } + + it { is_expected.to eq("#{user.name} left project") } + end + + context 'when a users membership expires for the project' do + before { visit_activities_and_wait_with_event(Event::EXPIRED) } + + it "presents the correct message" do + message = "#{user.name} removed due to membership expiration from project" + is_expected.to eq(message) + end + end +end diff --git a/spec/models/concerns/expirable_spec.rb b/spec/models/concerns/expirable_spec.rb new file mode 100644 index 00000000000..f7b436f32e6 --- /dev/null +++ b/spec/models/concerns/expirable_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Expirable do + describe 'ProjectMember' do + let(:no_expire) { create(:project_member) } + let(:expire_later) { create(:project_member, expires_at: Time.current + 6.days) } + let(:expired) { create(:project_member, expires_at: Time.current - 6.days) } + + describe '.expired' do + it { expect(ProjectMember.expired).to match_array([expired]) } + end + + describe '#expired?' do + it { expect(no_expire.expired?).to eq(false) } + it { expect(expire_later.expired?).to eq(false) } + it { expect(expired.expired?).to eq(true) } + end + + describe '#expires?' do + it { expect(no_expire.expires?).to eq(false) } + it { expect(expire_later.expires?).to eq(true) } + it { expect(expired.expires?).to eq(true) } + end + + describe '#expires_soon?' do + it { expect(no_expire.expires_soon?).to eq(false) } + it { expect(expire_later.expires_soon?).to eq(true) } + it { expect(expired.expires_soon?).to eq(true) } + end + end +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 733b79079ed..aca49be2942 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -40,6 +40,33 @@ describe Event, models: true do end end + describe '#membership_changed?' do + context "created" do + subject { build(:event, action: Event::CREATED).membership_changed? } + it { is_expected.to be_falsey } + end + + context "updated" do + subject { build(:event, action: Event::UPDATED).membership_changed? } + it { is_expected.to be_falsey } + end + + context "expired" do + subject { build(:event, action: Event::EXPIRED).membership_changed? } + it { is_expected.to be_truthy } + end + + context "left" do + subject { build(:event, action: Event::LEFT).membership_changed? } + it { is_expected.to be_truthy } + end + + context "joined" do + subject { build(:event, action: Event::JOINED).membership_changed? } + it { is_expected.to be_truthy } + end + end + describe '#note?' do subject { Event.new(project: target.project, target: target) } diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index d85a1c1e3b2..b2fe96e2e02 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -54,6 +54,17 @@ describe ProjectMember, models: true do master_todos end + it "creates an expired event when left due to expiry" do + expired = create(:project_member, project: project, expires_at: Time.now - 6.days) + expired.destroy + expect(Event.first.action).to eq(Event::EXPIRED) + end + + it "creates a left event when left due to leave" do + master.destroy + expect(Event.first.action).to eq(Event::LEFT) + end + it "destroys itself and delete associated todos" do expect(owner.user.todos.size).to eq(2) expect(master.user.todos.size).to eq(3) diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index 16a9956fe7f..b7dc99ed887 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -110,4 +110,23 @@ describe EventCreateService, services: true do end end end + + describe 'Project' do + let(:user) { create :user } + let(:project) { create(:empty_project) } + + describe '#join_project' do + subject { service.join_project(project, user) } + + it { is_expected.to be_truthy } + it { expect { subject }.to change { Event.count }.from(0).to(1) } + end + + describe '#expired_leave_project' do + subject { service.expired_leave_project(project, user) } + + it { is_expected.to be_truthy } + it { expect { subject }.to change { Event.count }.from(0).to(1) } + end + end end From 7077162af28b589c00bed5603cc4f18653cc5678 Mon Sep 17 00:00:00 2001 From: Paco Guzman Date: Wed, 19 Oct 2016 21:58:12 +0200 Subject: [PATCH 006/129] Simpler arguments passed to named_route on toggle_award_url helper method --- CHANGELOG.md | 1 + app/helpers/award_emoji_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb848c3957..dfde2cc81e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) + - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) diff --git a/app/helpers/award_emoji_helper.rb b/app/helpers/award_emoji_helper.rb index 592ffe7b89f..167b09e678f 100644 --- a/app/helpers/award_emoji_helper.rb +++ b/app/helpers/award_emoji_helper.rb @@ -3,8 +3,8 @@ module AwardEmojiHelper return url_for([:toggle_award_emoji, awardable]) unless @project if awardable.is_a?(Note) - # We render a list of notes very frequently and calling the specific method is a lot faster than the generic one (6.5x) - toggle_award_emoji_namespace_project_note_url(namespace_id: @project.namespace, project_id: @project, id: awardable.id) + # We render a list of notes very frequently and calling the specific method is a lot faster than the generic one (4.5x) + toggle_award_emoji_namespace_project_note_url(@project.namespace, @project, awardable.id) else url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) end From 6278ee5ab0c4c48d3c8a12ade85c263fefb935d2 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Thu, 20 Oct 2016 08:57:23 +0900 Subject: [PATCH 007/129] Fix a broken table in Project API doc --- CHANGELOG.md | 1 + doc/api/projects.md | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfde2cc81e5..399a7f65267 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,6 +135,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Delete dynamic environments - Fix buggy iOS tooltip layering behavior. - Make guests unable to view MRs on private projects + - Fix broken Project API docs (Takuya Noguchi) ## 8.12.7 diff --git a/doc/api/projects.md b/doc/api/projects.md index b7791b4748a..77d6bd6b5c2 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1333,8 +1333,8 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `query` (required) - A string contained in the project name -| `per_page` (optional) - number of projects to return per page -| `page` (optional) - the page to retrieve -| `order_by` (optional) - Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields +| `query` | string | yes | A string contained in the project name | +| `per_page` | integer | no | number of projects to return per page | +| `page` | integer | no | the page to retrieve | +| `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields | | `sort` | string | no | Return requests sorted in `asc` or `desc` order | From 6fd561d282098e71a523cf1b73396b440ef13e63 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Thu, 20 Oct 2016 08:58:35 +0900 Subject: [PATCH 008/129] Remove pagination description from individual doc --- doc/api/projects.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 77d6bd6b5c2..b69db90e70d 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1334,7 +1334,5 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `query` | string | yes | A string contained in the project name | -| `per_page` | integer | no | number of projects to return per page | -| `page` | integer | no | the page to retrieve | | `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields | | `sort` | string | no | Return requests sorted in `asc` or `desc` order | From 7a184617199fa01c040857ed3a5d2a071944760a Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 19 Oct 2016 19:43:04 +0300 Subject: [PATCH 009/129] Refactoring find_commits functionality --- app/controllers/projects/commits_controller.rb | 2 +- app/models/repository.rb | 9 ++++++--- lib/gitlab/project_search_results.rb | 6 +----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index a52c614b259..c2e7bf1ffec 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -13,7 +13,7 @@ class Projects::CommitsController < Projects::ApplicationController @commits = if search.present? - @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact + @repository.find_commits_by_message(search, @ref, @path, @limit, @offset) else @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end diff --git a/app/models/repository.rb b/app/models/repository.rb index 72e473871fa..1b7f20a2134 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -109,6 +109,10 @@ class Repository end def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) + unless exists? && has_visible_content? && query.present? + return [] + end + ref ||= root_ref args = %W( @@ -117,9 +121,8 @@ class Repository ) args = args.concat(%W(-- #{path})) if path.present? - git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) - commits = git_log_results.map { |c| commit(c) } - commits + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines + git_log_results.map { |c| commit(c.chomp) }.compact end def find_branch(name, fresh_repo: true) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 5b9cfaeb2f8..24733435a5a 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -73,11 +73,7 @@ module Gitlab end def commits - if project.empty_repo? || query.blank? - [] - else - project.repository.find_commits_by_message(query).compact - end + project.repository.find_commits_by_message(query) end def project_ids_relation From 17ae807ae3e03fcc67f557c4ad7d5c6e0502065e Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 14 Oct 2016 19:38:41 -0300 Subject: [PATCH 010/129] Create project feature when project is created --- CHANGELOG.md | 1 + app/models/project.rb | 7 +---- ...5_generate_project_feature_for_projects.rb | 28 +++++++++++++++++++ db/schema.rb | 2 +- spec/models/project_spec.rb | 14 ++++++++-- 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20161019213545_generate_project_feature_for_projects.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 399a7f65267..670404e4fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) - Cancelled pipelines could be retried. !6927 - Updating verbiage on git basics to be more intuitive + - Fix project_feature record not generated on project creation - Clarify documentation for Runners API (Gennady Trafimenkov) - The instrumentation for Banzai::Renderer has been restored - Change user & group landing page routing from /u/:username to /:username diff --git a/app/models/project.rb b/app/models/project.rb index 653c38322c5..6685baab699 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -32,8 +32,8 @@ class Project < ActiveRecord::Base default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } after_create :ensure_dir_exist + after_create :create_project_feature, unless: :project_feature after_save :ensure_dir_exist, if: :namespace_id_changed? - after_initialize :setup_project_feature # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -1310,11 +1310,6 @@ class Project < ActiveRecord::Base "projects/#{id}/pushes_since_gc" end - # Prevents the creation of project_feature record for every project - def setup_project_feature - build_project_feature unless project_feature - end - def default_branch_protected? current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE diff --git a/db/migrate/20161019213545_generate_project_feature_for_projects.rb b/db/migrate/20161019213545_generate_project_feature_for_projects.rb new file mode 100644 index 00000000000..4554e14b0df --- /dev/null +++ b/db/migrate/20161019213545_generate_project_feature_for_projects.rb @@ -0,0 +1,28 @@ +class GenerateProjectFeatureForProjects < ActiveRecord::Migration + DOWNTIME = true + + DOWNTIME_REASON = <<-HEREDOC + Application was eager loading project_feature for all projects generating an extra query + everytime a project was fetched. We removed that behavior to avoid the extra query, this migration + makes sure all projects have a project_feature record associated. + HEREDOC + + def up + # Generate enabled values for each project feature 20, 20, 20, 20, 20 + # All features are enabled by default + enabled_values = [ProjectFeature::ENABLED] * 5 + + execute <<-EOF.strip_heredoc + INSERT INTO project_features + (project_id, merge_requests_access_level, builds_access_level, + issues_access_level, snippets_access_level, wiki_access_level) + (SELECT projects.id, #{enabled_values.join(',')} FROM projects LEFT OUTER JOIN project_features + ON project_features.project_id = projects.id + WHERE project_features.id IS NULL) + EOF + end + + def down + "Not needed" + end +end diff --git a/db/schema.rb b/db/schema.rb index f777ed39378..f5c01511195 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: 20161018024550) do +ActiveRecord::Schema.define(version: 20161019213545) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e6d98e25d0b..f4dda1ee558 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -67,6 +67,14 @@ describe Project, models: true do it { is_expected.to have_many(:notification_settings).dependent(:destroy) } it { is_expected.to have_many(:forks).through(:forked_project_links) } + context 'after create' do + it "creates project feature" do + project = FactoryGirl.build(:project) + + expect { project.save }.to change{ project.project_feature.present? }.from(false).to(true) + end + end + describe '#members & #requesters' do let(:project) { create(:project, :public) } let(:requester) { create(:user) } @@ -531,9 +539,9 @@ describe Project, models: true do end describe '#has_wiki?' do - let(:no_wiki_project) { build(:project, wiki_enabled: false, has_external_wiki: false) } - let(:wiki_enabled_project) { build(:project) } - let(:external_wiki_project) { build(:project, has_external_wiki: true) } + let(:no_wiki_project) { create(:project, wiki_access_level: ProjectFeature::DISABLED, has_external_wiki: false) } + let(:wiki_enabled_project) { create(:project) } + let(:external_wiki_project) { create(:project, has_external_wiki: true) } it 'returns true if project is wiki enabled or has external wiki' do expect(wiki_enabled_project).to have_wiki From 89408aed1f32d760a879c9e26563b956f498793d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 20 Oct 2016 12:10:27 +0200 Subject: [PATCH 011/129] Make label API spec independent of order --- spec/requests/api/labels_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 1da9988978b..867bc615b97 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -22,8 +22,7 @@ describe API::API, api: true do expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) - expect(json_response.first['name']).to eq(group_label.name) - expect(json_response.second['name']).to eq(label1.name) + expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, label1.name]) end end From ae0a9336f106b14a7abc39e5a32fe8491dd9b544 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 10:27:30 +0100 Subject: [PATCH 012/129] Removed code from project members controller This code was meant to be added to another branch as an expirement, but instead was commited to wrong branch --- .../projects/project_members_controller.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 37a86ed0523..2a07d154853 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -32,21 +32,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController current_user: current_user ) - if params[:group_ids].present? - group_ids = params[:group_ids].split(',') - groups = Group.where(id: group_ids) - - groups.each do |group| - next unless can?(current_user, :read_group, group) - - project.project_group_links.create( - group: group, - group_access: params[:access_level], - expires_at: params[:expires_at] - ) - end - end - redirect_to namespace_project_project_members_path(@project.namespace, @project) end From 583b79a46875b14405061f3036cf041c44241c62 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 20 Oct 2016 12:59:39 +0200 Subject: [PATCH 013/129] Restrict ProjectCacheWorker jobs to one per 15 min This ensures ProjectCacheWorker jobs for a given project are performed at most once per 15 minutes. This should reduce disk load a bit in cases where there are multiple pushes happening (which should schedule multiple ProjectCacheWorker jobs). --- CHANGELOG.md | 3 +- app/workers/project_cache_worker.rb | 27 ++++++++++++++++ spec/workers/project_cache_worker_spec.rb | 38 +++++++++++++++++------ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 670404e4fce..98cffab8c03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - - Simpler arguments passed to named_route on toggle_award_url helper method + - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) @@ -36,6 +36,7 @@ Please view this file on the master branch, on stable branches it's out of date. - AbstractReferenceFilter caches project_refs on RequestStore when active - Replaced the check sign to arrow in the show build view. !6501 - Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar) + - ProjectCacheWorker updates caches at most once per 15 minutes per project - Fix Error 500 when viewing old merge requests with bad diff data - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar) - Speed-up group milestones show page diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index ccefd0f71a0..0d524e88dc3 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -1,9 +1,30 @@ +# Worker for updating any project specific caches. +# +# This worker runs at most once every 15 minutes per project. This is to ensure +# that multiple instances of jobs for this worker don't hammer the underlying +# storage engine as much. class ProjectCacheWorker include Sidekiq::Worker sidekiq_options queue: :default + LEASE_TIMEOUT = 15.minutes.to_i + def perform(project_id) + if try_obtain_lease_for(project_id) + Rails.logger. + info("Obtained ProjectCacheWorker lease for project #{project_id}") + else + Rails.logger. + info("Could not obtain ProjectCacheWorker lease for project #{project_id}") + + return + end + + update_caches(project_id) + end + + def update_caches(project_id) project = Project.find(project_id) return unless project.repository.exists? @@ -15,4 +36,10 @@ class ProjectCacheWorker project.repository.build_cache end end + + def try_obtain_lease_for(project_id) + Gitlab::ExclusiveLease. + new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT). + try_obtain + end end diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index 5785a6a06ff..f5b60b90d11 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -6,21 +6,39 @@ describe ProjectCacheWorker do subject { described_class.new } describe '#perform' do - it 'updates project cache data' do - expect_any_instance_of(Repository).to receive(:size) - expect_any_instance_of(Repository).to receive(:commit_count) + context 'when an exclusive lease can be obtained' do + before do + allow(subject).to receive(:try_obtain_lease_for).with(project.id). + and_return(true) + end - expect_any_instance_of(Project).to receive(:update_repository_size) - expect_any_instance_of(Project).to receive(:update_commit_count) + it 'updates project cache data' do + expect_any_instance_of(Repository).to receive(:size) + expect_any_instance_of(Repository).to receive(:commit_count) - subject.perform(project.id) + expect_any_instance_of(Project).to receive(:update_repository_size) + expect_any_instance_of(Project).to receive(:update_commit_count) + + subject.perform(project.id) + end + + it 'handles missing repository data' do + expect_any_instance_of(Repository).to receive(:exists?).and_return(false) + expect_any_instance_of(Repository).not_to receive(:size) + + subject.perform(project.id) + end end - it 'handles missing repository data' do - expect_any_instance_of(Repository).to receive(:exists?).and_return(false) - expect_any_instance_of(Repository).not_to receive(:size) + context 'when an exclusive lease can not be obtained' do + it 'does nothing' do + allow(subject).to receive(:try_obtain_lease_for).with(project.id). + and_return(false) - subject.perform(project.id) + expect(subject).not_to receive(:update_caches) + + subject.perform(project.id) + end end end end From bd3548c19e00cfadbc5d2c122b968090a160afbc Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 13 Oct 2016 13:58:31 -0500 Subject: [PATCH 014/129] Change input order on Sign In form for better tabbing. This *unreverts* 8751491b, which was mistakenly reverted in !6328. It also changes the implementation of the original commit to work with the new login styling and markup. cc: @ClemMakesApps --- app/assets/stylesheets/pages/login.scss | 17 +++++++++++++++++ app/views/devise/sessions/_new_base.html.haml | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index e6d9be5185d..bdb13bee178 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -53,6 +53,7 @@ margin: 0 0 10px; } + .login-footer { margin-top: 10px; @@ -246,3 +247,19 @@ padding: 65px; // height of footer + bottom padding of email confirmation link } } + +// For sign in pane only, to improve tab order, the following removes the submit button from +// normal document flow and pins it to the bottom of the form. For context, see !6867 & !6928 + +.login-box { + .new_user { + position: relative; + padding-bottom: 35px; + } + + .move-submit-down { + position: absolute; + width: 100%; + bottom: 0; + } +} diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index a96b579c593..525e7d99d71 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -5,6 +5,8 @@ %div.form-group = f.label :password = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required." + %div.submit-container.move-submit-down + = f.submit "Sign in", class: "btn btn-save" - if devise_mapping.rememberable? .remember-me.checkbox %label{for: "user_remember_me"} @@ -12,5 +14,3 @@ %span Remember me .pull-right = link_to "Forgot your password?", new_password_path(resource_name) - %div.submit-container - = f.submit "Sign in", class: "btn btn-save" From a2f64619af8da5c5b09ead7554e5613368f80dae Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 18 Sep 2016 17:20:40 +0100 Subject: [PATCH 015/129] Added tooltip with jobs full name to build items in graph Added status to build tooltip and removed status icon tooltip Added Pipelines class to force tooltips ontop of the dropdown, we cannot do this with data attributes because dropdown toggle is already set Corrected dispatcher invocation --- app/assets/javascripts/pipelines.js.es6 | 11 +++++++++++ app/helpers/ci_status_helper.rb | 4 ++-- app/views/projects/commit/_pipeline_stage.html.haml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index a7624de6089..dd320c52e44 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -4,6 +4,17 @@ constructor() { $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); this.addMarginToBuildColumns(); + this.initGroupedPipelineTooltips(); + } + + initGroupedPipelineTooltips() { + $('.dropdown-menu-toggle', $('.grouped-pipeline-dropdown').parent()).each(function() { + const $this = $(this); + $this.tooltip({ + title: $this.data('tooltip-title'), + placement: 'bottom' + }); + }); } toggleGraph() { diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index b7f48630bd4..0e727f3dcf0 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -71,10 +71,10 @@ module CiStatusHelper Ci::Runner.shared.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body') + def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body', show_tooltip: true) klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" - data = { toggle: 'tooltip', placement: tooltip_placement, container: container } + data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if show_tooltip if path link_to ci_icon_for_status(status), path, diff --git a/app/views/projects/commit/_pipeline_stage.html.haml b/app/views/projects/commit/_pipeline_stage.html.haml index 289aa5178b1..993fc89d23d 100644 --- a/app/views/projects/commit/_pipeline_stage.html.haml +++ b/app/views/projects/commit/_pipeline_stage.html.haml @@ -5,7 +5,7 @@ - is_playable = status.playable? && can?(current_user, :update_build, @project) %li.build{ class: ("playable" if is_playable) } .curve - .build-content + .build-content{ { data: { toggle: 'tooltip', title: "#{group_name} - #{status.status}", placement: 'bottom' } } } = render "projects/#{status.to_partial_path}_pipeline", subject: status - else %li.build From 33f3c3792abe2cbf1b68c0ce3414edb8e199be1e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 7 Oct 2016 19:30:34 +0100 Subject: [PATCH 016/129] Added dyanmic position adjustment Added tooltips for dropdown items Reverted pretty much everything in favour of a DOM approach Simplified JS --- app/assets/javascripts/pipelines.js.es6 | 11 ----------- app/assets/stylesheets/pages/pipelines.scss | 14 ++++++++++---- app/helpers/ci_status_helper.rb | 4 ++-- .../projects/ci/builds/_build_pipeline.html.haml | 4 ++-- .../projects/commit/_pipeline_stage.html.haml | 2 +- .../commit/_pipeline_status_group.html.haml | 2 +- .../_generic_commit_status_pipeline.html.haml | 13 +++++++------ 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index dd320c52e44..a7624de6089 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -4,17 +4,6 @@ constructor() { $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); this.addMarginToBuildColumns(); - this.initGroupedPipelineTooltips(); - } - - initGroupedPipelineTooltips() { - $('.dropdown-menu-toggle', $('.grouped-pipeline-dropdown').parent()).each(function() { - const $this = $(this); - $this.tooltip({ - title: $this.data('tooltip-title'), - placement: 'bottom' - }); - }); } toggleGraph() { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index dfaf1ab732d..5b8dc7f8c40 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -369,10 +369,6 @@ &:hover { background-color: $gray-lighter; - - .dropdown-menu-toggle { - background-color: transparent; - } } &.playable { @@ -402,6 +398,15 @@ } } + .tooltip { + white-space: nowrap; + + .tooltip-inner { + overflow: hidden; + text-overflow: ellipsis; + } + } + .ci-status-text { width: 135px; white-space: nowrap; @@ -419,6 +424,7 @@ } .dropdown-menu-toggle { + background-color: transparent; border: none; width: auto; padding: 0; diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 0e727f3dcf0..b7f48630bd4 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -71,10 +71,10 @@ module CiStatusHelper Ci::Runner.shared.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body', show_tooltip: true) + def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body') klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" - data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if show_tooltip + data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if path link_to ci_icon_for_status(status), path, diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml index 017d3ff6af2..55965172d3f 100644 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ b/app/views/projects/ci/builds/_build_pipeline.html.haml @@ -1,10 +1,10 @@ - is_playable = subject.playable? && can?(current_user, :update_build, @project) - if is_playable - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: 'Play' do + = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do = render_status_with_link('build', 'play') .ci-status-text= subject.name - elsif can?(current_user, :read_build, @project) - = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject) do + = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do %span.ci-status-icon = render_status_with_link('build', subject.status) .ci-status-text= subject.name diff --git a/app/views/projects/commit/_pipeline_stage.html.haml b/app/views/projects/commit/_pipeline_stage.html.haml index 993fc89d23d..289aa5178b1 100644 --- a/app/views/projects/commit/_pipeline_stage.html.haml +++ b/app/views/projects/commit/_pipeline_stage.html.haml @@ -5,7 +5,7 @@ - is_playable = status.playable? && can?(current_user, :update_build, @project) %li.build{ class: ("playable" if is_playable) } .curve - .build-content{ { data: { toggle: 'tooltip', title: "#{group_name} - #{status.status}", placement: 'bottom' } } } + .build-content = render "projects/#{status.to_partial_path}_pipeline", subject: status - else %li.build diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml index 5d0d5ba0262..f2d71fa6989 100644 --- a/app/views/projects/commit/_pipeline_status_group.html.haml +++ b/app/views/projects/commit/_pipeline_status_group.html.haml @@ -1,5 +1,5 @@ - group_status = CommitStatus.where(id: subject).status -%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } +%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } } %span.ci-status-icon = render_status_with_link('build', group_status) %span.ci-status-text diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml index 0a66d60accc..c45b73e4225 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml @@ -1,9 +1,10 @@ -- if subject.target_url - = link_to subject.target_url do +%a{ data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } } + - if subject.target_url + = link_to subject.target_url do + %span.ci-status-icon + = render_status_with_link('commit status', subject.status) + %span.ci-status-text= subject.name + - else %span.ci-status-icon = render_status_with_link('commit status', subject.status) %span.ci-status-text= subject.name -- else - %span.ci-status-icon - = render_status_with_link('commit status', subject.status) - %span.ci-status-text= subject.name From b6c859974307b71a82544cb137196b1bcd7ef7ef Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 16:53:12 +0100 Subject: [PATCH 017/129] Revert "Add #closed_without_source_project?" This reverts commit 31c37c6c38258684fc92e0d91119c33872e39034. See #23341 --- app/models/merge_request.rb | 10 ++---- .../projects/merge_requests/_show.html.haml | 31 ++++++++---------- spec/models/merge_request_spec.rb | 32 ------------------- 3 files changed, 16 insertions(+), 57 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0cc0b3c2a0e..d32bc9f882f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -333,11 +333,7 @@ class MergeRequest < ActiveRecord::Base end def closed_without_fork? - closed? && forked_source_project_missing? - end - - def closed_without_source_project? - closed? && !source_project + closed? && (forked_source_project_missing? || !source_project) end def forked_source_project_missing? @@ -348,9 +344,7 @@ class MergeRequest < ActiveRecord::Base end def reopenable? - return false if closed_without_fork? || closed_without_source_project? || merged? - - closed? + source_branch_exists? && closed? end def ensure_merge_request_diff diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index cd98aaf8d75..fe90383b00f 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,19 +26,17 @@ %ul.dropdown-menu.dropdown-menu-align-right %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) - - unless @merge_request.closed_without_fork? - .normal - %span Request to merge - %span.label-branch= source_branch_with_namespace(@merge_request) - %span into - %span.label-branch - = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch) - - if @merge_request.open? && @merge_request.diverged_from_target_branch? - %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) + .normal + %span Request to merge + %span.label-branch= source_branch_with_namespace(@merge_request) + %span into + %span.label-branch + = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch) + - if @merge_request.open? && @merge_request.diverged_from_target_branch? + %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) - - unless @merge_request.closed_without_source_project? - = render "projects/merge_requests/show/how_to_merge" - = render "projects/merge_requests/widget/show.html.haml" + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) .light.prepend-top-default.append-bottom-default @@ -52,11 +50,10 @@ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do Discussion %span.badge= @merge_request.mr_and_commit_notes.user.count - - unless @merge_request.closed_without_source_project? - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count - if @pipeline %li.pipelines-tab = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6db5e7f7d80..ee003a9d18f 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1274,38 +1274,6 @@ describe MergeRequest, models: true do end end - describe '#closed_without_source_project?' do - let(:project) { create(:project) } - let(:user) { create(:user) } - let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) } - let(:destroy_service) { Projects::DestroyService.new(fork_project, user) } - - context 'when the merge request is closed' do - let(:closed_merge_request) do - create(:closed_merge_request, - source_project: fork_project, - target_project: project) - end - - it 'returns false if the source project exists' do - expect(closed_merge_request.closed_without_source_project?).to be_falsey - end - - it 'returns true if the source project does not exist' do - destroy_service.execute - closed_merge_request.reload - - expect(closed_merge_request.closed_without_source_project?).to be_truthy - end - end - - context 'when the merge request is open' do - it 'returns false' do - expect(subject.closed_without_source_project?).to be_falsey - end - end - end - describe '#reopenable?' do context 'when the merge request is closed' do it 'returns true' do From f00ee5c2ed9a0812b6337e52814756c560c879c8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 17:13:04 +0100 Subject: [PATCH 018/129] Fix the merge request view when source projects or branches are removed --- .../projects/merge_requests_controller.rb | 2 +- app/helpers/merge_requests_helper.rb | 10 +++++++--- app/models/merge_request.rb | 15 ++++++--------- app/views/projects/merge_requests/_show.html.haml | 13 ++++++++----- .../merge_requests/created_from_fork_spec.rb | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0f593d1a936..55ea31e48a0 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -554,7 +554,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController def define_pipelines_vars @pipelines = @merge_request.all_pipelines - if @pipelines.any? + if @pipelines.present? @pipeline = @pipelines.first @statuses = @pipeline.statuses.relevant end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 249cb44e9d5..a6659ea2fd1 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -86,11 +86,15 @@ module MergeRequestsHelper end def source_branch_with_namespace(merge_request) - branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + namespace = merge_request.source_project_namespace + branch = merge_request.source_branch + + if merge_request.source_branch_exists? + namespace = link_to(namespace, project_path(merge_request.source_project)) + branch = link_to(branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + end if merge_request.for_fork? - namespace = link_to(merge_request.source_project_namespace, - project_path(merge_request.source_project)) namespace + ":" + branch else branch diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d32bc9f882f..45fa8af60e5 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -333,7 +333,7 @@ class MergeRequest < ActiveRecord::Base end def closed_without_fork? - closed? && (forked_source_project_missing? || !source_project) + closed? && forked_source_project_missing? end def forked_source_project_missing? @@ -344,7 +344,7 @@ class MergeRequest < ActiveRecord::Base end def reopenable? - source_branch_exists? && closed? + closed? && !source_project_missing? && source_branch_exists? end def ensure_merge_request_diff @@ -656,7 +656,7 @@ class MergeRequest < ActiveRecord::Base end def has_ci? - source_project.ci_service && commits.any? + source_project.try(:ci_service) && commits.any? end def branch_missing? @@ -688,12 +688,9 @@ class MergeRequest < ActiveRecord::Base @environments ||= begin - environments = source_project.environments_for( - source_branch, diff_head_commit) - environments += target_project.environments_for( - target_branch, diff_head_commit, with_tags: true) - - environments.uniq + envs = target_project.environments_for(target_branch, diff_head_commit, with_tags: true) + envs.concat(source_project.environments_for(source_branch, diff_head_commit)) if source_project + envs.uniq end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index fe90383b00f..0e19d224fcd 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -35,7 +35,9 @@ - if @merge_request.open? && @merge_request.diverged_from_target_branch? %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) - = render "projects/merge_requests/show/how_to_merge" + - if @merge_request.source_branch_exists? + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) @@ -50,10 +52,11 @@ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do Discussion %span.badge= @merge_request.mr_and_commit_notes.user.count - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count + - if @merge_request.source_project + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count - if @pipeline %li.pipelines-tab = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb index a506624b30d..cfc1244429f 100644 --- a/spec/features/merge_requests/created_from_fork_spec.rb +++ b/spec/features/merge_requests/created_from_fork_spec.rb @@ -25,6 +25,20 @@ feature 'Merge request created from fork' do expect(page).to have_content 'Test merge request' end + context 'source project is deleted' do + background do + MergeRequests::MergeService.new(project, user).execute(merge_request) + fork_project.destroy! + end + + scenario 'user can access merge request' do + visit_merge_request(merge_request) + + expect(page).to have_content 'Test merge request' + expect(page).to have_content "(removed):#{merge_request.source_branch}" + end + end + context 'pipeline present in source project' do include WaitForAjax From f118d2fc7394cbbd71a8c97e0c186581c045fb47 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 17:48:30 +0100 Subject: [PATCH 019/129] Fix two CI endpoints for MRs where the source project is deleted --- app/controllers/projects/merge_requests_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 55ea31e48a0..2ee53f7ceda 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -398,7 +398,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController status ||= "preparing" else - ci_service = @merge_request.source_project.ci_service + ci_service = @merge_request.source_project.try(:ci_service) status = ci_service.commit_status(merge_request.diff_head_sha, merge_request.source_branch) if ci_service if ci_service.respond_to?(:commit_coverage) From 51fbb9bfa90f293447cf3f338bdc0a189b601d0e Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 19:33:51 +0100 Subject: [PATCH 020/129] Rename forked_source_project_missing? to source_project_missing? --- app/models/merge_request.rb | 6 +++--- spec/models/merge_request_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 45fa8af60e5..c476a3bb14e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -326,17 +326,17 @@ class MergeRequest < ActiveRecord::Base def validate_fork return true unless target_project && source_project return true if target_project == source_project - return true unless forked_source_project_missing? + return true unless source_project_missing? errors.add :validate_fork, 'Source project is not a fork of the target project' end def closed_without_fork? - closed? && forked_source_project_missing? + closed? && source_project_missing? end - def forked_source_project_missing? + def source_project_missing? return false unless for_fork? return true unless source_project diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index ee003a9d18f..6e5137602aa 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1198,7 +1198,7 @@ describe MergeRequest, models: true do end end - describe "#forked_source_project_missing?" do + describe "#source_project_missing?" do let(:project) { create(:project) } let(:fork_project) { create(:project, forked_from_project: project) } let(:user) { create(:user) } @@ -1211,13 +1211,13 @@ describe MergeRequest, models: true do target_project: project) end - it { expect(merge_request.forked_source_project_missing?).to be_falsey } + it { expect(merge_request.source_project_missing?).to be_falsey } end context "when the source project is the same as the target project" do let(:merge_request) { create(:merge_request, source_project: project) } - it { expect(merge_request.forked_source_project_missing?).to be_falsey } + it { expect(merge_request.source_project_missing?).to be_falsey } end context "when the fork does not exist" do @@ -1231,7 +1231,7 @@ describe MergeRequest, models: true do unlink_project.execute merge_request.reload - expect(merge_request.forked_source_project_missing?).to be_truthy + expect(merge_request.source_project_missing?).to be_truthy end end end From 25447b46c6adf62a0aafc0da38d456ef80e489f3 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 18:09:33 +0100 Subject: [PATCH 021/129] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98cffab8c03..4f163f323e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Please view this file on the master branch, on stable branches it's out of date. - ProjectCacheWorker updates caches at most once per 15 minutes per project - Fix Error 500 when viewing old merge requests with bad diff data - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar) + - Fix viewing merged MRs when the source project has been removed !6991 - Speed-up group milestones show page - Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps) - Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService From 3ab461dc64d8cadd61a67e632fde309fb887d1e8 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Thu, 15 Sep 2016 18:45:22 +0300 Subject: [PATCH 022/129] Render hipchat notification descriptions as HTML instead of raw markdown --- .../project_services/hipchat_service.rb | 9 +++------ .../project_services/hipchat_service_spec.rb | 20 +++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index afebd3b6a12..98f0312d84e 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -144,8 +144,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" if description - description = format_body(description) - message << description + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) end message @@ -167,8 +166,7 @@ class HipchatService < Service "#{project_link}: #{title}" if description - description = format_body(description) - message << description + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) end message @@ -219,8 +217,7 @@ class HipchatService < Service message << title if note - note = format_body(note) - message << note + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(note)) end message diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 26dd95bdfec..90066f01f6f 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -117,7 +117,7 @@ describe HipchatService, models: true do end context 'issue events' do - let(:issue) { create(:issue, title: 'Awesome issue', description: 'please fix') } + let(:issue) { create(:issue, title: 'Awesome issue', description: '**please** fix') } let(:issue_service) { Issues::CreateService.new(project, user) } let(:issues_sample_data) { issue_service.hook_data(issue, 'open') } @@ -135,12 +135,12 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "
please fix
") + "

please fix

\n
") end end context 'merge request events' do - let(:merge_request) { create(:merge_request, description: 'please fix', title: 'Awesome merge request', target_project: project, source_project: project) } + let(:merge_request) { create(:merge_request, description: '**please** fix', title: 'Awesome merge request', target_project: project, source_project: project) } let(:merge_service) { MergeRequests::CreateService.new(project, user) } let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') } @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "
please fix
") + "

please fix

\n
") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "
a comment on a commit
") + "

a comment on a commit

\n
") end end @@ -203,7 +203,7 @@ describe HipchatService, models: true do let(:merge_request_note) do create(:note_on_merge_request, noteable: merge_request, project: project, - note: "merge request note") + note: "merge request **note**") end it "calls Hipchat API for merge request comment events" do @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "
merge request note
") + "

merge request note

\n
") end end @@ -230,7 +230,7 @@ describe HipchatService, models: true do let(:issue) { create(:issue, project: project) } let(:issue_note) do create(:note_on_issue, noteable: issue, project: project, - note: "issue note") + note: "issue **note**") end it "calls Hipchat API for issue comment events" do @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "
issue note
") + "

issue note

\n
") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "
snippet note
") + "

snippet note

\n
") end end end From 0090116d87585ab6e433ba7c04198271848c43c0 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 12:28:42 +0100 Subject: [PATCH 023/129] Full Banzai rendering for HipChat notifications --- app/models/project_services/hipchat_service.rb | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 98f0312d84e..f4edbbbceb2 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -121,14 +121,6 @@ class HipchatService < Service message end - def format_body(body) - if body - body = body.truncate(200, separator: ' ', omission: '...') - end - - "
#{body}
" - end - def create_issue_message(data) user_name = data[:user][:name] @@ -144,7 +136,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" if description - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) + message << Banzai.render(note, project: project) end message @@ -166,7 +158,7 @@ class HipchatService < Service "#{project_link}: #{title}" if description - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) + message << Banzai.render(note, project: project) end message @@ -217,7 +209,7 @@ class HipchatService < Service message << title if note - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(note)) + message << Banzai.render(note, project: project) end message From ad28b3989dece0d3495e08cf545708caf9642cfc Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 12:38:31 +0100 Subject: [PATCH 024/129] Also render commit titles in HipChat notifications --- app/models/project_services/hipchat_service.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index f4edbbbceb2..7d70452a70b 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -88,6 +88,10 @@ class HipchatService < Service end end + def render_line(text) + Banzai.render(text.lines.first.chomp, project: project, pipeline: :single_line) if text + end + def create_push_message(push) ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch' ref = Gitlab::Git.ref_name(push[:ref]) @@ -110,7 +114,7 @@ class HipchatService < Service message << "(Compare changes)" push[:commits].take(MAX_COMMITS).each do |commit| - message << "
- #{commit[:message].lines.first} (#{commit[:id][0..5]})" + message << "
- #{render_line(commit[:message])} (#{commit[:id][0..5]})" end if push[:commits].count > MAX_COMMITS @@ -126,7 +130,7 @@ class HipchatService < Service obj_attr = data[:object_attributes] obj_attr = HashWithIndifferentAccess.new(obj_attr) - title = obj_attr[:title] + title = render_line(obj_attr[:title]) state = obj_attr[:state] issue_iid = obj_attr[:iid] issue_url = obj_attr[:url] @@ -150,7 +154,7 @@ class HipchatService < Service merge_request_id = obj_attr[:iid] state = obj_attr[:state] description = obj_attr[:description] - title = obj_attr[:title] + title = render_line(obj_attr[:title]) merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}" merge_request_link = "merge request !#{merge_request_id}" @@ -165,7 +169,7 @@ class HipchatService < Service end def format_title(title) - "" + title.lines.first.chomp + "" + "#{render_line(title)}" end def create_note_message(data) From 59b2770a035489db9995b8638792ed5c92242035 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:23:16 +0100 Subject: [PATCH 025/129] Absolute URLs for Banzai HTML for HipChat Using "pipeline: :email" gets "only_path: false" into the context to produce full URLs instead of /namespace/project/... --- .../project_services/hipchat_service.rb | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 7d70452a70b..9d52a1423b7 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -89,7 +89,7 @@ class HipchatService < Service end def render_line(text) - Banzai.render(text.lines.first.chomp, project: project, pipeline: :single_line) if text + markdown(text.lines.first.chomp, pipeline: :single_line) if text end def create_push_message(push) @@ -125,6 +125,20 @@ class HipchatService < Service message end + def markdown(text, context = {}) + if text + context = ({ + project: project, + pipeline: :email + }).merge(context) + + html = Banzai.render(text, context) + html = Banzai.post_process(html, context) + else + "" + end + end + def create_issue_message(data) user_name = data[:user][:name] @@ -139,9 +153,7 @@ class HipchatService < Service issue_link = "issue ##{issue_iid}" message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" - if description - message << Banzai.render(note, project: project) - end + message << markdown(description) message end @@ -161,9 +173,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{merge_request_link} in " \ "#{project_link}: #{title}" - if description - message << Banzai.render(note, project: project) - end + message << markdown(description) message end @@ -180,11 +190,13 @@ class HipchatService < Service note = obj_attr[:note] note_url = obj_attr[:url] noteable_type = obj_attr[:noteable_type] + commit_id = nil case noteable_type when "Commit" commit_attr = HashWithIndifferentAccess.new(data[:commit]) - subject_desc = commit_attr[:id] + commit_id = commit_attr[:id] + subject_desc = commit_id subject_desc = Commit.truncate_sha(subject_desc) subject_type = "commit" title = format_title(commit_attr[:message]) @@ -212,9 +224,7 @@ class HipchatService < Service message = "#{user_name} commented on #{subject_html} in #{project_link}: " message << title - if note - message << Banzai.render(note, project: project) - end + message << markdown(note, ref: commit_id) message end From 73f13526d6ef3da1289074e2503bf2764b41b137 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:25:45 +0100 Subject: [PATCH 026/129] Ensure absolute URLs for single lines from Banzai for HipChat "pipeline: :single_line" leaves the protocol/host part out of the URLs and caches them that way. To avoid giving those out to HipChat, markdown is always rendered with "pipeline: :email" first. There must be a better way to do this, but I can't see how to avoid the link caching. --- app/models/project_services/hipchat_service.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 9d52a1423b7..ce4a2a96015 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -125,12 +125,16 @@ class HipchatService < Service message end - def markdown(text, context = {}) + def markdown(text, options = {}) if text - context = ({ + context = { project: project, pipeline: :email - }).merge(context) + } + + Banzai.render(text, context) + + context.merge!(options) html = Banzai.render(text, context) html = Banzai.post_process(html, context) From 551c74edf75e3fa89ea57a45b217a3a34f8c2fc1 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:27:40 +0100 Subject: [PATCH 027/129] Clean up Banzai HTML for HipChat The `class` and `data-*` attributes are meaningless in HipChat, and it would probably be better to limit the tags, too. For example, we could avoid block-level elements in `render_line`. --- app/models/project_services/hipchat_service.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index ce4a2a96015..8988a7b905e 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -1,4 +1,6 @@ class HipchatService < Service + include ActionView::Helpers::SanitizeHelper + MAX_COMMITS = 3 prop_accessor :token, :room, :server, :notify, :color, :api_version @@ -138,6 +140,7 @@ class HipchatService < Service html = Banzai.render(text, context) html = Banzai.post_process(html, context) + sanitize html, attributes: %w(href title alt) else "" end From f6a97e6c0bf4d0f699ded24983c6be1ca4b5d6cc Mon Sep 17 00:00:00 2001 From: David Eisner Date: Wed, 5 Oct 2016 13:38:08 +0100 Subject: [PATCH 028/129] Tests for markdown HipChat notifications --- spec/models/project_services/hipchat_service_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 90066f01f6f..1029b6d2459 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -135,7 +135,7 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "

please fix

\n
") + "

please fix

") end end @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "

please fix

\n
") + "

please fix

") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "

a comment on a commit

\n
") + "

a comment on a commit

") end end @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "

merge request note

\n
") + "

merge request note

") end end @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "

issue note

\n
") + "

issue note

") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "

snippet note

\n
") + "

snippet note

") end end end From 1c7807925aaa3efcc85275273cf65c574985dd61 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Wed, 12 Oct 2016 09:51:02 +0300 Subject: [PATCH 029/129] Use guard clause instead of if-else statement --- .../project_services/hipchat_service.rb | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 8988a7b905e..e7a77070b9f 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -128,22 +128,21 @@ class HipchatService < Service end def markdown(text, options = {}) - if text - context = { - project: project, - pipeline: :email - } + return "" unless text - Banzai.render(text, context) + context = { + project: project, + pipeline: :email + } - context.merge!(options) + Banzai.render(text, context) - html = Banzai.render(text, context) - html = Banzai.post_process(html, context) - sanitize html, attributes: %w(href title alt) - else - "" - end + context.merge!(options) + + html = Banzai.render(text, context) + html = Banzai.post_process(html, context) + + sanitize html, attributes: %w(href title alt) end def create_issue_message(data) From 5d608ceb4f433a0d3f196a2a5cb11cf6a535baf2 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Wed, 19 Oct 2016 22:51:15 +0300 Subject: [PATCH 030/129] Return truncation for notification descriptions, fix minor bugs with rendering --- app/models/project_services/hipchat_service.rb | 17 +++++++++++------ .../project_services/hipchat_service_spec.rb | 12 ++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index e7a77070b9f..660a8ae3421 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -2,6 +2,11 @@ class HipchatService < Service include ActionView::Helpers::SanitizeHelper MAX_COMMITS = 3 + HIPCHAT_ALLOWED_TAGS = %w[ + a b i strong em br img pre code + table th tr td caption colgroup col thead tbody tfoot + ul ol li dl dt dd + ] prop_accessor :token, :room, :server, :notify, :color, :api_version boolean_accessor :notify_only_broken_builds @@ -139,10 +144,10 @@ class HipchatService < Service context.merge!(options) - html = Banzai.render(text, context) - html = Banzai.post_process(html, context) + html = Banzai.post_process(Banzai.render(text, context), context) + sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt]) - sanitize html, attributes: %w(href title alt) + sanitized_html.truncate(200, separator: ' ', omission: '...') end def create_issue_message(data) @@ -159,7 +164,7 @@ class HipchatService < Service issue_link = "issue ##{issue_iid}" message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" - message << markdown(description) + message << "
#{markdown(description)}
" message end @@ -179,7 +184,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{merge_request_link} in " \ "#{project_link}: #{title}" - message << markdown(description) + message << "
#{markdown(description)}
" message end @@ -230,7 +235,7 @@ class HipchatService < Service message = "#{user_name} commented on #{subject_html} in #{project_link}: " message << title - message << markdown(note, ref: commit_id) + message << "
#{markdown(note, ref: commit_id)}
" message end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 1029b6d2459..2da3a9cb09f 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -135,7 +135,7 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "

please fix

") + "
please fix
") end end @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "

please fix

") + "
please fix
") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "

a comment on a commit

") + "
a comment on a commit
") end end @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "

merge request note

") + "
merge request note
") end end @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "

issue note

") + "
issue note
") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "

snippet note

") + "
snippet note
") end end end From 435678d58828db76c3ef5f95643dac0e3ae979b3 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Thu, 20 Oct 2016 15:12:39 +0300 Subject: [PATCH 031/129] Add CHANGELOG.md entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f163f323e6..0bfdf23b959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) + - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) From ea704005c7bdca55e2782a2c24fe0c5e89cad179 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 19 Oct 2016 14:48:37 +0200 Subject: [PATCH 032/129] Update duration at the end of pipeline --- CHANGELOG.md | 1 + app/models/ci/pipeline.rb | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bfdf23b959..de24e08c52f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add `/projects/visible` API endpoint (Ben Boeckel) - Fix centering of custom header logos (Ashley Dumaine) - Keep around commits only pipeline creation as pipeline data doesn't change over time + - Update duration at the end of pipeline - ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup - Add group level labels. (!6425) - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index e84c91b417d..d5c1e03b461 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -59,9 +59,6 @@ module Ci before_transition any => [:success, :failed, :canceled] do |pipeline| pipeline.finished_at = Time.now - end - - before_transition do |pipeline| pipeline.update_duration end From 523ee4a5f4c28aab29310a742f4798bc77f20ccf Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 16:16:57 +0800 Subject: [PATCH 033/129] Preserve note_type and position for notes from emails Closes #23208 --- .../email/handler/create_note_handler.rb | 4 +++- spec/fixtures/emails/commands_in_reply.eml | 2 -- spec/fixtures/emails/commands_only_reply.eml | 2 -- .../email/handler/create_note_handler_spec.rb | 24 ++++++++++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb index 06dae31cc27..447c7a6a6b9 100644 --- a/lib/gitlab/email/handler/create_note_handler.rb +++ b/lib/gitlab/email/handler/create_note_handler.rb @@ -46,7 +46,9 @@ module Gitlab noteable_type: sent_notification.noteable_type, noteable_id: sent_notification.noteable_id, commit_id: sent_notification.commit_id, - line_code: sent_notification.line_code + line_code: sent_notification.line_code, + position: sent_notification.position, + type: sent_notification.note_type ).execute end end diff --git a/spec/fixtures/emails/commands_in_reply.eml b/spec/fixtures/emails/commands_in_reply.eml index 06bf60ab734..712f6f797b4 100644 --- a/spec/fixtures/emails/commands_in_reply.eml +++ b/spec/fixtures/emails/commands_in_reply.eml @@ -23,8 +23,6 @@ Cool! /close /todo -/due tomorrow - On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta wrote: diff --git a/spec/fixtures/emails/commands_only_reply.eml b/spec/fixtures/emails/commands_only_reply.eml index aed64224b06..2d2e2f94290 100644 --- a/spec/fixtures/emails/commands_only_reply.eml +++ b/spec/fixtures/emails/commands_only_reply.eml @@ -21,8 +21,6 @@ X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 /close /todo -/due tomorrow - On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta wrote: diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index 4909fed6b77..48660d1dd1b 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -12,10 +12,13 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do let(:email_raw) { fixture_file('emails/valid_reply.eml') } let(:project) { create(:project, :public) } - let(:noteable) { create(:issue, project: project) } let(:user) { create(:user) } + let(:note) { create(:diff_note_on_merge_request, project: project) } + let(:noteable) { note.noteable } - let!(:sent_notification) { SentNotification.record(noteable, user.id, mail_key) } + let!(:sent_notification) do + SentNotification.record_note(note, user.id, mail_key) + end context "when the recipient address doesn't include a mail key" do let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "") } @@ -82,7 +85,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(1) expect(noteable.reload).to be_closed - expect(noteable.due_date).to eq(Date.tomorrow) expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy end end @@ -100,7 +102,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(1) expect(noteable.reload).to be_open - expect(noteable.due_date).to be_nil expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy end end @@ -117,7 +118,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(2) expect(noteable.reload).to be_closed - expect(noteable.due_date).to eq(Date.tomorrow) expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy end end @@ -138,10 +138,11 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do it "creates a comment" do expect { receiver.execute }.to change { noteable.notes.count }.by(1) - note = noteable.notes.last + new_note = noteable.notes.last - expect(note.author).to eq(sent_notification.recipient) - expect(note.note).to include("I could not disagree more.") + expect(new_note.author).to eq(sent_notification.recipient) + expect(new_note.position).to eq(note.position) + expect(new_note.note).to include("I could not disagree more.") end it "adds all attachments" do @@ -160,10 +161,11 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do shared_examples 'an email that contains a mail key' do |header| it "fetches the mail key from the #{header} header and creates a comment" do expect { receiver.execute }.to change { noteable.notes.count }.by(1) - note = noteable.notes.last + new_note = noteable.notes.last - expect(note.author).to eq(sent_notification.recipient) - expect(note.note).to include('I could not disagree more.') + expect(new_note.author).to eq(sent_notification.recipient) + expect(new_note.position).to eq(note.position) + expect(new_note.note).to include('I could not disagree more.') end end From cbb65aae395821c596746d2b347fc3a7de1b3da8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 16:24:37 +0800 Subject: [PATCH 034/129] Add CHANGELOG entry [ci skip] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de24e08c52f..98d95e275e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Simpler arguments passed to named_route on toggle_award_url helper method + - Fix discussion thread from emails for merge requests. !7010 + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) From 25d00ea871a970fb82d79067d97c84fdcb5264c5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 20:47:21 +0800 Subject: [PATCH 035/129] We want to release this in 8.13.0 --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d95e275e2..73dc323e02c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,6 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Simpler arguments passed to named_route on toggle_award_url helper method - - Fix discussion thread from emails for merge requests. !7010 - ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) @@ -47,6 +45,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Speed-up group milestones show page - Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps) - Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService + - Fix discussion thread from emails for merge requests. !7010 - Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs) - Add tag shortcut from the Commit page. !6543 - Keep refs for each deployment From 8979567e439c624145ffef7ebc255b7936b5d05e Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 19 Oct 2016 14:49:09 +0200 Subject: [PATCH 036/129] Use deployment IID when saving refs --- app/models/deployment.rb | 2 +- app/models/environment.rb | 4 ++-- spec/controllers/projects/merge_requests_controller_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 1f8c5fb3d85..c843903877b 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -102,6 +102,6 @@ class Deployment < ActiveRecord::Base private def ref_path - File.join(environment.ref_path, 'deployments', id.to_s) + File.join(environment.ref_path, 'deployments', iid.to_s) end end diff --git a/app/models/environment.rb b/app/models/environment.rb index d575f1dc73a..73f415c0ef0 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -71,8 +71,8 @@ class Environment < ActiveRecord::Base return nil unless ref - deployment_id = ref.split('/').last - deployments.find(deployment_id) + deployment_iid = ref.split('/').last + deployments.find_by(iid: deployment_iid) end def ref_path diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index d6980471ea4..940d54f8686 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -913,7 +913,7 @@ describe Projects::MergeRequestsController do end describe 'GET ci_environments_status' do - context 'when the environment is from a forked project' do + context 'the environment is from a forked project' do let!(:forked) { create(:project) } let!(:environment) { create(:environment, project: forked) } let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } From 1cb75998992745216cb634537c281561559d1964 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 20 Oct 2016 14:21:40 +0200 Subject: [PATCH 037/129] Only create refs for new deployments This patch makes sure GitLab does not save the refs to the filesystem each time the deployment is updated. This will save some IO although I expect the impact to be minimal. --- app/models/deployment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index c843903877b..91d85c2279b 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -11,7 +11,7 @@ class Deployment < ActiveRecord::Base delegate :name, to: :environment, prefix: true - after_save :create_ref + after_create :create_ref def commit project.commit(sha) From 3974c2e9e18d80a9b5a49bd16ea3fc0d157dd919 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 14 Oct 2016 17:25:12 -0500 Subject: [PATCH 038/129] Create protected branches bundle --- .../protected_branch_access_dropdown.js.es6 | 0 .../{ => protected_branches}/protected_branch_create.js.es6 | 0 .../{ => protected_branches}/protected_branch_dropdown.js.es6 | 0 .../{ => protected_branches}/protected_branch_edit.js.es6 | 0 .../{ => protected_branches}/protected_branch_edit_list.js.es6 | 0 .../javascripts/protected_branches/protected_branches_bundle.js | 1 + app/views/projects/protected_branches/index.html.haml | 2 ++ config/application.rb | 1 + 8 files changed, 4 insertions(+) rename app/assets/javascripts/{ => protected_branches}/protected_branch_access_dropdown.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_create.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_dropdown.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_edit.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_edit_list.js.es6 (100%) create mode 100644 app/assets/javascripts/protected_branches/protected_branches_bundle.js diff --git a/app/assets/javascripts/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_access_dropdown.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 diff --git a/app/assets/javascripts/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_create.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_create.js.es6 diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_dropdown.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 diff --git a/app/assets/javascripts/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_edit.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 diff --git a/app/assets/javascripts/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_edit_list.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js new file mode 100644 index 00000000000..15b3affd469 --- /dev/null +++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js @@ -0,0 +1 @@ +/*= require_tree . */ diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 49dcc9a6ba4..42e9bdbd30e 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -1,4 +1,6 @@ - page_title "Protected branches" +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('protected_branches/protected_branches_bundle.js') .row.prepend-top-default.append-bottom-default .col-lg-3 diff --git a/config/application.rb b/config/application.rb index 8a9c539cb43..f3337b00dc6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -87,6 +87,7 @@ module Gitlab config.assets.precompile << "users/users_bundle.js" config.assets.precompile << "network/network_bundle.js" config.assets.precompile << "profile/profile_bundle.js" + config.assets.precompile << "protected_branches/protected_branches_bundle.js" config.assets.precompile << "diff_notes/diff_notes_bundle.js" config.assets.precompile << "boards/boards_bundle.js" config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js" From a2aa1abbc272b285dab12f03465f13e71fc91e15 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 15:35:38 +0200 Subject: [PATCH 039/129] Test GitLab project import for a user with only their default namespace. Refactor the spec file: - remove hardcoded record IDs - avoid top-level let if not used in all scenarios - prefer expect { ... }.to change { ... }.from(0).to(1) over checking that there are no records at the beginning of the test --- .../import_export/import_file_spec.rb | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index f32834801a0..3015576f6f8 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -3,13 +3,8 @@ require 'spec_helper' feature 'Import/Export - project import integration test', feature: true, js: true do include Select2Helper - let(:admin) { create(:admin) } - let(:normal_user) { create(:user) } - let!(:namespace) { create(:namespace, name: "asd", owner: admin) } let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } - let(:project) { Project.last } - let(:project_hook) { Gitlab::Git::Hook.new('post-receive', project.repository.path) } background do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) @@ -19,41 +14,43 @@ feature 'Import/Export - project import integration test', feature: true, js: tr FileUtils.rm_rf(export_path, secure: true) end - context 'admin user' do + context 'when selecting the namespace' do + let(:user) { create(:admin) } + let!(:namespace) { create(:namespace, name: "asd", owner: user) } + before do - login_as(admin) + login_as(user) end scenario 'user imports an exported project successfully' do - expect(Project.all.count).to be_zero - visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') fill_in :project_path, with: 'test-project-path', visible: true click_link 'GitLab export' expect(page).to have_content('GitLab project export') - expect(URI.parse(current_url).query).to eq('namespace_id=2&path=test-project-path') + expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=test-project-path") attach_file('file', file) - click_on 'Import project' # import starts + expect { click_on 'Import project' }.to change { Project.count }.from(0).to(1) + project = Project.last expect(project).not_to be_nil expect(project.issues).not_to be_empty expect(project.merge_requests).not_to be_empty - expect(project_hook).to exist - expect(wiki_exists?).to be true + expect(project_hook_exists?(project)).to be true + expect(wiki_exists?(project)).to be true expect(project.import_status).to eq('finished') end scenario 'invalid project' do - project = create(:project, namespace_id: 2) + project = create(:project, namespace: namespace) visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') fill_in :project_path, with: project.name, visible: true click_link 'GitLab export' @@ -66,11 +63,11 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end scenario 'project with no name' do - create(:project, namespace_id: 2) + create(:project, namespace: namespace) visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') # click on disabled element find(:link, 'GitLab export').trigger('click') @@ -81,24 +78,30 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end end - context 'normal user' do + context 'when limited to the default user namespace' do + let(:user) { create(:user) } before do - login_as(normal_user) + login_as(user) end - scenario 'non-admin user is allowed to import a project' do - expect(Project.all.count).to be_zero - + scenario 'passes correct namespace ID in the URL' do visit new_project_path fill_in :project_path, with: 'test-project-path', visible: true - expect(page).to have_content('GitLab export') + click_link 'GitLab export' + + expect(page).to have_content('GitLab project export') + expect(URI.parse(current_url).query).to eq("namespace_id=#{user.namespace.id}&path=test-project-path") end end - def wiki_exists? + def wiki_exists?(project) wiki = ProjectWiki.new(project) File.exist?(wiki.repository.path_to_repo) && !wiki.repository.empty? end + + def project_hook_exists?(project) + Gitlab::Git::Hook.new('post-receive', project.repository.path).exists? + end end From beddc37478113771785385a645fecf50f743d2ec Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 20:42:35 +0200 Subject: [PATCH 040/129] Fix GitLab project import when a user has access only to their default namespace. Render a hidden field with namespace ID so it can be read by JavaScript and passed to "/import/gitlab_project/new" screen. --- app/views/projects/new.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index cc8cb134fb8..399ccf15b7f 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -27,6 +27,7 @@ - else .input-group-addon.static-namespace #{root_url}#{current_user.username}/ + = f.hidden_field :namespace_id, value: current_user.namespace_id .form-group.col-xs-12.col-sm-6.project-path = f.label :namespace_id, class: 'label-light' do %span From 1bdda800b6a18237921dd0cdec60c36c0fa0153f Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 20 Oct 2016 14:15:39 -0200 Subject: [PATCH 041/129] [ci skip] Add a comment explaining validate_board_limit callback Callback associations are not common to see around. We want to make clear that the `before_add` callback uses the number before the addition, in this particular case 1. --- app/models/project.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index 6685baab699..af117f0acb0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1339,6 +1339,13 @@ class Project < ActiveRecord::Base shared_projects.any? end + # Similar to the normal callbacks that hook into the life cycle of an + # Active Record object, you can also define callbacks that get triggered + # when you add an object to an association collection. If any of these + # callbacks throw an exception, the object will not be added to the + # collection. Before you add a new board to the boards collection if you + # already have 1, 2, or n it will fail, but it if you have 0 that is lower + # than the number of permitted boards per project it won't fail. def validate_board_limit(board) raise BoardLimitExceeded, 'Number of permitted boards exceeded' if boards.size >= NUMBER_OF_PERMITTED_BOARDS end From 327ae90c6bcbbc3cc08895ece2f6cc641983a84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 20 Oct 2016 18:51:03 +0200 Subject: [PATCH 042/129] Don't use Hash#slice since it's not supported in Ruby 2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/commit_statuses.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index f282a3b9cd6..f54d4f06627 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -67,9 +67,14 @@ module API pipeline = @project.ensure_pipeline(ref, commit.sha, current_user) status = GenericCommitStatus.running_or_pending.find_or_initialize_by( - project: @project, pipeline: pipeline, - user: current_user, name: name, ref: ref) - status.attributes = declared(params).slice(:target_url, :description) + project: @project, + pipeline: pipeline, + user: current_user, + name: name, + ref: ref, + target_url: params[:target_url], + description: params[:description] + ) begin case params[:state].to_s From 813286e5ea6712039fd868310160eef8acb1c012 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 21 Oct 2016 08:39:37 +1100 Subject: [PATCH 043/129] Added item to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..128f5dec039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Fix HipChat notifications rendering (airatshigapov, eisnerd) + - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) From e7e2562e4f82b10301e899ebb0e355df6beac0e6 Mon Sep 17 00:00:00 2001 From: Jared Deckard Date: Mon, 6 Feb 2017 16:25:30 -0600 Subject: [PATCH 044/129] Position task list checkbox to match the list indent --- app/assets/stylesheets/framework/lists.scss | 10 -------- app/assets/stylesheets/framework/mixins.scss | 7 ++++++ .../stylesheets/framework/typography.scss | 25 ++++++++++++++++++- .../unreleased/22466-task-list-alignment.yml | 4 +++ 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/22466-task-list-alignment.yml diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 2bfdb9f9601..55ed4b7b06c 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -96,16 +96,6 @@ ul.unstyled-list > li { border-bottom: none; } -ul.task-list { - li.task-list-item { - list-style-type: none; - } - - ul:not(.task-list) { - padding-left: 1.3em; - } -} - // Generic content list ul.content-list { @include basic-list; diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 1acd06122a3..df78bbdea51 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -76,6 +76,13 @@ #{$property}: $value; } +/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */ +@mixin on-webkit-only { + @media screen and (-webkit-min-device-pixel-ratio:0) { + @content; + } +} + @mixin keyframes($animation-name) { @-webkit-keyframes #{$animation-name} { @content; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 54958973f15..db5e2c51fe7 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -134,7 +134,7 @@ ul, ol { padding: 0; - margin: 3px 0 3px 28px !important; + margin: 3px 0 !important; } ul:dir(rtl), @@ -144,6 +144,29 @@ li { line-height: 1.6em; + margin-left: 25px; + padding-left: 3px; + + /* Normalize the bullet position on webkit. */ + @include on-webkit-only { + margin-left: 28px; + padding-left: 0; + } + } + + ul.task-list { + li.task-list-item { + list-style-type: none; + position: relative; + padding-left: 28px; + margin-left: 0 !important; + + input.task-list-item-checkbox { + position: absolute; + left: 8px; + top: 5px; + } + } } a[href*="/uploads/"], diff --git a/changelogs/unreleased/22466-task-list-alignment.yml b/changelogs/unreleased/22466-task-list-alignment.yml new file mode 100644 index 00000000000..6e6ccb873ec --- /dev/null +++ b/changelogs/unreleased/22466-task-list-alignment.yml @@ -0,0 +1,4 @@ +--- +title: Align task list checkboxes +merge_request: 6487 +author: Jared Deckard From 905fdfba927dcfa32b99d649521a52445bc6838f Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 17 Feb 2017 07:03:42 +1100 Subject: [PATCH 045/129] Add merge request count to each issue on issues list --- .../images/icon-merge-request-unmerged.svg | 1 + app/assets/stylesheets/pages/issues.scss | 5 +++++ .../concerns/issuable_collections.rb | 20 +++++++++++++------ app/controllers/projects/issues_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/models/concerns/issuable.rb | 4 ++-- app/models/merge_requests_closing_issues.rb | 8 ++++++++ .../shared/_issuable_meta_data.html.haml | 6 ++++++ .../unreleased/add_mr_info_to_issues_list.yml | 4 ++++ 9 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 app/assets/images/icon-merge-request-unmerged.svg create mode 100644 changelogs/unreleased/add_mr_info_to_issues_list.yml diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg new file mode 100644 index 00000000000..c4d8e65122d --- /dev/null +++ b/app/assets/images/icon-merge-request-unmerged.svg @@ -0,0 +1 @@ + diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 80b0c9493d8..b595480561b 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -10,6 +10,11 @@ .issue-labels { display: inline-block; } + + .icon-merge-request-unmerged { + height: 13px; + margin-bottom: 3px; + } } } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index a6e158ebae6..d7d781cbe72 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -9,24 +9,32 @@ module IssuableCollections private - def issuable_meta_data(issuable_collection) + def issuable_meta_data(issuable_collection, collection_type) # map has to be used here since using pluck or select will # throw an error when ordering issuables by priority which inserts # a new order into the collection. # We cannot use reorder to not mess up the paginated collection. - issuable_ids = issuable_collection.map(&:id) - issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) + issuable_ids = issuable_collection.map(&:id) + issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type) + issuable_merge_requests_count = + if collection_type == 'Issue' + MergeRequestsClosingIssues.count_for_collection(issuable_ids) + else + [] + end issuable_ids.each_with_object({}) do |id, issuable_meta| downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? } - upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } - notes = issuable_note_count.find { |notes| notes.noteable_id == id } + upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } + notes = issuable_note_count.find { |notes| notes.noteable_id == id } + merge_requests = issuable_merge_requests_count.find { |mr| mr.issue_id == id } issuable_meta[id] = Issuable::IssuableMeta.new( upvotes.try(:count).to_i, downvotes.try(:count).to_i, - notes.try(:count).to_i + notes.try(:count).to_i, + merge_requests.try(:count).to_i ) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 744a4af1c51..05056ad046a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController @collection_type = "Issue" @issues = issues_collection @issues = @issues.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@issues) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) if @issues.out_of_range? && @issues.total_pages != 0 return redirect_to url_for(params.merge(page: @issues.total_pages)) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index c3e1760f168..8a7aeeaa96f 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @collection_type = "MergeRequest" @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@merge_requests) + @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 return redirect_to url_for(params.merge(page: @merge_requests.total_pages)) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5f53c48fc88..c9c6bd24d75 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -16,9 +16,9 @@ module Issuable include TimeTrackable # This object is used to gather issuable meta data for displaying - # upvotes, downvotes and notes count for issues and merge requests + # upvotes, downvotes, notes and closing merge requests count for issues and merge requests # lists avoiding n+1 queries and improving performance. - IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count) + IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count, :merge_requests_count) included do cache_markdown_field :title, pipeline: :single_line diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index ab597c37947..1ecdfd1dfdb 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -4,4 +4,12 @@ class MergeRequestsClosingIssues < ActiveRecord::Base validates :merge_request_id, uniqueness: { scope: :issue_id }, presence: true validates :issue_id, presence: true + + class << self + def count_for_collection(ids) + select('issue_id', 'COUNT(*) as count'). + group(:issue_id). + where(issue_id: ids) + end + end end diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml index 1264e524d86..66310da5cd6 100644 --- a/app/views/shared/_issuable_meta_data.html.haml +++ b/app/views/shared/_issuable_meta_data.html.haml @@ -2,6 +2,12 @@ - issue_votes = @issuable_meta_data[issuable.id] - upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes - issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes') +- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count + +- if issuable_mr > 0 + %li + = image_tag('icon-merge-request-unmerged', class: 'icon-merge-request-unmerged') + = issuable_mr - if upvotes > 0 %li diff --git a/changelogs/unreleased/add_mr_info_to_issues_list.yml b/changelogs/unreleased/add_mr_info_to_issues_list.yml new file mode 100644 index 00000000000..8087aa6296c --- /dev/null +++ b/changelogs/unreleased/add_mr_info_to_issues_list.yml @@ -0,0 +1,4 @@ +--- +title: Add merge request count to each issue on issues list +merge_request: 9252 +author: blackst0ne From 204a0865e0e69d740abcaa85a689218140678a49 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 17 Feb 2017 07:09:24 +1100 Subject: [PATCH 046/129] Add merge request count to each issue on issues list --- .../images/icon-merge-request-unmerged.svg | 1 + app/assets/stylesheets/pages/issues.scss | 5 +++++ .../concerns/issuable_collections.rb | 20 +++++++++++++------ app/controllers/projects/issues_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/models/concerns/issuable.rb | 4 ++-- app/models/merge_requests_closing_issues.rb | 8 ++++++++ .../shared/_issuable_meta_data.html.haml | 6 ++++++ .../unreleased/add_mr_info_to_issues_list.yml | 4 ++++ 9 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 app/assets/images/icon-merge-request-unmerged.svg create mode 100644 changelogs/unreleased/add_mr_info_to_issues_list.yml diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg new file mode 100644 index 00000000000..c4d8e65122d --- /dev/null +++ b/app/assets/images/icon-merge-request-unmerged.svg @@ -0,0 +1 @@ + diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 80b0c9493d8..b595480561b 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -10,6 +10,11 @@ .issue-labels { display: inline-block; } + + .icon-merge-request-unmerged { + height: 13px; + margin-bottom: 3px; + } } } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index a6e158ebae6..d7d781cbe72 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -9,24 +9,32 @@ module IssuableCollections private - def issuable_meta_data(issuable_collection) + def issuable_meta_data(issuable_collection, collection_type) # map has to be used here since using pluck or select will # throw an error when ordering issuables by priority which inserts # a new order into the collection. # We cannot use reorder to not mess up the paginated collection. - issuable_ids = issuable_collection.map(&:id) - issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) + issuable_ids = issuable_collection.map(&:id) + issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type) + issuable_merge_requests_count = + if collection_type == 'Issue' + MergeRequestsClosingIssues.count_for_collection(issuable_ids) + else + [] + end issuable_ids.each_with_object({}) do |id, issuable_meta| downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? } - upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } - notes = issuable_note_count.find { |notes| notes.noteable_id == id } + upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } + notes = issuable_note_count.find { |notes| notes.noteable_id == id } + merge_requests = issuable_merge_requests_count.find { |mr| mr.issue_id == id } issuable_meta[id] = Issuable::IssuableMeta.new( upvotes.try(:count).to_i, downvotes.try(:count).to_i, - notes.try(:count).to_i + notes.try(:count).to_i, + merge_requests.try(:count).to_i ) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 744a4af1c51..05056ad046a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController @collection_type = "Issue" @issues = issues_collection @issues = @issues.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@issues) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) if @issues.out_of_range? && @issues.total_pages != 0 return redirect_to url_for(params.merge(page: @issues.total_pages)) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 63b5bcbb586..1d286ca62e5 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @collection_type = "MergeRequest" @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@merge_requests) + @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 return redirect_to url_for(params.merge(page: @merge_requests.total_pages)) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5f53c48fc88..c9c6bd24d75 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -16,9 +16,9 @@ module Issuable include TimeTrackable # This object is used to gather issuable meta data for displaying - # upvotes, downvotes and notes count for issues and merge requests + # upvotes, downvotes, notes and closing merge requests count for issues and merge requests # lists avoiding n+1 queries and improving performance. - IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count) + IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count, :merge_requests_count) included do cache_markdown_field :title, pipeline: :single_line diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index ab597c37947..1ecdfd1dfdb 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -4,4 +4,12 @@ class MergeRequestsClosingIssues < ActiveRecord::Base validates :merge_request_id, uniqueness: { scope: :issue_id }, presence: true validates :issue_id, presence: true + + class << self + def count_for_collection(ids) + select('issue_id', 'COUNT(*) as count'). + group(:issue_id). + where(issue_id: ids) + end + end end diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml index 1264e524d86..66310da5cd6 100644 --- a/app/views/shared/_issuable_meta_data.html.haml +++ b/app/views/shared/_issuable_meta_data.html.haml @@ -2,6 +2,12 @@ - issue_votes = @issuable_meta_data[issuable.id] - upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes - issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes') +- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count + +- if issuable_mr > 0 + %li + = image_tag('icon-merge-request-unmerged', class: 'icon-merge-request-unmerged') + = issuable_mr - if upvotes > 0 %li diff --git a/changelogs/unreleased/add_mr_info_to_issues_list.yml b/changelogs/unreleased/add_mr_info_to_issues_list.yml new file mode 100644 index 00000000000..8087aa6296c --- /dev/null +++ b/changelogs/unreleased/add_mr_info_to_issues_list.yml @@ -0,0 +1,4 @@ +--- +title: Add merge request count to each issue on issues list +merge_request: 9252 +author: blackst0ne From ffd3583486f4772c8aec58b2750619d889d18d1b Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 17 Feb 2017 08:30:00 +1100 Subject: [PATCH 047/129] Added second parameter to @issuable_meta_data variables --- app/controllers/concerns/issues_action.rb | 2 +- app/controllers/concerns/merge_requests_action.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index fb5edb34370..b17c138d5c7 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -10,7 +10,7 @@ module IssuesAction .page(params[:page]) @collection_type = "Issue" - @issuable_meta_data = issuable_meta_data(@issues) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) respond_to do |format| format.html diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 6229759dcf1..d3c8e4888bc 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -9,7 +9,7 @@ module MergeRequestsAction .page(params[:page]) @collection_type = "MergeRequest" - @issuable_meta_data = issuable_meta_data(@merge_requests) + @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) end private From 27ffc2817554902c380a1101db7a0feb5816955f Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 17 Feb 2017 08:33:09 +1100 Subject: [PATCH 048/129] Add merge request count to each issue on issues list --- .../images/icon-merge-request-unmerged.svg | 1 + app/assets/stylesheets/pages/issues.scss | 5 +++++ .../concerns/issuable_collections.rb | 20 +++++++++++++------ app/controllers/concerns/issues_action.rb | 2 +- .../concerns/merge_requests_action.rb | 2 +- app/controllers/projects/issues_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/models/concerns/issuable.rb | 4 ++-- app/models/merge_requests_closing_issues.rb | 8 ++++++++ .../shared/_issuable_meta_data.html.haml | 6 ++++++ .../unreleased/add_mr_info_to_issues_list.yml | 4 ++++ 11 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 app/assets/images/icon-merge-request-unmerged.svg create mode 100644 changelogs/unreleased/add_mr_info_to_issues_list.yml diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg new file mode 100644 index 00000000000..c4d8e65122d --- /dev/null +++ b/app/assets/images/icon-merge-request-unmerged.svg @@ -0,0 +1 @@ + diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 80b0c9493d8..b595480561b 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -10,6 +10,11 @@ .issue-labels { display: inline-block; } + + .icon-merge-request-unmerged { + height: 13px; + margin-bottom: 3px; + } } } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index a6e158ebae6..d7d781cbe72 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -9,24 +9,32 @@ module IssuableCollections private - def issuable_meta_data(issuable_collection) + def issuable_meta_data(issuable_collection, collection_type) # map has to be used here since using pluck or select will # throw an error when ordering issuables by priority which inserts # a new order into the collection. # We cannot use reorder to not mess up the paginated collection. - issuable_ids = issuable_collection.map(&:id) - issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) + issuable_ids = issuable_collection.map(&:id) + issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type) + issuable_merge_requests_count = + if collection_type == 'Issue' + MergeRequestsClosingIssues.count_for_collection(issuable_ids) + else + [] + end issuable_ids.each_with_object({}) do |id, issuable_meta| downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? } - upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } - notes = issuable_note_count.find { |notes| notes.noteable_id == id } + upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } + notes = issuable_note_count.find { |notes| notes.noteable_id == id } + merge_requests = issuable_merge_requests_count.find { |mr| mr.issue_id == id } issuable_meta[id] = Issuable::IssuableMeta.new( upvotes.try(:count).to_i, downvotes.try(:count).to_i, - notes.try(:count).to_i + notes.try(:count).to_i, + merge_requests.try(:count).to_i ) end end diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index fb5edb34370..b17c138d5c7 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -10,7 +10,7 @@ module IssuesAction .page(params[:page]) @collection_type = "Issue" - @issuable_meta_data = issuable_meta_data(@issues) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) respond_to do |format| format.html diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 6229759dcf1..d3c8e4888bc 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -9,7 +9,7 @@ module MergeRequestsAction .page(params[:page]) @collection_type = "MergeRequest" - @issuable_meta_data = issuable_meta_data(@merge_requests) + @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) end private diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 744a4af1c51..05056ad046a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController @collection_type = "Issue" @issues = issues_collection @issues = @issues.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@issues) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) if @issues.out_of_range? && @issues.total_pages != 0 return redirect_to url_for(params.merge(page: @issues.total_pages)) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 63b5bcbb586..1d286ca62e5 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @collection_type = "MergeRequest" @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@merge_requests) + @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 return redirect_to url_for(params.merge(page: @merge_requests.total_pages)) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5f53c48fc88..c9c6bd24d75 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -16,9 +16,9 @@ module Issuable include TimeTrackable # This object is used to gather issuable meta data for displaying - # upvotes, downvotes and notes count for issues and merge requests + # upvotes, downvotes, notes and closing merge requests count for issues and merge requests # lists avoiding n+1 queries and improving performance. - IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count) + IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count, :merge_requests_count) included do cache_markdown_field :title, pipeline: :single_line diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index ab597c37947..1ecdfd1dfdb 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -4,4 +4,12 @@ class MergeRequestsClosingIssues < ActiveRecord::Base validates :merge_request_id, uniqueness: { scope: :issue_id }, presence: true validates :issue_id, presence: true + + class << self + def count_for_collection(ids) + select('issue_id', 'COUNT(*) as count'). + group(:issue_id). + where(issue_id: ids) + end + end end diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml index 1264e524d86..66310da5cd6 100644 --- a/app/views/shared/_issuable_meta_data.html.haml +++ b/app/views/shared/_issuable_meta_data.html.haml @@ -2,6 +2,12 @@ - issue_votes = @issuable_meta_data[issuable.id] - upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes - issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes') +- issuable_mr = @issuable_meta_data[issuable.id].merge_requests_count + +- if issuable_mr > 0 + %li + = image_tag('icon-merge-request-unmerged', class: 'icon-merge-request-unmerged') + = issuable_mr - if upvotes > 0 %li diff --git a/changelogs/unreleased/add_mr_info_to_issues_list.yml b/changelogs/unreleased/add_mr_info_to_issues_list.yml new file mode 100644 index 00000000000..8087aa6296c --- /dev/null +++ b/changelogs/unreleased/add_mr_info_to_issues_list.yml @@ -0,0 +1,4 @@ +--- +title: Add merge request count to each issue on issues list +merge_request: 9252 +author: blackst0ne From 32b59a1fa7f439cccbf0de29094c3fab3ec518a8 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 18 Feb 2017 11:47:56 +1100 Subject: [PATCH 049/129] Added specs --- spec/features/issuables/issuable_list_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index e31bc40adc3..13ea9ce853c 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -30,6 +30,12 @@ describe 'issuable list', feature: true do end end + it "counts merge requests closing issues icons for each issue" do + visit_issuable_list(:issue) + + expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1) + end + def visit_issuable_list(issuable_type) if issuable_type == :issue visit namespace_project_issues_path(project.namespace, project) @@ -42,6 +48,13 @@ describe 'issuable list', feature: true do 3.times do if issuable_type == :issue issuable = create(:issue, project: project, author: user) + merge_request = create(:merge_request, + title: FFaker::Lorem.sentence, + description: "Closes #{issuable.to_reference}", + source_project: project, + source_branch: FFaker::Name.name) + + MergeRequestsClosingIssues.create!(issue: issuable, merge_request: merge_request) else issuable = create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) end From c21df05066a3a9351db649ebc3f00f4bee63cce8 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 18 Feb 2017 11:49:13 +1100 Subject: [PATCH 050/129] Remove empty line in note --- app/views/projects/notes/_note.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index a68a09baf4f..1b08165c14c 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -63,7 +63,6 @@ = icon('pencil', class: 'link-highlight') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do = icon('trash-o', class: 'danger-highlight') - .note-body{ class: note_editable ? 'js-task-list-container' : '' } .note-text.md = preserve do From 24ba7585e6da5ee8881ff8b4db53558940cb0c23 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 18 Feb 2017 12:58:24 +1100 Subject: [PATCH 051/129] Fixed rubocop offenses --- spec/features/issuables/issuable_list_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 13ea9ce853c..ce4dca1175f 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -30,11 +30,11 @@ describe 'issuable list', feature: true do end end - it "counts merge requests closing issues icons for each issue" do - visit_issuable_list(:issue) + it "counts merge requests closing issues icons for each issue" do + visit_issuable_list(:issue) - expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1) - end + expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1) + end def visit_issuable_list(issuable_type) if issuable_type == :issue From b2f1c2b8be610695cf0523ae93524a1f53831c44 Mon Sep 17 00:00:00 2001 From: Rudi Kramer Date: Mon, 20 Feb 2017 11:14:40 +0000 Subject: [PATCH 052/129] Update two_factor_authentication.md by correcting FreeOTP link. --- doc/user/profile/account/two_factor_authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index a23ad79ae1d..eaa39a0c4ea 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -213,5 +213,5 @@ your GitLab server's time is synchronized via a service like NTP. Otherwise, you may have cases where authorization always fails because of time differences. [Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en -[FreeOTP]: https://fedorahosted.org/freeotp/ +[FreeOTP]: https://freeotp.github.io/ [YubiKey]: https://www.yubico.com/products/yubikey-hardware/ From 95ed01749e0ffe55a979acb246ca8e0ae663bffb Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Sat, 11 Feb 2017 20:15:01 +0100 Subject: [PATCH 053/129] Add AsciiDoc snippet for CI/CD Badges This commit adds CI/CD Badges Snippets for AsciiDoc as requested in #26087. I've however run into an issue in highlighting the snippet, it seems as if AsciiDoc is currently not being highlighted properly (displayed as plaintext) Add testcase for to_asciidoc Update test case for Badges list --- app/views/projects/pipelines_settings/_badge.html.haml | 7 +++++++ .../unreleased/26087-asciidoc-cicd-badges-snippet.yml | 4 ++++ lib/gitlab/badge/metadata.rb | 4 ++++ spec/features/projects/badges/list_spec.rb | 6 ++++-- spec/lib/gitlab/badge/shared/metadata.rb | 10 ++++++++++ 5 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml diff --git a/app/views/projects/pipelines_settings/_badge.html.haml b/app/views/projects/pipelines_settings/_badge.html.haml index 22a3b884520..43bbd735059 100644 --- a/app/views/projects/pipelines_settings/_badge.html.haml +++ b/app/views/projects/pipelines_settings/_badge.html.haml @@ -25,3 +25,10 @@ HTML .col-md-10.code.js-syntax-highlight = highlight('.html', badge.to_html) + .row + %hr + .row + .col-md-2.text-center + AsciiDoc + .col-md-10.code.js-syntax-highlight + = highlight('.adoc', badge.to_asciidoc) diff --git a/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml b/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml new file mode 100644 index 00000000000..799c5277207 --- /dev/null +++ b/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml @@ -0,0 +1,4 @@ +--- +title: Added AsciiDoc Snippet to CI/CD Badges +merge_request: 9164 +author: Jan Christophersen diff --git a/lib/gitlab/badge/metadata.rb b/lib/gitlab/badge/metadata.rb index 548f85b78bb..4a049ef758d 100644 --- a/lib/gitlab/badge/metadata.rb +++ b/lib/gitlab/badge/metadata.rb @@ -20,6 +20,10 @@ module Gitlab "[![#{title}](#{image_url})](#{link_url})" end + def to_asciidoc + "image:#{image_url}[link=\"#{link_url}\",title=\"#{title}\"]" + end + def title raise NotImplementedError end diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb index 67a4a5d1ab1..ae9db0c0d6e 100644 --- a/spec/features/projects/badges/list_spec.rb +++ b/spec/features/projects/badges/list_spec.rb @@ -14,7 +14,8 @@ feature 'list of badges' do expect(page).to have_content 'build status' expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' - expect(page).to have_css('.highlight', count: 2) + expect(page).to have_content 'AsciiDoc' + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='build status']") page.within('.highlight', match: :first) do @@ -28,7 +29,8 @@ feature 'list of badges' do expect(page).to have_content 'coverage report' expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' - expect(page).to have_css('.highlight', count: 2) + expect(page).to have_content 'AsciiDoc' + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='coverage report']") page.within('.highlight', match: :first) do diff --git a/spec/lib/gitlab/badge/shared/metadata.rb b/spec/lib/gitlab/badge/shared/metadata.rb index 0cf18514251..63c7ca5a915 100644 --- a/spec/lib/gitlab/badge/shared/metadata.rb +++ b/spec/lib/gitlab/badge/shared/metadata.rb @@ -18,4 +18,14 @@ shared_examples 'badge metadata' do it { is_expected.to include metadata.image_url } it { is_expected.to include metadata.link_url } end + + describe '#to_asciidoc' do + subject { metadata.to_asciidoc } + + it { is_expected.to include metadata.image_url } + it { is_expected.to include metadata.link_url } + it { is_expected.to include 'image:' } + it { is_expected.to include 'link=' } + it { is_expected.to include 'title=' } + end end From 6c373e718161da77fa1ec02065307641f6e55003 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 15:19:19 -0300 Subject: [PATCH 054/129] add index file to Pages docs --- doc/pages/index.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 doc/pages/index.md diff --git a/doc/pages/index.md b/doc/pages/index.md new file mode 100644 index 00000000000..3590cf5cfb1 --- /dev/null +++ b/doc/pages/index.md @@ -0,0 +1,39 @@ +# All you need to know about GitLab Pages + +## Product + +- [Product Webpage](https://pages.gitlab.io) +- [We're Bringing GitLab Pages to CE](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/) +- [Pages Group - Templates](https://gitlab.com/pages) + +## Getting Started + +- Comprehensive Step-by-Step Guide: [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) +- GitLab Pages from A to Z + - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](https://gitlab.com/marcia/drafts/blob/master/pages/pages_part-1.md) + - [Part 2: Quick Start Guide - Setting Up GitLab Pages](https://gitlab.com/marcia/drafts/blob/master/pages/pages_part-2.md) + - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](#LINK) + - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from scratch](#LINK) + - [Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](#LINK) +- Secure GitLab Pages Custom Domain with SSL/TLS Certificates + - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) + - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) + - [StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/) +- Static Site Generators - Blog Posts Series + - [SSGs Part 1: Static vs Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) + - [SSGs Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) + - [SSGs Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) +- [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) + +## Advanced Use + +- Blog Posts: + - [GitLab CI: Run jobs Sequentially, in Parallel, or Build a Custom Pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) + - [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) + - [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) + - [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) + +## General Documentation + +- [User docs](../user/project/pages/) +- [Admin docs](administration.html) From a2b7f8d1410bf97772a03c38df1396cce795b983 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 16:14:48 -0300 Subject: [PATCH 055/129] add pages guide part 1 - static sites, domains, DNS, SSL/TLS --- doc/pages/index.md | 2 +- ...omains_dns_records_ssl_tls_certificates.md | 159 ++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md diff --git a/doc/pages/index.md b/doc/pages/index.md index 3590cf5cfb1..c3dd08b46a7 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -10,7 +10,7 @@ - Comprehensive Step-by-Step Guide: [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) - GitLab Pages from A to Z - - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](https://gitlab.com/marcia/drafts/blob/master/pages/pages_part-1.md) + - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html) - [Part 2: Quick Start Guide - Setting Up GitLab Pages](https://gitlab.com/marcia/drafts/blob/master/pages/pages_part-2.md) - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](#LINK) - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from scratch](#LINK) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md new file mode 100644 index 00000000000..f76e02471e9 --- /dev/null +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -0,0 +1,159 @@ +# GitLab Pages from A to Z + +> Type: user guide +> +> Level: beginner + +- **Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates** +- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ + +---- + +This is a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. + +To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](#LINK). + +> For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. + +## What you need to know before getting started + +Before we begin, let's understand a few concepts first. + +### Static Sites + +GitLab Pages only supports static websites, meaning, your output files must be HTML, CSS, and JavaScript only. + +To create your static site, you can either hardcode in HTML, CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/) to simplify your code and build the static site for you, which is highly recommendable and much faster than hardcoding. + +#### Further Reading + +- Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) +- Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site +- You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) +- Fork an [example project](https://gitlab.com/pages) to build your website based upon + +### GitLab Pages Domain + +If you set up a GitLab Pages project on GitLab.com, it will automatically be accessible under a [subdomain of `.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). The `` is defined by your username on GitLab.com, or the group name you created this project under. + +> Note: If you use your own GitLab instance to deploy your site with GitLab Pages, check with your sysadmin what's your Pages wildcard domain. This guide is valid for any GitLab instance, you just need to replace Pages wildcard domain on GitLab.com (`*.gitlab.io`) with your own. + +#### Practical examples + +**Project Websites:** + +- You created a project called `blog` under your username `john`, therefore your project URL is `https://gitlab.com/john/blog/`. Once you enable GitLab Pages for this project, and build your site, it will be available under `https://john.gitlab.io/blog/`. +- You created a group for all your websites called `websites`, and a project within this group is called `blog`. Your project URL is `https://gitlab.com/websites/blog/`. Once you enable GitLab Pages for this project, the site will live under `https://websites.gitlab.io/blog/`. + +**User and Group Websites:** + +- Under your username, `john`, you created a project called `john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`. Once you enable GitLab Pages for your project, your website will be published under `https://john.gitlab.io`. +- Under your group `websites`, you created a project called `websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project, your website will be published under `https://websites.gitlab.io`. + +**General example:** + +- On GitLab.com, a project site will always be available under `https://namespace.gitlab.io/project-name` +- On GitLab.com, a user or group website will be available under `https://namespace.gitlab.io/` +- On your GitLab instance, replace `gitlab.io` above with your Pages server domain. Ask your sysadmin for this information. + +### DNS Records + +A Domain Name System (DNS) web service routes visitors to websites by translating domain names (such as `www.example.com`) into the numeric IP addresses (such as `192.0.2.1`) that computers use to connect to each other. + +A DNS record is created to point a (sub)domain to a certain location, which can be an IP address or another domain. In case you want to use GitLab Pages with your own (sub)domain, you need to access your domain's registrar control panel to add a DNS record pointing it back to your GitLab Pages site. + +Note that **how to** add DNS records depends on which server your domain is hosted on. Every control panel has its own place to do it. If you are not an admin of your domain, and don't have access to your registrar, you'll need to ask for the technical support of your hosting service to do it for you. + +To help you out, we've gathered some instructions on how to do that for the most popular hosting services: + +- [Amazon](http://docs.aws.amazon.com/gettingstarted/latest/swh/getting-started-configure-route53.html) +- [Bluehost](https://my.bluehost.com/cgi/help/559) +- [CloudFlare](https://support.cloudflare.com/hc/en-us/articles/200169096-How-do-I-add-A-records-) +- [cPanel](https://documentation.cpanel.net/display/ALD/Edit+DNS+Zone) +- [DreamHost](https://help.dreamhost.com/hc/en-us/articles/215414867-How-do-I-add-custom-DNS-records-) +- [Go Daddy](https://www.godaddy.com/help/add-an-a-record-19238) +- [Hostgator](http://support.hostgator.com/articles/changing-dns-records) +- [Inmotion hosting](https://my.bluehost.com/cgi/help/559) +- [Media Temple](https://mediatemple.net/community/products/dv/204403794/how-can-i-change-the-dns-records-for-my-domain) +- [Microsoft](https://msdn.microsoft.com/en-us/library/bb727018.aspx) + +If your hosting service is not listed above, you can just try to search the web for "how to add dns record on ". + +#### DNS A record + +In case you want to point a root domain (`example.com`) to your GitLab Pages site, deployed to `namespace.gitlab.io`, you need to log into your domain's admin control panel and add a DNS `A` record pointing your domain to Pages' server IP address. For projects on GitLab.com, this IP is `104.208.235.32`. For projects leaving in other GitLab instances (CE or EE), please contact your sysadmin asking for this information (which IP address is Pages server running on your instance). + +**Practical Example:** + +![DNS A record pointing to GitLab.com Pages server]() + +#### DNS CNAME record + +In case you want to point a subdomain (`hello-world.example.com`) to your GitLab Pages site initially deployed to `namespace.gitlab.io`, you need to log into your domain's admin control panel and add a DNS `CNAME` record pointing your subdomain to your website URL (`namespace.gitlab.io`) address. + +Notice that, despite it's a user or project website, the `CNAME` should point to your Pages domain (`namespace.gitlab.io`), without any `/project-name`. + +**Practical Example:** + +![DNS CNAME record pointing to GitLab.com project]() + +#### TL;DR + +| From | DNS Record | To | +| ---- | ---------- | -- | +| domain.com | A | 104.208.235.32 | +| subdomain.domain.com | CNAME | namespace.gitlab.io | + +> **Notes**: +> +> - **Do not** use a CNAME record if you want to point your `domain.com` to your GitLab Pages site. Use an `A` record instead. +> - **Do not** add any special chars after the default Pages domain. E.g., **do not** point your `subdomain.domain.com` to `namespace.gitlab.io.` or `namespace.gitlab.io/`. + +### SSL/TLS Certificates + +Every GitLab Pages project on GitLab.com will be available under HTTPS for the default Pages domain (`*.gitlab.io`). Once you set up your Pages project with your custom (sub)domain, if you want it secured by HTTPS, you will have to issue a certificate for that (sub)domain and install it on your project. + +> Note: certificates are NOT required to add to your custom (sub)domain on your GitLab Pages project, though they are highly recommendable. + +The importance of having any website securely served under HTTPS is explained on the introductory section of the blog post [Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview). + +The reason why certificates are so important is that they encrypt the connection between the **client** (you, me, your visitors) and the **server** (where you site lives), through a keychain of authentications and validations. + +### Issuing Certificates + +GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by [Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) and self-signed certificates. Of course, [you'd rather issue a certificate than generate a self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate), for security reasons and for having browsers trusting your site's certificate. + +There are several different kinds of certificates, each one with certain security level. A static personal website will not require the same security level as an online banking web app, for instance. There are a couple Certificate Authorities that offer free certificates, aiming to make the internet more secure to everyone. The most popular is [Let's Encrypt](https://letsencrypt.org/), which issues certificates trusted by most of browsers, it's open source, and free to use. Please read through this tutorial to understand [how to secure your GitLab Pages website with Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/). + +With the same popularity, there are [certificates issued by CloudFlare](https://www.cloudflare.com/ssl/), which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-free-cdn-and-you/). Their certs are valid up to 15 years. Read through the tutorial on [how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/). + +### Adding certificates to your project + +Regardless the CA you choose, the steps to add your certificate to your Pages project are the same. + +#### What do you need + +1. A PEM certificate +1. An intermediary certificate +1. A public key + +![Pages project - adding certificates]() + +These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**. + +#### What's what? + +- A PEM certificate is the certificate generated by the CA, which needs to be added to the field **Certificate (PEM)**. +- An [intermediary certificate][] (aka "root certificate") is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. +- A public key is an encrypted key which validates your PEM against your domain. + +#### Now what? + +Now that you hopefully understand why you need all of this, it's simple: + +- Your PEM certificate needs to be added to the first field +- If your certificate is missing its intermediary, copy and paste the root certificate (usually available from your CA website) and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/), just jumping a line between them. +- Copy your public key and paste it in the last field + +> Note: **do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). From abb668f1e4ad82cd44d0d6c175a9dbee23e98f64 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 20 Feb 2017 13:54:29 -0600 Subject: [PATCH 056/129] remove require.context from boards_bundle --- .../javascripts/boards/boards_bundle.js.es6 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 878ad1b6031..f6b75ac6196 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -2,15 +2,19 @@ /* global Vue */ /* global BoardService */ -function requireAll(context) { return context.keys().map(context); } - window.Vue = require('vue'); window.Vue.use(require('vue-resource')); -requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./mixins', true, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./filters', true, /^\.\/.*\.(js|es6)$/)); +require('./models/issue'); +require('./models/label'); +require('./models/list'); +require('./models/milestone'); +require('./models/user'); +require('./stores/boards_store'); +require('./stores/modal_store'); +require('./services/board_service'); +require('./mixins/modal_mixins'); +require('./mixins/sortable_default_options'); +require('./filters/due_date_filters'); require('./components/board'); require('./components/board_sidebar'); require('./components/new_list_dropdown'); From 6d9edfa60bf3f3fbf0aa7cb8c4dd9755effcdeef Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 17:03:22 -0300 Subject: [PATCH 057/129] add closing section: further reading --- ...tatic_sites_domains_dns_records_ssl_tls_certificates.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index f76e02471e9..797e7d28469 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -1,4 +1,4 @@ -# GitLab Pages from A to Z +# GitLab Pages from A to Z: Part 1 > Type: user guide > @@ -157,3 +157,8 @@ Now that you hopefully understand why you need all of this, it's simple: - Copy your public key and paste it in the last field > Note: **do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). + +## Further Reading + +- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ +- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ From 41e36f2c0fc21a63bdc51383bbc3be03cb3497cf Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 20 Feb 2017 14:04:02 -0600 Subject: [PATCH 058/129] remove require.context from cycle_analytics_bundle --- .../cycle_analytics_bundle.js.es6 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 index 1ac715aab77..411ac7b24b2 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 @@ -4,10 +4,20 @@ window.Vue = require('vue'); window.Cookies = require('js-cookie'); - -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('.', true, /^\.\/(?!cycle_analytics_bundle).*\.(js|es6)$/)); +require('./svg/icon_branch'); +require('./svg/icon_build_status'); +require('./svg/icon_commit'); +require('./components/stage_code_component'); +require('./components/stage_issue_component'); +require('./components/stage_plan_component'); +require('./components/stage_production_component'); +require('./components/stage_review_component'); +require('./components/stage_staging_component'); +require('./components/stage_test_component'); +require('./components/total_time_component'); +require('./cycle_analytics_service'); +require('./cycle_analytics_store'); +require('./default_event_objects'); $(() => { const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; From dec96c248d44b8f6f5615b7bcdb2ce9ad63ff32a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 20 Feb 2017 14:11:43 -0600 Subject: [PATCH 059/129] remove unneeded eslint exceptions --- app/assets/javascripts/boards/boards_bundle.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index f6b75ac6196..0c5177ced2f 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -1,4 +1,4 @@ -/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren, import/newline-after-import, no-multi-spaces, max-len */ +/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */ /* global Vue */ /* global BoardService */ From 0aa685658319d837c090def9604cc2fba1dd5cf5 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 17:13:45 -0300 Subject: [PATCH 060/129] add part 2: quick start guide --- doc/pages/pages_quick_start_guide.md | 112 +++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/pages/pages_quick_start_guide.md diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/pages_quick_start_guide.md new file mode 100644 index 00000000000..804b34a8e75 --- /dev/null +++ b/doc/pages/pages_quick_start_guide.md @@ -0,0 +1,112 @@ +# GitLab Pages from A to Z: Part 2 + +> Type: user guide +> +> Level: beginner + +- **Part 2: Quick Start Guide - Setting Up GitLab Pages** +- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ + +---- + +## Setting Up GitLab Pages + +For a complete step-by-step tutorial, please read the blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain what do you need and why do you need them. + + + +### What You Need to Get Started + +1. A project +1. A configuration file (`.gitlab-ci.yml`) to deploy your site +1. A specific `job` called `pages` in the configuration file that will make GitLab aware that you are deploying a GitLab Pages website + +#### Optional Features + +1. A custom domain or subdomain +1. A DNS pointing your (sub)domain to your Pages site + 1. **Optional**: an SSL/TLS certificate so your custom domain is accessible under HTTPS. + +### Project + +Your GitLab Pages project is a regular project created the same way you do for the other ones. To get started with GitLab Pages, you have two ways: + +- Fork one of the templates from Page Examples, or +- Create a new project from scratch + +Let's go over both options. + +#### Fork a Project to Get Started From + +To make things easy for you, we've created this [group](https://gitlab.com/pages) of default projects containing the most popular SSGs templates. + +Watch the [video tutorial](#LINK) we've created for the steps below. + +1. Choose your SSG template +1. Fork a project from the [Pages group](https://gitlab.com/pages) +1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project** + + ![remove fork relashionship]() + +1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **CI/CD Pipelines** +1. Trigger a build (push a change to any file) +1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your **Project**'s **Settings** > **Pages** + +To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to: + +- Rename it to `namespace.gitlab.io`: navigate to **Project**'s **Settings** > **Edit Project** > **Rename repository** +- Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file. + +> **Notes:** +> +>1. Why do I need to remove the fork relationship? +> +> Unless you want to contribute to the original project, you won't need it connected to the upstream. A [fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork) is useful for submitting merge requests to the upstream. +> +> 2. Why do I need to enable Shared Runners? +> +> Shared Runners will run the script set by your GitLab CI configuration file. They're enabled by default to new projects, but not to forks. + +#### Create a Project from Scratch + +1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the [examples above](#practical-examples). +1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. +1. From the your **Project**'s page, click **Set up CI**: + + ![add new file]() + +1. Choose one of the templates from the dropbox menu. Pick up the template corresponding to the SSG you're using (or plain HTML). + + ![gitlab-ci templates]() + +Once you have both site files and `.gitlab-ci.yml` in your project's root, GitLab CI will build your site and deploy it with Pages. Once the first build passes, you see your site is live by navigating to your **Project**'s **Settings** > **Pages**, where you'll find its default URL. + +> **Notes:** +> +> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) to our examples. +> +> - The second step _"Clone it to your local computer"_, can be done differently, achieving the same results: instead of cloning the bare repository to you local computer and moving your site files into it, you can run `git init` in your local website directory, add the remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, then add, commit, and push. + +### URLs and Baseurls + + + +Every Static Site Generator (SSG) default configuration expects to find your website under a (sub)domain (`example.com`), not in a subdirectory of that domain (`example.com/subdir`). Therefore, whenever you publish a project website (`namespace.gitlab.io/project-name`), you'll have to look for this configuration (base URL) on your SSG's documentation and set it up to reflect this pattern. + +For example, for a Jekyll site, the `baseurl` is defined in the Jekyll configuration file, `_config.yml`. If your website URL is `https://john.gitlab.io/blog/`, you need to add this line to `_config.yml`: + +```yaml +baseurl: "/blog" +``` + +On the contrary, if you deploy your website after forking one of our [default examples](https://gitlab.com/pages), the baseurl will already be configured this way, as all examples there are project websites. If you decide to make yours a user or group website, you'll have to remove this configuration from your project. For the Jekyll example we've just mentioned, you'd have to change Jekyll's `_config.yml` to: + +```yaml +baseurl: "" +``` + +## Further Reading + +- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ +- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ From 8ba08213a16aaa9d00b5d7df308212550d90d28e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 20 Feb 2017 14:29:09 -0600 Subject: [PATCH 061/129] remove require.context from diff_notes_bundle --- .../diff_notes/diff_notes_bundle.js.es6 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 index 190461451d5..cadf8b96b87 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 @@ -1,14 +1,18 @@ -/* eslint-disable func-names, comma-dangle, new-cap, no-new, import/newline-after-import, no-multi-spaces, max-len */ +/* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */ /* global Vue */ /* global ResolveCount */ -function requireAll(context) { return context.keys().map(context); } const Vue = require('vue'); -requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/)); -requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/)); +require('./models/discussion'); +require('./models/note'); +require('./stores/comments'); +require('./services/resolve'); +require('./mixins/discussion'); +require('./components/comment_resolve_btn'); +require('./components/jump_to_discussion'); +require('./components/resolve_btn'); +require('./components/resolve_count'); +require('./components/resolve_discussion_btn'); $(() => { const projectPath = document.querySelector('.merge-request').dataset.projectPath; From 2a7e2a6447842a0ab152a791b93fe7b980bc7973 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 17:34:54 -0300 Subject: [PATCH 062/129] fix links --- doc/pages/pages_quick_start_guide.md | 4 ++-- ...s_static_sites_domains_dns_records_ssl_tls_certificates.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/pages_quick_start_guide.md index 804b34a8e75..f7b0c3c9520 100644 --- a/doc/pages/pages_quick_start_guide.md +++ b/doc/pages/pages_quick_start_guide.md @@ -6,7 +6,7 @@ - **Part 2: Quick Start Guide - Setting Up GitLab Pages** - _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ ---- @@ -109,4 +109,4 @@ baseurl: "" ## Further Reading - Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ +- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index 797e7d28469..bb6268bacb9 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -6,7 +6,7 @@ - **Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates** - _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ -- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ ---- @@ -161,4 +161,4 @@ Now that you hopefully understand why you need all of this, it's simple: ## Further Reading - Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ -- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html)_ +- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ From 0b1636daf69fc5eea8dfc5f9cd6426c84ee5e3db Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 17:35:31 -0300 Subject: [PATCH 063/129] add part 3: creating and tweaking CI for Pages --- doc/pages/index.md | 4 +- .../pages_creating_and_tweaking_gitlab-ci.md | 279 ++++++++++++++++++ 2 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 doc/pages/pages_creating_and_tweaking_gitlab-ci.md diff --git a/doc/pages/index.md b/doc/pages/index.md index c3dd08b46a7..047fe0477cf 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -11,10 +11,10 @@ - Comprehensive Step-by-Step Guide: [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) - GitLab Pages from A to Z - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html) - - [Part 2: Quick Start Guide - Setting Up GitLab Pages](https://gitlab.com/marcia/drafts/blob/master/pages/pages_part-2.md) + - [Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html) - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](#LINK) - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from scratch](#LINK) - - [Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](#LINK) + - [Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html) - Secure GitLab Pages Custom Domain with SSL/TLS Certificates - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) diff --git a/doc/pages/pages_creating_and_tweaking_gitlab-ci.md b/doc/pages/pages_creating_and_tweaking_gitlab-ci.md new file mode 100644 index 00000000000..8e1877c4981 --- /dev/null +++ b/doc/pages/pages_creating_and_tweaking_gitlab-ci.md @@ -0,0 +1,279 @@ +# GitLab Pages from A to Z: Part 3 + +> Type: user guide +> +> Level: intermediate + +- **Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages** +- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ +- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ + +---- + +### Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages + +[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves inumerous purposes, to build, test, and deploy your app from GitLab through [Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) methods. You will need it to build your website with GitLab Pages, and deploy it to the Pages server. + +What this file actually does is telling the [GitLab Runner](https://docs.gitlab.com/runner/) to run scripts as you would do from the command line. The Runner acts as your terminal. GitLab CI tells the Runner which commands to run. Both are built-in in GitLab, and you don't need to set up anything for them to work. + +Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) and GitLab Runner is out of the scope of this guide, but we'll need to understand just a few things to be able to write our own `.gitlab-ci.yml` or tweak an existing one. It's an [Yaml](http://docs.ansible.com/ansible/YAMLSyntax.html) file, with its own syntax. You can always check your CI syntax with the [GitLab CI Lint Tool](https://gitlab.com/ci/lint). + +**Practical Example:** + +Let's consider you have a [Jekyll](https://jekyllrb.com/) site. To build it locally, you would open your terminal, and run `jekyll build`. Of course, before building it, you had to install Jekyll in your computer. For that, you had to open your terminal and run `gem install jekyll`. Right? GitLab CI + GitLab Runner do the same thing. But you need to write in the `.gitlab-ci.yml` the script you want to run so the GitLab Runner will do it for you. It looks more complicated then it is. What you need to tell the Runner: + +``` +$ gem install jekyll +$ jekyll build +``` + +#### Script + +To transpose this script to Yaml, it would be like this: + +```yaml +script: + - gem install jekyll + - jekyll build +``` + +#### Job + +So far so good. Now, each `script`, in GitLab is organized by a `job`, which is a bunch of scripts and settings you want to apply to that specific task. + +```yaml +job: + script: + - gem install jekyll + - jekyll build +``` + +For GitLab Pages, this `job` has a specific name, called `pages`, which tells the Runner you want that task to deploy your website with GitLab Pages: + +```yaml +pages: + script: + - gem install jekyll + - jekyll build +``` + +#### `public` Dir + +We also need to tell Jekyll where do you want the website to build, and GitLab Pages will only consider files in a directory called `public`. To do that with Jekyll, we need to add a flag specifying the [destination (`-d`)](https://jekyllrb.com/docs/usage/) of the built website: `jekyll build -d public`. Of course, we need to tell this to our Runner: + +```yaml +pages: + script: + - gem install jekyll + - jekyll build -d public +``` + +#### Artifacts + +We also need to tell the Runner that this _job_ generates _artifacts_, which is the site built by Jekyll. Where are these artifacts stored? In the `public` directory: + +```yaml +pages: + script: + - gem install jekyll + - jekyll build -d public + artifacts: + paths: + - public +``` + +The script above would be enough to build your Jekyll site with GitLab Pages. But, from Jekyll 3.4.0 on, its default template originated by `jekyll new project` requires [Bundler](http://bundler.io/) to install Jekyll dependencies and the default theme. To adjust our script to meet these new requirements, we only need to install and build Jekyll with Bundler: + +```yaml +pages: + script: + - bundle install + - bundle exec jekyll build -d public + artifacts: + paths: + - public +``` + +That's it! A `.gitlab-ci.yml` with the content above would deploy your Jekyll 3.4.0 site with GitLab Pages. This is the minimum configuration for our example. On the steps below, we'll refine the script by adding extra options to our GitLab CI. + +#### Image + +At this point, you probably ask yourself: "okay, but to install Jekyll I need Ruby. Where is Ruby on that script?". The answer is simple: the first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a [Docker](https://www.docker.com/) image specifying what do you need in your container to run that script: + +```yaml +image: ruby:2.3 + +pages: + script: + - bundle install + - bundle exec jekyll build -d public + artifacts: + paths: + - public +``` + +In this case, you're telling the Runner to pull this image, which contains Ruby 2.3 as part of its file system. When you don't specify this image in your configuration, the Runner will use a default image, which is Ruby 2.1. + +If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll need to specify which image you want to use, and this image should contain NodeJS as part of its file system. E.g., for a [Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`. + +> Note: we're not trying to explain what a Docker image is, we just need to introduce the concept with a minimum viable explanation. To know more about Docker images, please visit their website or take a look at a [summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here. + +Let's go a little further. + +#### Branching + +If you use GitLab as a version control platform, you will have your branching strategy to work on your project. Meaning, you will have other branches in your project, but you'll want only pushes to the default branch (usually `master`) to be deployed to your website. To do that, we need to add another line to our CI, telling the Runner to only perform that _job_ called `pages` on the `master` branch `only`: + +```yaml +image: ruby:2.3 + +pages: + script: + - bundle install + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master +``` + +#### Stages + +Another interesting concept to keep in mind are build stages. Your web app can pass through a lot of tests and other tasks until it's deployed to staging or production environments. There are three default stages on GitLab CI: build, test, and deploy. To specify which stage your _job_ is running, simply add another line to your CI: + +```yaml +image: ruby:2.3 + +pages: + stage: deploy + script: + - bundle install + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master +``` + +You might ask yourself: "why should I bother with stages at all?" Well, let's say you want to be able to test your script and check the built site before deploying your site to production. You want to run the test exactly as your script will do when you push to `master`. It's simple, let's add another task (_job_) to our CI, telling it to test every push to other branches, `except` the `master` branch: + +```yaml +image: ruby:2.3 + +pages: + stage: deploy + script: + - bundle install + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master + +test: + stage: test + script: + - bundle install + - bundle exec jekyll build -d test + artifacts: + paths: + - test + except: + - master +``` + +The `test` job is running on the stage `test`, Jekyll will build the site in a directory called `test`, and this job will affect all the branches except `master`. + +The best benefit of applying _stages_ to different _jobs_ is that every job in the same stage builds in parallel. So, if your web app needs more than one test before being deployed, you can run all your test at the same time, it's not necessary to wait on test to finish to run the other. Of course, this is just a brief introduction of GitLab CI and GitLab Runner, which are tools much more powerful than that. This is what you need to be able to create and tweak your builds for your GitLab Pages site. + +#### Before Script + +To avoid running the same script multiple times across your _jobs_, you can add the parameter `before_script`, in which you specify which commands you want to run for every single _job_. In our example, notice that we run `bundle install` for both jobs, `pages` and `test`. We don't need to repeat it: + +```yaml +image: ruby:2.3 + +before_script: + - bundle install + +pages: + stage: deploy + script: + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master + +test: + stage: test + script: + - bundle exec jekyll build -d test + artifacts: + paths: + - test + except: + - master +``` + +#### Caching Dependencies + +If you want to cache the installation files for your projects dependencies, for building faster, you can use the parameter `cache`. For this example, we'll cache Jekyll dependencies in a `vendor` directory when we run `bundle install`: + +```yaml +image: ruby:2.3 + +cache: + paths: + - vendor/ + +before_script: + - bundle install --path vendor + +pages: + stage: deploy + script: + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master + +test: + stage: test + script: + - bundle exec jekyll build -d test + artifacts: + paths: + - test + except: + - master +``` + +For this specific case, we need to exclude `/vendor` from Jekyll `_config.yml` file, otherwise Jekyll will understand it as a regular directory to build together with the site: + +```yml +exclude: + - vendor +``` + +There we go! Now our GitLab CI not only builds our website, but also **continuously test** pushes to feature-branches, **caches** dependencies installed with Bundler, and **continuously deploy** every push to the `master` branch. + +### Advanced GitLab CI for GitLab Pages + +What you can do with GitLab CI is pretty much up to your creativity. Once you get used to it, you start creating awesome scripts that automate most of tasks you'd do manually in the past. Read through the [documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) to understand how to go even further on your scripts. + +- On this blog post, understand the concept of [using GitLab CI `environments` to deploy your web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/). +- On this post, learn [how to run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) +- On this blog post, we go through the process of [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) to deploy this website you're looking at, docs.gitlab.com. +- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/). + +## Further Reading + +- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ +- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ From 625fc65b98a7c346afa7aa689566af33c02ff799 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 19:01:05 -0300 Subject: [PATCH 064/129] add link to Matija's video --- ...ges_static_sites_domains_dns_records_ssl_tls_certificates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index bb6268bacb9..56ad4805eda 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -12,7 +12,7 @@ This is a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. -To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](#LINK). +To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](https://youtu.be/b_qjiabYhmQ). > For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. From 2a12cbe6d6d5c7c78c6fac64e7d5a6af6742462a Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Tue, 21 Feb 2017 12:07:50 +1100 Subject: [PATCH 065/129] Improved specs --- spec/features/issuables/issuable_list_spec.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index ce4dca1175f..4ea801cd1ac 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -33,6 +33,7 @@ describe 'issuable list', feature: true do it "counts merge requests closing issues icons for each issue" do visit_issuable_list(:issue) + expect(page).to have_selector('.icon-merge-request-unmerged', count: 1) expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1) end @@ -48,13 +49,6 @@ describe 'issuable list', feature: true do 3.times do if issuable_type == :issue issuable = create(:issue, project: project, author: user) - merge_request = create(:merge_request, - title: FFaker::Lorem.sentence, - description: "Closes #{issuable.to_reference}", - source_project: project, - source_branch: FFaker::Name.name) - - MergeRequestsClosingIssues.create!(issue: issuable, merge_request: merge_request) else issuable = create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) end @@ -65,6 +59,16 @@ describe 'issuable list', feature: true do create(:award_emoji, :downvote, awardable: issuable) create(:award_emoji, :upvote, awardable: issuable) + + if issuable_type == :issue + issue = Issue.reorder(:iid).first + merge_request = create(:merge_request, + title: FFaker::Lorem.sentence, + source_project: project, + source_branch: FFaker::Name.name) + + MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) if MergeRequestsClosingIssues.count.zero? + end end end end From 1eb72a71f54da310b2277e5890dce27c15e11036 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Tue, 21 Feb 2017 12:45:08 +1100 Subject: [PATCH 066/129] Refactored count_for_collection() for using pluck instead of select --- app/controllers/concerns/issuable_collections.rb | 4 ++-- app/models/merge_requests_closing_issues.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index d7d781cbe72..85ae4985e58 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -28,13 +28,13 @@ module IssuableCollections downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? } upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? } notes = issuable_note_count.find { |notes| notes.noteable_id == id } - merge_requests = issuable_merge_requests_count.find { |mr| mr.issue_id == id } + merge_requests = issuable_merge_requests_count.find { |mr| mr.first == id } issuable_meta[id] = Issuable::IssuableMeta.new( upvotes.try(:count).to_i, downvotes.try(:count).to_i, notes.try(:count).to_i, - merge_requests.try(:count).to_i + merge_requests.try(:last).to_i ) end end diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index 1ecdfd1dfdb..97210900bd5 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -7,9 +7,9 @@ class MergeRequestsClosingIssues < ActiveRecord::Base class << self def count_for_collection(ids) - select('issue_id', 'COUNT(*) as count'). - group(:issue_id). - where(issue_id: ids) + group(:issue_id). + where(issue_id: ids). + pluck('issue_id', 'COUNT(*) as count') end end end From 2807cafe41de14d343f1c97edecd2e531e0f1fd2 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 23:05:52 -0300 Subject: [PATCH 067/129] add video tutorial - Pages admin --- doc/pages/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/pages/index.md b/doc/pages/index.md index 047fe0477cf..11b2582841f 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -37,3 +37,4 @@ - [User docs](../user/project/pages/) - [Admin docs](administration.html) + - Video tutorial - [How to Enable GitLab Pages for GitLab CE and EE](https://youtu.be/dD8c7WNcc6s) From b90a1656e486deddb29dbc4bbfce3f663f7484bc Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Mon, 20 Feb 2017 23:06:00 -0300 Subject: [PATCH 068/129] update link --- ...ges_static_sites_domains_dns_records_ssl_tls_certificates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index 56ad4805eda..1a4e798df66 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -12,7 +12,7 @@ This is a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. -To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](https://youtu.be/b_qjiabYhmQ). +To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). > For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. From 7e648110ed09bd12e1d405fbeba8dcb0590de602 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 10 Feb 2017 16:13:13 +0000 Subject: [PATCH 069/129] Added tooltip to add issues button on issue boards Closes #27985 --- .../javascripts/boards/boards_bundle.js.es6 | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 878ad1b6031..d194dc9b188 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -93,17 +93,53 @@ $(() => { modal: ModalStore.store, store: Store.state, }, + watch: { + disabled() { + this.updateTooltip(); + }, + }, computed: { disabled() { return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length; }, + tooltipTitle() { + if (this.disabled) { + return 'Please add a list to your board first'; + } + + return ''; + }, + }, + methods: { + updateTooltip() { + const $tooltip = $(this.$el); + + this.$nextTick(() => { + if (this.disabled) { + $tooltip.tooltip(); + } else { + $tooltip.tooltip('destroy'); + } + }); + }, + openModal() { + if (!this.disabled) { + this.toggleModal(true); + } + }, + }, + mounted() { + this.updateTooltip(); }, template: ` `, From 216d3ad7cba15016ffd038645fe0db0d91ce84f3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 10 Feb 2017 16:19:00 +0000 Subject: [PATCH 070/129] Added CHANGELOG --- changelogs/unreleased/add-issues-tooltip.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/add-issues-tooltip.yml diff --git a/changelogs/unreleased/add-issues-tooltip.yml b/changelogs/unreleased/add-issues-tooltip.yml new file mode 100644 index 00000000000..58adb6c6b5a --- /dev/null +++ b/changelogs/unreleased/add-issues-tooltip.yml @@ -0,0 +1,4 @@ +--- +title: Disabled tooltip on add issues button in usse boards +merge_request: +author: From 8ed25c80003b327b359079d600f7207db46846b4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 10 Feb 2017 16:38:11 +0000 Subject: [PATCH 071/129] Added tests --- spec/features/boards/add_issues_modal_spec.rb | 6 ++++++ spec/features/boards/boards_spec.rb | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb index 2875fc1e533..a3e24bb5ffa 100644 --- a/spec/features/boards/add_issues_modal_spec.rb +++ b/spec/features/boards/add_issues_modal_spec.rb @@ -49,6 +49,12 @@ describe 'Issue Boards add issue modal', :feature, :js do expect(page).not_to have_selector('.add-issues-modal') end + + it 'does not show tooltip on add issues button' do + button = page.find('.issue-boards-search button', text: 'Add issues') + + expect(button[:title]).not_to eq("Please add a list to your board first") + end end context 'issues list' do diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 1b25b51cfb2..e247bfa2980 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -28,10 +28,10 @@ describe 'Issue Boards', feature: true, js: true do expect(page).to have_content('Welcome to your Issue Board!') end - it 'disables add issues button by default' do + it 'shows tooltip on add issues button' do button = page.find('.issue-boards-search button', text: 'Add issues') - expect(button[:disabled]).to eq true + expect(button[:"data-original-title"]).to eq("Please add a list to your board first") end it 'hides the blank state when clicking nevermind button' do From 63bd79f594272d98ce022685f26a47ce0afae9d4 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 21 Feb 2017 09:53:44 +0100 Subject: [PATCH 072/129] Chat slash commands show labels correctly --- .../unreleased/zj-fix-slash-command-labels.yml | 4 ++++ lib/gitlab/chat_commands/presenters/issue_base.rb | 2 +- .../chat_commands/presenters/issue_show_spec.rb | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/zj-fix-slash-command-labels.yml diff --git a/changelogs/unreleased/zj-fix-slash-command-labels.yml b/changelogs/unreleased/zj-fix-slash-command-labels.yml new file mode 100644 index 00000000000..93b7194dd4e --- /dev/null +++ b/changelogs/unreleased/zj-fix-slash-command-labels.yml @@ -0,0 +1,4 @@ +--- +title: Chat slash commands show labels correctly +merge_request: +author: diff --git a/lib/gitlab/chat_commands/presenters/issue_base.rb b/lib/gitlab/chat_commands/presenters/issue_base.rb index a0058407fb2..054f7f4be0c 100644 --- a/lib/gitlab/chat_commands/presenters/issue_base.rb +++ b/lib/gitlab/chat_commands/presenters/issue_base.rb @@ -32,7 +32,7 @@ module Gitlab }, { title: "Labels", - value: @resource.labels.any? ? @resource.label_names : "_None_", + value: @resource.labels.any? ? @resource.label_names.join(', ') : "_None_", short: true } ] diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb index 5b678d31fce..3916fc704a4 100644 --- a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb +++ b/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb @@ -26,6 +26,21 @@ describe Gitlab::ChatCommands::Presenters::IssueShow do end end + context 'with labels' do + let(:label) { create(:label, project: project, title: 'mep') } + let(:label1) { create(:label, project: project, title: 'mop') } + + before do + issue.labels << [label, label1] + end + + it 'shows the labels' do + labels = attachment[:fields].find { |f| f[:title] == 'Labels' } + + expect(labels[:value]).to eq("mep, mop") + end + end + context 'confidential issue' do let(:issue) { create(:issue, project: project) } From 71270f80dd42e524a904a3c19f23217e8766e282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Nov=C3=BD?= Date: Thu, 25 Aug 2016 21:56:32 +0000 Subject: [PATCH 073/129] UI: Allow a project variable to be set to an empty value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/views/projects/variables/_form.html.haml | 2 +- .../lnovy-gitlab-ce-empty-variables.yml | 4 ++ spec/features/variables_spec.rb | 44 ++++++++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml diff --git a/app/views/projects/variables/_form.html.haml b/app/views/projects/variables/_form.html.haml index a5bae83e0ce..1ae86d258af 100644 --- a/app/views/projects/variables/_form.html.haml +++ b/app/views/projects/variables/_form.html.haml @@ -6,5 +6,5 @@ = f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true .form-group = f.label :value, "Value", class: "label-light" - = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true + = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE" = f.submit btn_text, class: "btn btn-save" diff --git a/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml b/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml new file mode 100644 index 00000000000..bd5db5ac7af --- /dev/null +++ b/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml @@ -0,0 +1,4 @@ +--- +title: 'UI: Allow a project variable to be set to an empty value' +merge_request: 6044 +author: Lukáš Nový diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index 9a4bc027004..a07eba2f01e 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Project variables', js: true do let(:user) { create(:user) } let(:project) { create(:project) } - let(:variable) { create(:ci_variable, key: 'test') } + let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') } before do login_as(user) @@ -16,16 +16,28 @@ describe 'Project variables', js: true do it 'shows list of variables' do page.within('.variables-table') do expect(page).to have_content(variable.key) + expect(page).to have_content(variable.value) end end it 'adds new variable' do - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'key value') + fill_in('variable_key', with: 'new_key') + fill_in('variable_value', with: 'new value') click_button('Add new variable') page.within('.variables-table') do - expect(page).to have_content('key') + expect(page).to have_content('new_key') + expect(page).to have_content('new value') + end + end + + it 'adds empty variable' do + fill_in('variable_key', with: 'new_key') + fill_in('variable_value', with: '') + click_button('Add new variable') + + page.within('.variables-table') do + expect(page).to have_content('new_key') end end @@ -68,12 +80,30 @@ describe 'Project variables', js: true do end expect(page).to have_content('Update variable') - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'key value') + fill_in('variable_key', with: 'new_key') + fill_in('variable_value', with: 'new value') click_button('Save variable') page.within('.variables-table') do - expect(page).to have_content('key') + expect(page).not_to have_content(variable.key) + expect(page).not_to have_content(variable.value) + expect(page).to have_content('new_key') + expect(page).to have_content('new value') + end + end + + it 'edits variable with empty value' do + page.within('.variables-table') do + find('.btn-variable-edit').click + end + + expect(page).to have_content('Update variable') + fill_in('variable_value', with: '') + click_button('Save variable') + + page.within('.variables-table') do + expect(page).to have_content(variable.key) + expect(page).not_to have_content(variable.value) end end end From 55f2425a678b61178d46e50f2b5a2da929228f52 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 17 Feb 2017 10:45:05 +0100 Subject: [PATCH 074/129] API: Make subscription API more RESTfuL --- .../unreleased/api-subscription-restful.yml | 4 + doc/api/v3_to_v4.md | 1 + lib/api/api.rb | 1 + lib/api/subscriptions.rb | 4 +- lib/api/v3/subscriptions.rb | 53 ++++++++++++ spec/requests/api/issues_spec.rb | 22 ++--- spec/requests/api/labels_spec.rb | 24 +++--- spec/requests/api/merge_requests_spec.rb | 22 ++--- spec/requests/api/v3/labels_spec.rb | 82 +++++++++++++++++++ 9 files changed, 177 insertions(+), 36 deletions(-) create mode 100644 changelogs/unreleased/api-subscription-restful.yml create mode 100644 lib/api/v3/subscriptions.rb diff --git a/changelogs/unreleased/api-subscription-restful.yml b/changelogs/unreleased/api-subscription-restful.yml new file mode 100644 index 00000000000..95db470e6c9 --- /dev/null +++ b/changelogs/unreleased/api-subscription-restful.yml @@ -0,0 +1,4 @@ +--- +title: 'API: - Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource.' +merge_request: 9325 +author: Robert Schilling diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 1af124c56b1..e24ee0da204 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -29,4 +29,5 @@ changes are in V4: - Return pagination headers for all endpoints that return an array - Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` +- Make subscription API more RESTful. Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. - Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) diff --git a/lib/api/api.rb b/lib/api/api.rb index e729c07f8c3..2e51be9fff3 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -17,6 +17,7 @@ module API mount ::API::V3::Projects mount ::API::V3::ProjectSnippets mount ::API::V3::Repositories + mount ::API::V3::Subscriptions mount ::API::V3::SystemHooks mount ::API::V3::Tags mount ::API::V3::Todos diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb index e11d7537cc9..acf11dbdf26 100644 --- a/lib/api/subscriptions.rb +++ b/lib/api/subscriptions.rb @@ -21,7 +21,7 @@ module API desc 'Subscribe to a resource' do success entity_class end - post ":id/#{type}/:subscribable_id/subscription" do + post ":id/#{type}/:subscribable_id/subscribe" do resource = instance_exec(params[:subscribable_id], &finder) if resource.subscribed?(current_user, user_project) @@ -35,7 +35,7 @@ module API desc 'Unsubscribe from a resource' do success entity_class end - delete ":id/#{type}/:subscribable_id/subscription" do + post ":id/#{type}/:subscribable_id/unsubscribe" do resource = instance_exec(params[:subscribable_id], &finder) if !resource.subscribed?(current_user, user_project) diff --git a/lib/api/v3/subscriptions.rb b/lib/api/v3/subscriptions.rb new file mode 100644 index 00000000000..02a4157c26e --- /dev/null +++ b/lib/api/v3/subscriptions.rb @@ -0,0 +1,53 @@ +module API + module V3 + class Subscriptions < Grape::API + before { authenticate! } + + subscribable_types = { + 'merge_request' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, + 'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, + 'issues' => proc { |id| find_project_issue(id) }, + 'labels' => proc { |id| find_project_label(id) }, + } + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :subscribable_id, type: String, desc: 'The ID of a resource' + end + resource :projects do + subscribable_types.each do |type, finder| + type_singularized = type.singularize + entity_class = ::API::Entities.const_get(type_singularized.camelcase) + + desc 'Subscribe to a resource' do + success entity_class + end + post ":id/#{type}/:subscribable_id/subscription" do + resource = instance_exec(params[:subscribable_id], &finder) + + if resource.subscribed?(current_user, user_project) + not_modified! + else + resource.subscribe(current_user, user_project) + present resource, with: entity_class, current_user: current_user, project: user_project + end + end + + desc 'Unsubscribe from a resource' do + success entity_class + end + delete ":id/#{type}/:subscribable_id/subscription" do + resource = instance_exec(params[:subscribable_id], &finder) + + if !resource.subscribed?(current_user, user_project) + not_modified! + else + resource.unsubscribe(current_user, user_project) + present resource, with: entity_class, current_user: current_user, project: user_project + end + end + end + end + end + end +end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index ece1b43567d..774a8a1946f 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1232,55 +1232,55 @@ describe API::Issues, api: true do end end - describe 'POST :id/issues/:issue_id/subscription' do + describe 'POST :id/issues/:issue_id/subscribe' do it 'subscribes to an issue' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user2) expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user) expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do - post api("/projects/#{project.id}/issues/123/subscription", user) + post api("/projects/#{project.id}/issues/123/subscribe", user) expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do - post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) + post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscribe", non_member) expect(response).to have_http_status(404) end end - describe 'DELETE :id/issues/:issue_id/subscription' do + describe 'POST :id/issues/:issue_id/unsubscribe' do it 'unsubscribes from an issue' do - delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user2) expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do - delete api("/projects/#{project.id}/issues/123/subscription", user) + post api("/projects/#{project.id}/issues/123/unsubscribe", user) expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do - delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) + post api("/projects/#{project.id}/issues/#{confidential_issue.id}/unsubscribe", non_member) expect(response).to have_http_status(404) end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 5d7a76cf3be..566d11bba57 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -318,10 +318,10 @@ describe API::Labels, api: true do end end - describe "POST /projects/:id/labels/:label_id/subscription" do + describe "POST /projects/:id/labels/:label_id/subscribe" do context "when label_id is a label title" do it "subscribes to the label" do - post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.title}/subscribe", user) expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) @@ -331,7 +331,7 @@ describe API::Labels, api: true do context "when label_id is a label ID" do it "subscribes to the label" do - post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user) expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) @@ -343,7 +343,7 @@ describe API::Labels, api: true do before { label1.subscribe(user, project) } it "returns 304" do - post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user) expect(response).to have_http_status(304) end @@ -351,21 +351,21 @@ describe API::Labels, api: true do context "when label ID is not found" do it "returns 404 error" do - post api("/projects/#{project.id}/labels/1234/subscription", user) + post api("/projects/#{project.id}/labels/1234/subscribe", user) expect(response).to have_http_status(404) end end end - describe "DELETE /projects/:id/labels/:label_id/subscription" do + describe "POST /projects/:id/labels/:label_id/unsubscribe" do before { label1.subscribe(user, project) } context "when label_id is a label title" do it "unsubscribes from the label" do - delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.title}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -373,9 +373,9 @@ describe API::Labels, api: true do context "when label_id is a label ID" do it "unsubscribes from the label" do - delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -385,7 +385,7 @@ describe API::Labels, api: true do before { label1.unsubscribe(user, project) } it "returns 304" do - delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user) expect(response).to have_http_status(304) end @@ -393,7 +393,7 @@ describe API::Labels, api: true do context "when label ID is not found" do it "returns 404 error" do - delete api("/projects/#{project.id}/labels/1234/subscription", user) + post api("/projects/#{project.id}/labels/1234/unsubscribe", user) expect(response).to have_http_status(404) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index f4dee4a4ca1..c125df8b90b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -662,22 +662,22 @@ describe API::MergeRequests, api: true do end end - describe 'POST :id/merge_requests/:merge_request_id/subscription' do + describe 'POST :id/merge_requests/:merge_request_id/subscribe' do it 'subscribes to a merge request' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", admin) expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user) expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do - post api("/projects/#{project.id}/merge_requests/123/subscription", user) + post api("/projects/#{project.id}/merge_requests/123/subscribe", user) expect(response).to have_http_status(404) end @@ -686,28 +686,28 @@ describe API::MergeRequests, api: true do guest = create(:user) project.team << [guest, :guest] - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", guest) expect(response).to have_http_status(403) end end - describe 'DELETE :id/merge_requests/:merge_request_id/subscription' do + describe 'POST :id/merge_requests/:merge_request_id/unsubscribe' do it 'unsubscribes from a merge request' do - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", admin) expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do - post api("/projects/#{project.id}/merge_requests/123/subscription", user) + post api("/projects/#{project.id}/merge_requests/123/unsubscribe", user) expect(response).to have_http_status(404) end @@ -716,7 +716,7 @@ describe API::MergeRequests, api: true do guest = create(:user) project.team << [guest, :guest] - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", guest) expect(response).to have_http_status(403) end diff --git a/spec/requests/api/v3/labels_spec.rb b/spec/requests/api/v3/labels_spec.rb index 18e2c0d40c8..bcb0c6b9449 100644 --- a/spec/requests/api/v3/labels_spec.rb +++ b/spec/requests/api/v3/labels_spec.rb @@ -67,4 +67,86 @@ describe API::V3::Labels, api: true do expect(priority_label_response['subscribed']).to be_falsey end end + + describe "POST /projects/:id/labels/:label_id/subscription" do + context "when label_id is a label title" do + it "subscribes to the label" do + post v3_api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + + expect(response).to have_http_status(201) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_truthy + end + end + + context "when label_id is a label ID" do + it "subscribes to the label" do + post v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(201) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_truthy + end + end + + context "when user is already subscribed to label" do + before { label1.subscribe(user, project) } + + it "returns 304" do + post v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(304) + end + end + + context "when label ID is not found" do + it "returns 404 error" do + post v3_api("/projects/#{project.id}/labels/1234/subscription", user) + + expect(response).to have_http_status(404) + end + end + end + + describe "DELETE /projects/:id/labels/:label_id/subscription" do + before { label1.subscribe(user, project) } + + context "when label_id is a label title" do + it "unsubscribes from the label" do + delete v3_api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + + expect(response).to have_http_status(200) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_falsey + end + end + + context "when label_id is a label ID" do + it "unsubscribes from the label" do + delete v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(200) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_falsey + end + end + + context "when user is already unsubscribed from label" do + before { label1.unsubscribe(user, project) } + + it "returns 304" do + delete v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(304) + end + end + + context "when label ID is not found" do + it "returns 404 error" do + delete v3_api("/projects/#{project.id}/labels/1234/subscription", user) + + expect(response).to have_http_status(404) + end + end + end end From 4f115a090a931c3999f6b51d9fa027165b96b618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 21 Feb 2017 18:48:22 +0100 Subject: [PATCH 075/129] Fix specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/features/variables_spec.rb | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index a07eba2f01e..a362d6fd3b6 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -16,18 +16,17 @@ describe 'Project variables', js: true do it 'shows list of variables' do page.within('.variables-table') do expect(page).to have_content(variable.key) - expect(page).to have_content(variable.value) end end it 'adds new variable' do - fill_in('variable_key', with: 'new_key') - fill_in('variable_value', with: 'new value') + fill_in('variable_key', with: 'key') + fill_in('variable_value', with: 'key value') click_button('Add new variable') + expect(page).to have_content('Variables were successfully updated.') page.within('.variables-table') do - expect(page).to have_content('new_key') - expect(page).to have_content('new value') + expect(page).to have_content('key') end end @@ -36,6 +35,7 @@ describe 'Project variables', js: true do fill_in('variable_value', with: '') click_button('Add new variable') + expect(page).to have_content('Variables were successfully updated.') page.within('.variables-table') do expect(page).to have_content('new_key') end @@ -80,16 +80,12 @@ describe 'Project variables', js: true do end expect(page).to have_content('Update variable') - fill_in('variable_key', with: 'new_key') - fill_in('variable_value', with: 'new value') + fill_in('variable_key', with: 'key') + fill_in('variable_value', with: 'key value') click_button('Save variable') - page.within('.variables-table') do - expect(page).not_to have_content(variable.key) - expect(page).not_to have_content(variable.value) - expect(page).to have_content('new_key') - expect(page).to have_content('new value') - end + expect(page).to have_content('Variable was successfully updated.') + expect(project.variables.first.value).to eq('key value') end it 'edits variable with empty value' do @@ -101,9 +97,7 @@ describe 'Project variables', js: true do fill_in('variable_value', with: '') click_button('Save variable') - page.within('.variables-table') do - expect(page).to have_content(variable.key) - expect(page).not_to have_content(variable.value) - end + expect(page).to have_content('Variable was successfully updated.') + expect(project.variables.first.value).to eq('') end end From c468ad75f4dba1833b139e7949dc0fa0c75c74dc Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 21 Feb 2017 13:20:38 -0600 Subject: [PATCH 076/129] Keep right padding the same whether sidebar is open or not --- app/assets/stylesheets/framework/sidebar.scss | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index d09b1c9d7f5..040a7ce0c16 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -29,14 +29,16 @@ } } +@media (min-width: $screen-sm-min) { + .content-wrapper { + padding-right: $gutter_collapsed_width; + } +} + .right-sidebar-collapsed { padding-right: 0; @media (min-width: $screen-sm-min) { - .content-wrapper { - padding-right: $gutter_collapsed_width; - } - .merge-request-tabs-holder.affix { right: $gutter_collapsed_width; } @@ -54,12 +56,6 @@ .right-sidebar-expanded { padding-right: 0; - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - &:not(.build-sidebar):not(.wiki-sidebar) { - padding-right: $gutter_collapsed_width; - } - } - @media (min-width: $screen-md-min) { .content-wrapper { padding-right: $gutter_width; From 0b2fc0557f00753a552600da6e5c13d5617afaee Mon Sep 17 00:00:00 2001 From: Dmitriy Volkov Date: Sat, 11 Feb 2017 16:56:53 +0300 Subject: [PATCH 077/129] docs(ci/docker_build): Add example of variable use --- doc/ci/docker/using_docker_build.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 28141cced3b..174fa8393e4 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -298,6 +298,30 @@ push to the Registry connected to your project. Its password is provided in the `$CI_BUILD_TOKEN` variable. This allows you to automate building and deployment of your Docker images. +You can also make use of [other variables](../variables/README.md) to avoid hardcoding: + +```yaml +services: + - docker:dind + +variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_BUILD_REF_NAME + +before_script: + - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY + +build: + stage: build + script: + - docker build -t $IMAGE_TAG . + - docker push $IMAGE_TAG +``` + +Here, `$CI_REGISTRY_IMAGE` would be resolved to the address of the registry tied +to this project, and `$CI_BUILD_REF_NAME` would be resolved to the branch or +tag name for this particular job. We also declare our own variable, `$IMAGE_TAG`, +combining the two to save us some typing in the `script` section. + Here's a more elaborate example that splits up the tasks into 4 pipeline stages, including two tests that run in parallel. The build is stored in the container registry and used by subsequent stages, downloading the image From c79bbe26ab1e16facddaf159d4d074937586d742 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Thu, 2 Feb 2017 12:24:30 -0200 Subject: [PATCH 078/129] Change branch_name param to branch throughout V4 API --- app/services/files/multi_service.rb | 8 +- ...32-rename-branch-name-params-to-branch.yml | 4 + doc/api/branches.md | 4 +- doc/api/commits.md | 4 +- doc/api/repository_files.md | 24 +- doc/api/v3_to_v4.md | 6 + lib/api/api.rb | 2 + lib/api/branches.rb | 6 +- lib/api/commits.rb | 7 +- lib/api/files.rb | 8 +- lib/api/v3/commits.rb | 205 +++++++ lib/api/v3/files.rb | 138 +++++ spec/requests/api/branches_spec.rb | 16 +- spec/requests/api/commits_spec.rb | 20 +- spec/requests/api/files_spec.rb | 8 +- spec/requests/api/v3/commits_spec.rb | 578 ++++++++++++++++++ spec/requests/api/v3/files_spec.rb | 270 ++++++++ 17 files changed, 1255 insertions(+), 53 deletions(-) create mode 100644 changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml create mode 100644 lib/api/v3/commits.rb create mode 100644 lib/api/v3/files.rb create mode 100644 spec/requests/api/v3/commits_spec.rb create mode 100644 spec/requests/api/v3/files_spec.rb diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index 6ba868df04d..af6da5b9d56 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -55,7 +55,7 @@ module Files file_path = action[:file_path] file_path = action[:previous_path] if action[:action] == :move - blob = repository.blob_at_branch(params[:branch_name], file_path) + blob = repository.blob_at_branch(params[:branch], file_path) unless blob raise_error("File to be #{action[:action]}d `#{file_path}` does not exist.") @@ -89,7 +89,7 @@ module Files def validate_create(action) return if project.empty_repo? - if repository.blob_at_branch(params[:branch_name], action[:file_path]) + if repository.blob_at_branch(params[:branch], action[:file_path]) raise_error("Your changes could not be committed because a file with the name `#{action[:file_path]}` already exists.") end end @@ -102,14 +102,14 @@ module Files raise_error("You must supply the original file path when moving file `#{action[:file_path]}`.") end - blob = repository.blob_at_branch(params[:branch_name], action[:file_path]) + blob = repository.blob_at_branch(params[:branch], action[:file_path]) if blob raise_error("Move destination `#{action[:file_path]}` already exists.") end if action[:content].nil? - blob = repository.blob_at_branch(params[:branch_name], action[:previous_path]) + blob = repository.blob_at_branch(params[:branch], action[:previous_path]) blob.load_all_data!(repository) if blob.truncated? params[:actions][index][:content] = blob.data end diff --git a/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml b/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml new file mode 100644 index 00000000000..028923b83cf --- /dev/null +++ b/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml @@ -0,0 +1,4 @@ +--- +title: Standardize branch name params as branch on V4 API +merge_request: 8936 +author: diff --git a/doc/api/branches.md b/doc/api/branches.md index 5eaa8d2e920..765ca439720 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -193,11 +193,11 @@ POST /projects/:id/repository/branches | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | -| `branch_name` | string | yes | The name of the branch | +| `branch` | string | yes | The name of the branch | | `ref` | string | yes | The branch name or commit SHA to create branch from | ```bash -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches?branch_name=newbranch&ref=master" +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches?branch=newbranch&ref=master" ``` Example response: diff --git a/doc/api/commits.md b/doc/api/commits.md index eaab3e0df3d..18bc2873678 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -69,7 +69,7 @@ POST /projects/:id/repository/commits | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME | -| `branch_name` | string | yes | The name of a branch | +| `branch` | string | yes | The name of a branch | | `commit_message` | string | yes | Commit message | | `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. | | `author_email` | string | no | Specify the commit author's email address | @@ -87,7 +87,7 @@ POST /projects/:id/repository/commits ```bash PAYLOAD=$(cat << 'JSON' { - "branch_name": "master", + "branch": "master", "commit_message": "some commit message", "actions": [ { diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index dbb3c1113e8..677e209ccd9 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -46,22 +46,22 @@ POST /projects/:id/repository/files ``` ```bash -curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch_name=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file' +curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file' ``` Example response: ```json { - "file_path": "app/project.rb", - "branch_name": "master" + "file_name": "app/project.rb", + "branch": "master" } ``` Parameters: - `file_path` (required) - Full path to new file. Ex. lib/class.rb -- `branch_name` (required) - The name of branch +- `branch` (required) - The name of branch - `encoding` (optional) - Change encoding to 'base64'. Default is text. - `author_email` (optional) - Specify the commit author's email address - `author_name` (optional) - Specify the commit author's name @@ -75,22 +75,22 @@ PUT /projects/:id/repository/files ``` ```bash -curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch_name=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file' +curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file' ``` Example response: ```json { - "file_path": "app/project.rb", - "branch_name": "master" + "file_name": "app/project.rb", + "branch": "master" } ``` Parameters: - `file_path` (required) - Full path to file. Ex. lib/class.rb -- `branch_name` (required) - The name of branch +- `branch` (required) - The name of branch - `encoding` (optional) - Change encoding to 'base64'. Default is text. - `author_email` (optional) - Specify the commit author's email address - `author_name` (optional) - Specify the commit author's name @@ -113,22 +113,22 @@ DELETE /projects/:id/repository/files ``` ```bash -curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch_name=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file' +curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v3/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file' ``` Example response: ```json { - "file_path": "app/project.rb", - "branch_name": "master" + "file_name": "app/project.rb", + "branch": "master" } ``` Parameters: - `file_path` (required) - Full path to file. Ex. lib/class.rb -- `branch_name` (required) - The name of branch +- `branch` (required) - The name of branch - `author_email` (optional) - Specify the commit author's email address - `author_name` (optional) - Specify the commit author's name - `commit_message` (required) - Commit message diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 1af124c56b1..b72d9325bb4 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -30,3 +30,9 @@ changes are in V4: - Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` - Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) +- Renamed param `branch_name` to `branch` on the following endpoints + - POST `:id/repository/branches` + - POST `:id/repository/commits` + - POST/PUT/DELETE `:id/repository/files` +- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response + diff --git a/lib/api/api.rb b/lib/api/api.rb index e729c07f8c3..98692408204 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -7,7 +7,9 @@ module API version 'v3', using: :path do mount ::API::V3::Boards mount ::API::V3::Branches + mount ::API::V3::Commits mount ::API::V3::DeployKeys + mount ::API::V3::Files mount ::API::V3::Issues mount ::API::V3::Labels mount ::API::V3::Members diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 9d1f5a28ef6..c65de90cca2 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -97,13 +97,13 @@ module API success Entities::RepoBranch end params do - requires :branch_name, type: String, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' requires :ref, type: String, desc: 'Create branch from commit sha or existing branch' end post ":id/repository/branches" do authorize_push_project result = CreateBranchService.new(user_project, current_user). - execute(params[:branch_name], params[:ref]) + execute(params[:branch], params[:ref]) if result[:status] == :success present result[:branch], @@ -126,7 +126,7 @@ module API if result[:status] == :success { - branch_name: params[:branch] + branch: params[:branch] } else render_api_error!(result[:message], result[:return_code]) diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 3b314c89c6e..0cd817f9352 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -41,7 +41,7 @@ module API detail 'This feature was introduced in GitLab 8.13' end params do - requires :branch_name, type: String, desc: 'The name of branch' + requires :branch, type: String, desc: 'The name of branch' requires :commit_message, type: String, desc: 'Commit message' requires :actions, type: Array[Hash], desc: 'Actions to perform in commit' optional :author_email, type: String, desc: 'Author email for commit' @@ -50,9 +50,8 @@ module API post ":id/repository/commits" do authorize! :push_code, user_project - attrs = declared_params - attrs[:start_branch] = attrs[:branch_name] - attrs[:target_branch] = attrs[:branch_name] + attrs = declared_params.merge(start_branch: declared_params[:branch], target_branch: declared_params[:branch]) + attrs[:actions].map! do |action| action[:action] = action[:action].to_sym action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') diff --git a/lib/api/files.rb b/lib/api/files.rb index 6e16ccd2fd8..500f9d3c787 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -4,8 +4,8 @@ module API def commit_params(attrs) { file_path: attrs[:file_path], - start_branch: attrs[:branch_name], - target_branch: attrs[:branch_name], + start_branch: attrs[:branch], + target_branch: attrs[:branch], commit_message: attrs[:commit_message], file_content: attrs[:content], file_content_encoding: attrs[:encoding], @@ -17,13 +17,13 @@ module API def commit_response(attrs) { file_path: attrs[:file_path], - branch_name: attrs[:branch_name] + branch: attrs[:branch] } end params :simple_file_params do requires :file_path, type: String, desc: 'The path to new file. Ex. lib/class.rb' - requires :branch_name, type: String, desc: 'The name of branch' + requires :branch, type: String, desc: 'The name of branch' requires :commit_message, type: String, desc: 'Commit Message' optional :author_email, type: String, desc: 'The email of the author' optional :author_name, type: String, desc: 'The name of the author' diff --git a/lib/api/v3/commits.rb b/lib/api/v3/commits.rb new file mode 100644 index 00000000000..477e22fd25e --- /dev/null +++ b/lib/api/v3/commits.rb @@ -0,0 +1,205 @@ +require 'mime/types' + +module API + module V3 + class Commits < Grape::API + include PaginationParams + + before { authenticate! } + before { authorize! :download_code, user_project } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc 'Get a project repository commits' do + success ::API::Entities::RepoCommit + end + params do + optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used' + optional :since, type: DateTime, desc: 'Only commits after or in this date will be returned' + optional :until, type: DateTime, desc: 'Only commits before or in this date will be returned' + optional :page, type: Integer, default: 0, desc: 'The page for pagination' + optional :per_page, type: Integer, default: 20, desc: 'The number of results per page' + optional :path, type: String, desc: 'The file path' + end + get ":id/repository/commits" do + ref = params[:ref_name] || user_project.try(:default_branch) || 'master' + offset = params[:page] * params[:per_page] + + commits = user_project.repository.commits(ref, + path: params[:path], + limit: params[:per_page], + offset: offset, + after: params[:since], + before: params[:until]) + + present commits, with: ::API::Entities::RepoCommit + end + + desc 'Commit multiple file changes as one commit' do + success ::API::Entities::RepoCommitDetail + detail 'This feature was introduced in GitLab 8.13' + end + params do + requires :branch_name, type: String, desc: 'The name of branch' + requires :commit_message, type: String, desc: 'Commit message' + requires :actions, type: Array[Hash], desc: 'Actions to perform in commit' + optional :author_email, type: String, desc: 'Author email for commit' + optional :author_name, type: String, desc: 'Author name for commit' + end + post ":id/repository/commits" do + authorize! :push_code, user_project + + attrs = declared_params.dup + branch = attrs.delete(:branch_name) + attrs.merge!(branch: branch, start_branch: branch, target_branch: branch) + + attrs[:actions].map! do |action| + action[:action] = action[:action].to_sym + action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') + action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/') + action + end + + result = ::Files::MultiService.new(user_project, current_user, attrs).execute + + if result[:status] == :success + commit_detail = user_project.repository.commits(result[:result], limit: 1).first + present commit_detail, with: ::API::Entities::RepoCommitDetail + else + render_api_error!(result[:message], 400) + end + end + + desc 'Get a specific commit of a project' do + success ::API::Entities::RepoCommitDetail + failure [[404, 'Not Found']] + end + params do + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + end + get ":id/repository/commits/:sha" do + commit = user_project.commit(params[:sha]) + + not_found! "Commit" unless commit + + present commit, with: ::API::Entities::RepoCommitDetail + end + + desc 'Get the diff for a specific commit of a project' do + failure [[404, 'Not Found']] + end + params do + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + end + get ":id/repository/commits/:sha/diff" do + commit = user_project.commit(params[:sha]) + + not_found! "Commit" unless commit + + commit.raw_diffs.to_a + end + + desc "Get a commit's comments" do + success ::API::Entities::CommitNote + failure [[404, 'Not Found']] + end + params do + use :pagination + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + end + get ':id/repository/commits/:sha/comments' do + commit = user_project.commit(params[:sha]) + + not_found! 'Commit' unless commit + notes = Note.where(commit_id: commit.id).order(:created_at) + + present paginate(notes), with: ::API::Entities::CommitNote + end + + desc 'Cherry pick commit into a branch' do + detail 'This feature was introduced in GitLab 8.15' + success ::API::Entities::RepoCommit + end + params do + requires :sha, type: String, desc: 'A commit sha to be cherry picked' + requires :branch, type: String, desc: 'The name of the branch' + end + post ':id/repository/commits/:sha/cherry_pick' do + authorize! :push_code, user_project + + commit = user_project.commit(params[:sha]) + not_found!('Commit') unless commit + + branch = user_project.repository.find_branch(params[:branch]) + not_found!('Branch') unless branch + + commit_params = { + commit: commit, + create_merge_request: false, + source_project: user_project, + source_branch: commit.cherry_pick_branch_name, + target_branch: params[:branch] + } + + result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute + + if result[:status] == :success + branch = user_project.repository.find_branch(params[:branch]) + present user_project.repository.commit(branch.dereferenced_target), with: ::API::Entities::RepoCommit + else + render_api_error!(result[:message], 400) + end + end + + desc 'Post comment to commit' do + success ::API::Entities::CommitNote + end + params do + requires :sha, type: String, regexp: /\A\h{6,40}\z/, desc: "The commit's SHA" + requires :note, type: String, desc: 'The text of the comment' + optional :path, type: String, desc: 'The file path' + given :path do + requires :line, type: Integer, desc: 'The line number' + requires :line_type, type: String, values: ['new', 'old'], default: 'new', desc: 'The type of the line' + end + end + post ':id/repository/commits/:sha/comments' do + commit = user_project.commit(params[:sha]) + not_found! 'Commit' unless commit + + opts = { + note: params[:note], + noteable_type: 'Commit', + commit_id: commit.id + } + + if params[:path] + commit.raw_diffs(all_diffs: true).each do |diff| + next unless diff.new_path == params[:path] + lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line) + + lines.each do |line| + next unless line.new_pos == params[:line] && line.type == params[:line_type] + break opts[:line_code] = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos) + end + + break if opts[:line_code] + end + + opts[:type] = LegacyDiffNote.name if opts[:line_code] + end + + note = ::Notes::CreateService.new(user_project, current_user, opts).execute + + if note.save + present note, with: ::API::Entities::CommitNote + else + render_api_error!("Failed to save note #{note.errors.messages}", 400) + end + end + end + end + end +end diff --git a/lib/api/v3/files.rb b/lib/api/v3/files.rb new file mode 100644 index 00000000000..4f8d58d37c8 --- /dev/null +++ b/lib/api/v3/files.rb @@ -0,0 +1,138 @@ +module API + module V3 + class Files < Grape::API + helpers do + def commit_params(attrs) + { + file_path: attrs[:file_path], + start_branch: attrs[:branch], + target_branch: attrs[:branch], + commit_message: attrs[:commit_message], + file_content: attrs[:content], + file_content_encoding: attrs[:encoding], + author_email: attrs[:author_email], + author_name: attrs[:author_name] + } + end + + def commit_response(attrs) + { + file_path: attrs[:file_path], + branch: attrs[:branch] + } + end + + params :simple_file_params do + requires :file_path, type: String, desc: 'The path to new file. Ex. lib/class.rb' + requires :branch_name, type: String, desc: 'The name of branch' + requires :commit_message, type: String, desc: 'Commit Message' + optional :author_email, type: String, desc: 'The email of the author' + optional :author_name, type: String, desc: 'The name of the author' + end + + params :extended_file_params do + use :simple_file_params + requires :content, type: String, desc: 'File content' + optional :encoding, type: String, values: %w[base64], desc: 'File encoding' + end + end + + params do + requires :id, type: String, desc: 'The project ID' + end + resource :projects do + desc 'Get a file from repository' + params do + requires :file_path, type: String, desc: 'The path to the file. Ex. lib/class.rb' + requires :ref, type: String, desc: 'The name of branch, tag, or commit' + end + get ":id/repository/files" do + authorize! :download_code, user_project + + commit = user_project.commit(params[:ref]) + not_found!('Commit') unless commit + + repo = user_project.repository + blob = repo.blob_at(commit.sha, params[:file_path]) + not_found!('File') unless blob + + blob.load_all_data!(repo) + status(200) + + { + file_name: blob.name, + file_path: blob.path, + size: blob.size, + encoding: "base64", + content: Base64.strict_encode64(blob.data), + ref: params[:ref], + blob_id: blob.id, + commit_id: commit.id, + last_commit_id: repo.last_commit_id_for_path(commit.sha, params[:file_path]) + } + end + + desc 'Create new file in repository' + params do + use :extended_file_params + end + post ":id/repository/files" do + authorize! :push_code, user_project + + file_params = declared_params(include_missing: false) + file_params[:branch] = file_params.delete(:branch_name) + + result = ::Files::CreateService.new(user_project, current_user, commit_params(file_params)).execute + + if result[:status] == :success + status(201) + commit_response(file_params) + else + render_api_error!(result[:message], 400) + end + end + + desc 'Update existing file in repository' + params do + use :extended_file_params + end + put ":id/repository/files" do + authorize! :push_code, user_project + + file_params = declared_params(include_missing: false) + file_params[:branch] = file_params.delete(:branch_name) + + result = ::Files::UpdateService.new(user_project, current_user, commit_params(file_params)).execute + + if result[:status] == :success + status(200) + commit_response(file_params) + else + http_status = result[:http_status] || 400 + render_api_error!(result[:message], http_status) + end + end + + desc 'Delete an existing file in repository' + params do + use :simple_file_params + end + delete ":id/repository/files" do + authorize! :push_code, user_project + + file_params = declared_params(include_missing: false) + file_params[:branch] = file_params.delete(:branch_name) + + result = ::Files::DestroyService.new(user_project, current_user, commit_params(file_params)).execute + + if result[:status] == :success + status(200) + commit_response(file_params) + else + render_api_error!(result[:message], 400) + end + end + end + end + end +end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 2e6db0f43c6..5571f6cc107 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -272,7 +272,7 @@ describe API::Branches, api: true do describe "POST /projects/:id/repository/branches" do it "creates a new branch" do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'feature1', + branch: 'feature1', ref: branch_sha expect(response).to have_http_status(201) @@ -283,14 +283,14 @@ describe API::Branches, api: true do it "denies for user without push access" do post api("/projects/#{project.id}/repository/branches", user2), - branch_name: branch_name, + branch: branch_name, ref: branch_sha expect(response).to have_http_status(403) end it 'returns 400 if branch name is invalid' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new design', + branch: 'new design', ref: branch_sha expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') @@ -298,12 +298,12 @@ describe API::Branches, api: true do it 'returns 400 if branch already exists' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design1', + branch: 'new_design1', ref: branch_sha expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design1', + branch: 'new_design1', ref: branch_sha expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch already exists') @@ -311,7 +311,7 @@ describe API::Branches, api: true do it 'returns 400 if ref name is invalid' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design3', + branch: 'new_design3', ref: 'foo' expect(response).to have_http_status(400) expect(json_response['message']).to eq('Invalid reference name') @@ -326,14 +326,14 @@ describe API::Branches, api: true do it "removes branch" do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) expect(response).to have_http_status(200) - expect(json_response['branch_name']).to eq(branch_name) + expect(json_response['branch']).to eq(branch_name) end it "removes a branch with dots in the branch name" do delete api("/projects/#{project.id}/repository/branches/with.1.2.3", user) expect(response).to have_http_status(200) - expect(json_response['branch_name']).to eq("with.1.2.3") + expect(json_response['branch']).to eq("with.1.2.3") end it 'returns 404 if branch not exists' do diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index ecc6a597869..8b3dfedc5a9 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -107,7 +107,7 @@ describe API::Commits, api: true do let(:message) { 'Created file' } let!(:invalid_c_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -120,7 +120,7 @@ describe API::Commits, api: true do end let!(:valid_c_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -162,7 +162,7 @@ describe API::Commits, api: true do let(:message) { 'Deleted file' } let!(:invalid_d_params) do { - branch_name: 'markdown', + branch: 'markdown', commit_message: message, actions: [ { @@ -174,7 +174,7 @@ describe API::Commits, api: true do end let!(:valid_d_params) do { - branch_name: 'markdown', + branch: 'markdown', commit_message: message, actions: [ { @@ -203,7 +203,7 @@ describe API::Commits, api: true do let(:message) { 'Moved file' } let!(:invalid_m_params) do { - branch_name: 'feature', + branch: 'feature', commit_message: message, actions: [ { @@ -217,7 +217,7 @@ describe API::Commits, api: true do end let!(:valid_m_params) do { - branch_name: 'feature', + branch: 'feature', commit_message: message, actions: [ { @@ -248,7 +248,7 @@ describe API::Commits, api: true do let(:message) { 'Updated file' } let!(:invalid_u_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -261,7 +261,7 @@ describe API::Commits, api: true do end let!(:valid_u_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -291,7 +291,7 @@ describe API::Commits, api: true do let(:message) { 'Multiple actions' } let!(:invalid_mo_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -319,7 +319,7 @@ describe API::Commits, api: true do end let!(:valid_mo_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 5e26e779366..a8ce0430401 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -104,7 +104,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: 'newfile.rb', - branch_name: 'master', + branch: 'master', content: 'puts 8', commit_message: 'Added newfile' } @@ -153,7 +153,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', content: 'puts 8', commit_message: 'Changed file' } @@ -193,7 +193,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', commit_message: 'Changed file' } end @@ -241,7 +241,7 @@ describe API::Files, api: true do let(:put_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=', commit_message: 'Binary file with a \n should not be touched', encoding: 'base64' diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb new file mode 100644 index 00000000000..2d7584c3e59 --- /dev/null +++ b/spec/requests/api/v3/commits_spec.rb @@ -0,0 +1,578 @@ +require 'spec_helper' +require 'mime/types' + +describe API::V3::Commits, api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } + let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } + let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') } + + before { project.team << [user, :reporter] } + + describe "List repository commits" do + context "authorized user" do + before { project.team << [user2, :reporter] } + + it "returns project commits" do + commit = project.repository.commit + get v3_api("/projects/#{project.id}/repository/commits", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['id']).to eq(commit.id) + expect(json_response.first['committer_name']).to eq(commit.committer_name) + expect(json_response.first['committer_email']).to eq(commit.committer_email) + end + end + + context "unauthorized user" do + it "does not return project commits" do + get v3_api("/projects/#{project.id}/repository/commits") + expect(response).to have_http_status(401) + end + end + + context "since optional parameter" do + it "returns project commits since provided parameter" do + commits = project.repository.commits("master") + since = commits.second.created_at + + get v3_api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user) + + expect(json_response.size).to eq 2 + expect(json_response.first["id"]).to eq(commits.first.id) + expect(json_response.second["id"]).to eq(commits.second.id) + end + end + + context "until optional parameter" do + it "returns project commits until provided parameter" do + commits = project.repository.commits("master") + before = commits.second.created_at + + get v3_api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user) + + if commits.size >= 20 + expect(json_response.size).to eq(20) + else + expect(json_response.size).to eq(commits.size - 1) + end + + expect(json_response.first["id"]).to eq(commits.second.id) + expect(json_response.second["id"]).to eq(commits.third.id) + end + end + + context "invalid xmlschema date parameters" do + it "returns an invalid parameter error message" do + get v3_api("/projects/#{project.id}/repository/commits?since=invalid-date", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('since is invalid') + end + end + + context "path optional parameter" do + it "returns project commits matching provided path parameter" do + path = 'files/ruby/popen.rb' + + get v3_api("/projects/#{project.id}/repository/commits?path=#{path}", user) + + expect(json_response.size).to eq(3) + expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") + end + end + end + + describe "Create a commit with multiple files and actions" do + let!(:url) { "/projects/#{project.id}/repository/commits" } + + it 'returns a 403 unauthorized for user without permissions' do + post v3_api(url, user2) + + expect(response).to have_http_status(403) + end + + it 'returns a 400 bad request if no params are given' do + post v3_api(url, user) + + expect(response).to have_http_status(400) + end + + context :create do + let(:message) { 'Created file' } + let!(:invalid_c_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + let!(:valid_c_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 8' + } + ] + } + end + + it 'a new file in project repo' do + post v3_api(url, user), valid_c_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + expect(json_response['committer_name']).to eq(user.name) + expect(json_response['committer_email']).to eq(user.email) + end + + it 'returns a 400 bad request if file exists' do + post v3_api(url, user), invalid_c_params + + expect(response).to have_http_status(400) + end + + context 'with project path in URL' do + let(:url) { "/projects/#{project.namespace.path}%2F#{project.path}/repository/commits" } + + it 'a new file in project repo' do + post v3_api(url, user), valid_c_params + + expect(response).to have_http_status(201) + end + end + end + + context :delete do + let(:message) { 'Deleted file' } + let!(:invalid_d_params) do + { + branch_name: 'markdown', + commit_message: message, + actions: [ + { + action: 'delete', + file_path: 'doc/api/projects.md' + } + ] + } + end + let!(:valid_d_params) do + { + branch_name: 'markdown', + commit_message: message, + actions: [ + { + action: 'delete', + file_path: 'doc/api/users.md' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_d_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_d_params + + expect(response).to have_http_status(400) + end + end + + context :move do + let(:message) { 'Moved file' } + let!(:invalid_m_params) do + { + branch_name: 'feature', + commit_message: message, + actions: [ + { + action: 'move', + file_path: 'CHANGELOG', + previous_path: 'VERSION', + content: '6.7.0.pre' + } + ] + } + end + let!(:valid_m_params) do + { + branch_name: 'feature', + commit_message: message, + actions: [ + { + action: 'move', + file_path: 'VERSION.txt', + previous_path: 'VERSION', + content: '6.7.0.pre' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_m_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_m_params + + expect(response).to have_http_status(400) + end + end + + context :update do + let(:message) { 'Updated file' } + let!(:invalid_u_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'update', + file_path: 'foo/bar.baz', + content: 'puts 8' + } + ] + } + end + let!(:valid_u_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'update', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_u_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_u_params + + expect(response).to have_http_status(400) + end + end + + context "multiple operations" do + let(:message) { 'Multiple actions' } + let!(:invalid_mo_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + }, + { + action: 'delete', + file_path: 'doc/v3_api/projects.md' + }, + { + action: 'move', + file_path: 'CHANGELOG', + previous_path: 'VERSION', + content: '6.7.0.pre' + }, + { + action: 'update', + file_path: 'foo/bar.baz', + content: 'puts 8' + } + ] + } + end + let!(:valid_mo_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 8' + }, + { + action: 'delete', + file_path: 'Gemfile.zip' + }, + { + action: 'move', + file_path: 'VERSION.txt', + previous_path: 'VERSION', + content: '6.7.0.pre' + }, + { + action: 'update', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + + it 'are commited as one in project repo' do + post v3_api(url, user), valid_mo_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'return a 400 bad request if there are any issues' do + post v3_api(url, user), invalid_mo_params + + expect(response).to have_http_status(400) + end + end + end + + describe "Get a single commit" do + context "authorized user" do + it "returns a commit by sha" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['id']).to eq(project.repository.commit.id) + expect(json_response['title']).to eq(project.repository.commit.title) + expect(json_response['stats']['additions']).to eq(project.repository.commit.stats.additions) + expect(json_response['stats']['deletions']).to eq(project.repository.commit.stats.deletions) + expect(json_response['stats']['total']).to eq(project.repository.commit.stats.total) + end + + it "returns a 404 error if not found" do + get v3_api("/projects/#{project.id}/repository/commits/invalid_sha", user) + expect(response).to have_http_status(404) + end + + it "returns nil for commit without CI" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to be_nil + end + + it "returns status for CI" do + pipeline = project.ensure_pipeline('master', project.repository.commit.sha) + pipeline.update(status: 'success') + + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to eq(pipeline.status) + end + + it "returns status for CI when pipeline is created" do + project.ensure_pipeline('master', project.repository.commit.sha) + + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to eq("created") + end + end + + context "unauthorized user" do + it "does not return the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}") + expect(response).to have_http_status(401) + end + end + end + + describe "Get the diff of a commit" do + context "authorized user" do + before { project.team << [user2, :reporter] } + + it "returns the diff of the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) + expect(response).to have_http_status(200) + + expect(json_response).to be_an Array + expect(json_response.length).to be >= 1 + expect(json_response.first.keys).to include "diff" + end + + it "returns a 404 error if invalid commit" do + get v3_api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user) + expect(response).to have_http_status(404) + end + end + + context "unauthorized user" do + it "does not return the diff of the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff") + expect(response).to have_http_status(401) + end + end + end + + describe 'Get the comments of a commit' do + context 'authorized user' do + it 'returns merge_request comments' do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.first['note']).to eq('a comment on a commit') + expect(json_response.first['author']['id']).to eq(user.id) + end + + it 'returns a 404 error if merge_request_id not found' do + get v3_api("/projects/#{project.id}/repository/commits/1234ab/comments", user) + expect(response).to have_http_status(404) + end + end + + context 'unauthorized user' do + it 'does not return the diff of the selected commit' do + get v3_api("/projects/#{project.id}/repository/commits/1234ab/comments") + expect(response).to have_http_status(401) + end + end + end + + describe 'POST :id/repository/commits/:sha/cherry_pick' do + let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } + + context 'authorized user' do + it 'cherry picks a commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'master' + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(master_pickable_commit.title) + expect(json_response['message']).to eq(master_pickable_commit.message) + expect(json_response['author_name']).to eq(master_pickable_commit.author_name) + expect(json_response['committer_name']).to eq(user.name) + end + + it 'returns 400 if commit is already included in the target branch' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown' + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq('Sorry, we cannot cherry-pick this commit automatically. + A cherry-pick may have already been performed with this commit, or a more recent commit may have updated some of its content.') + end + + it 'returns 400 if you are not allowed to push to the target branch' do + project.team << [user2, :developer] + protected_branch = create(:protected_branch, project: project, name: 'feature') + + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user2), branch: protected_branch.name + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq('You are not allowed to push into this branch') + end + + it 'returns 400 for missing parameters' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('branch is missing') + end + + it 'returns 404 if commit is not found' do + post v3_api("/projects/#{project.id}/repository/commits/abcd0123/cherry_pick", user), branch: 'master' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Commit Not Found') + end + + it 'returns 404 if branch is not found' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'foo' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Branch Not Found') + end + + it 'returns 400 for missing parameters' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('branch is missing') + end + end + + context 'unauthorized user' do + it 'does not cherry pick the commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick"), branch: 'master' + + expect(response).to have_http_status(401) + end + end + end + + describe 'Post comment to commit' do + context 'authorized user' do + it 'returns comment' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment' + expect(response).to have_http_status(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to be_nil + expect(json_response['line']).to be_nil + expect(json_response['line_type']).to be_nil + end + + it 'returns the inline comment' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new' + + expect(response).to have_http_status(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to eq(project.repository.commit.raw_diffs.first.new_path) + expect(json_response['line']).to eq(1) + expect(json_response['line_type']).to eq('new') + end + + it 'returns 400 if note is missing' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) + expect(response).to have_http_status(400) + end + + it 'returns 404 if note is attached to non existent commit' do + post v3_api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment' + expect(response).to have_http_status(404) + end + end + + context 'unauthorized user' do + it 'does not return the diff of the selected commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments") + expect(response).to have_http_status(401) + end + end + end +end diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb new file mode 100644 index 00000000000..4af05605ec6 --- /dev/null +++ b/spec/requests/api/v3/files_spec.rb @@ -0,0 +1,270 @@ +require 'spec_helper' + +describe API::V3::Files, api: true do + include ApiHelpers + let(:user) { create(:user) } + let!(:project) { create(:project, :repository, namespace: user.namespace ) } + let(:guest) { create(:user) { |u| project.add_guest(u) } } + let(:file_path) { 'files/ruby/popen.rb' } + let(:params) do + { + file_path: file_path, + ref: 'master' + } + end + let(:author_email) { FFaker::Internet.email } + + # I have to remove periods from the end of the name + # This happened when the user's name had a suffix (i.e. "Sr.") + # This seems to be what git does under the hood. For example, this commit: + # + # $ git commit --author='Foo Sr. ' -m 'Where's my trailing period?' + # + # results in this: + # + # $ git show --pretty + # ... + # Author: Foo Sr + # ... + let(:author_name) { FFaker::Name.name.chomp("\.") } + + before { project.team << [user, :developer] } + + describe "GET /projects/:id/repository/files" do + let(:route) { "/projects/#{project.id}/repository/files" } + + shared_examples_for 'repository files' do + it "returns file info" do + get v3_api(route, current_user), params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + end + + context 'when no params are given' do + it_behaves_like '400 response' do + let(:request) { get v3_api(route, current_user) } + end + end + + context 'when file_path does not exist' do + let(:params) do + { + file_path: 'app/models/application.rb', + ref: 'master', + } + end + + it_behaves_like '404 response' do + let(:request) { get v3_api(route, current_user), params } + let(:message) { '404 File Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get v3_api(route, current_user), params } + end + end + end + + context 'when unauthenticated', 'and project is public' do + it_behaves_like 'repository files' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get v3_api(route), params } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do + it_behaves_like 'repository files' do + let(:current_user) { user } + end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get v3_api(route, guest), params } + end + end + end + + describe "POST /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: 'newfile.rb', + branch_name: 'master', + content: 'puts 8', + commit_message: 'Added newfile' + } + end + + it "creates a new file in project repo" do + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(201) + expect(json_response['file_path']).to eq('newfile.rb') + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + post v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + it "returns a 400 if editor fails to create file" do + allow_any_instance_of(Repository).to receive(:commit_file). + and_return(false) + + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "creates a new file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name) + + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(201) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "PUT /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: file_path, + branch_name: 'master', + content: 'puts 8', + commit_message: 'Changed file' + } + end + + it "updates existing file in project repo" do + put v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + put v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "updates a file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content") + + put v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "DELETE /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: file_path, + branch_name: 'master', + commit_message: 'Changed file' + } + end + + it "deletes existing file in project repo" do + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + delete v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + it "returns a 400 if fails to create file" do + allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) + + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "removes a file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name) + + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "POST /projects/:id/repository/files with binary file" do + let(:file_path) { 'test.bin' } + let(:put_params) do + { + file_path: file_path, + branch_name: 'master', + content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=', + commit_message: 'Binary file with a \n should not be touched', + encoding: 'base64' + } + end + let(:get_params) do + { + file_path: file_path, + ref: 'master', + } + end + + before do + post v3_api("/projects/#{project.id}/repository/files", user), put_params + end + + it "remains unchanged" do + get v3_api("/projects/#{project.id}/repository/files", user), get_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq(file_path) + expect(json_response['content']).to eq(put_params[:content]) + end + end +end From 53312250054a28fc5e5a7df6ef971995c20697dd Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 21 Feb 2017 19:35:08 +0000 Subject: [PATCH 079/129] Revert "Merge branch '27934-left-align-nav' into 'master'" This reverts merge request !9338 --- app/assets/stylesheets/framework/header.scss | 26 +++- app/assets/stylesheets/framework/nav.scss | 10 +- app/assets/stylesheets/pages/projects.scss | 30 +++-- app/views/layouts/_page.html.haml | 2 +- app/views/layouts/header/_default.html.haml | 4 +- app/views/projects/show.html.haml | 113 +++++++++--------- .../unreleased/27934-left-align-nav.yml | 4 - 7 files changed, 105 insertions(+), 84 deletions(-) delete mode 100644 changelogs/unreleased/27934-left-align-nav.yml diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 78434b99b62..3945a789c82 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -148,11 +148,16 @@ header { } .header-logo { - display: inline-block; - margin: 0 8px 0 3px; - position: relative; + position: absolute; + left: 50%; top: 7px; transition-duration: .3s; + z-index: 999; + + #logo { + position: relative; + left: -50%; + } svg, img { @@ -162,6 +167,15 @@ header { &:hover { cursor: pointer; } + + @media (max-width: $screen-xs-max) { + right: 20px; + left: auto; + + #logo { + left: auto; + } + } } .title { @@ -169,7 +183,7 @@ header { padding-right: 20px; margin: 0; font-size: 18px; - max-width: 450px; + max-width: 385px; display: inline-block; line-height: $header-height; font-weight: normal; @@ -179,6 +193,10 @@ header { vertical-align: top; white-space: nowrap; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + max-width: 300px; + } + @media (max-width: $screen-xs-max) { max-width: 190px; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 7d4a814a36c..674d3bb45aa 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -1,7 +1,7 @@ @mixin fade($gradient-direction, $gradient-color) { visibility: hidden; opacity: 0; - z-index: 1; + z-index: 2; position: absolute; bottom: 12px; width: 43px; @@ -18,7 +18,7 @@ .fa { position: relative; - top: 6px; + top: 5px; font-size: 18px; } } @@ -79,6 +79,7 @@ } &.sub-nav { + text-align: center; background-color: $gray-normal; .container-fluid { @@ -286,6 +287,7 @@ background: $gray-light; border-bottom: 1px solid $border-color; transition: padding $sidebar-transition-duration; + text-align: center; .container-fluid { position: relative; @@ -351,7 +353,7 @@ right: -5px; .fa { - right: -28px; + right: -7px; } } @@ -381,7 +383,7 @@ left: 0; .fa { - left: -4px; + left: 10px; } } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 8c0de314420..67110813abb 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -652,23 +652,29 @@ pre.light-well { } } -.container-fluid.project-stats-container { - @media (max-width: $screen-xs-max) { - padding: 12px 0; - } -} - .project-last-commit { - background-color: $gray-light; - padding: 12px $gl-padding; - border: 1px solid $border-color; - @media (min-width: $screen-sm-min) { margin-top: $gl-padding; } - @media (min-width: $screen-sm-min) { - border-radius: $border-radius-base; + &.container-fluid { + padding-top: 12px; + padding-bottom: 12px; + background-color: $gray-light; + border: 1px solid $border-color; + border-right-width: 0; + border-left-width: 0; + + @media (min-width: $screen-sm-min) { + border-right-width: 1px; + border-left-width: 1px; + } + } + + &.container-limited { + @media (min-width: 1281px) { + border-radius: $border-radius-base; + } } .ci-status { diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 1717ed6b365..a35a918d501 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,7 +1,7 @@ .page-with-sidebar{ class: page_gutter_class } - if defined?(nav) && nav .layout-nav - %div{ class: container_class } + .container-fluid = render "layouts/nav/#{nav}" .content-wrapper{ class: "#{layout_nav_class}" } = yield :sub_nav diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 60b9b8bdbc4..ddf50d6667f 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -61,12 +61,12 @@ %div = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' + %h1.title= title + .header-logo = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = brand_header_logo - %h1.title= title - = yield :header_content = render 'shared/outdated_browser' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index f7419728719..80d4081dd7b 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -13,70 +13,69 @@ = render "home_panel" - if current_user && can?(current_user, :download_code, @project) - .project-stats-container{ class: container_class } - %nav.project-stats - %ul.nav + %nav.project-stats{ class: container_class } + %ul.nav + %li + = link_to project_files_path(@project) do + Files (#{storage_counter(@project.statistics.total_repository_size)}) + %li + = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do + #{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)}) + %li + = link_to namespace_project_branches_path(@project.namespace, @project) do + #{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) + %li + = link_to namespace_project_tags_path(@project.namespace, @project) do + #{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) + + - if default_project_view != 'readme' && @repository.readme %li - = link_to project_files_path(@project) do - Files (#{storage_counter(@project.statistics.total_repository_size)}) + = link_to 'Readme', readme_path(@project) + + - if @repository.changelog %li - = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do - #{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)}) + = link_to 'Changelog', changelog_path(@project) + + - if @repository.license_blob %li - = link_to namespace_project_branches_path(@project.namespace, @project) do - #{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) + = link_to license_short_name(@project), license_path(@project) + + - if @repository.contribution_guide %li - = link_to namespace_project_tags_path(@project.namespace, @project) do - #{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) + = link_to 'Contribution guide', contribution_guide_path(@project) - - if default_project_view != 'readme' && @repository.readme - %li - = link_to 'Readme', readme_path(@project) + - if @repository.gitlab_ci_yml + %li + = link_to 'CI configuration', ci_configuration_path(@project) - - if @repository.changelog - %li - = link_to 'Changelog', changelog_path(@project) + - if current_user && can_push_branch?(@project, @project.default_branch) + - unless @repository.changelog + %li.missing + = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do + Add Changelog + - unless @repository.license_blob + %li.missing + = link_to add_special_file_path(@project, file_name: 'LICENSE') do + Add License + - unless @repository.contribution_guide + %li.missing + = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do + Add Contribution guide + - unless @repository.gitlab_ci_yml + %li.missing + = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do + Set up CI + - if koding_enabled? && @repository.koding_yml.blank? + %li.missing + = link_to 'Set up Koding', add_koding_stack_path(@project) + - if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? + %li.missing + = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do + Set up auto deploy - - if @repository.license_blob - %li - = link_to license_short_name(@project), license_path(@project) - - - if @repository.contribution_guide - %li - = link_to 'Contribution guide', contribution_guide_path(@project) - - - if @repository.gitlab_ci_yml - %li - = link_to 'CI configuration', ci_configuration_path(@project) - - - if current_user && can_push_branch?(@project, @project.default_branch) - - unless @repository.changelog - %li.missing - = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do - Add Changelog - - unless @repository.license_blob - %li.missing - = link_to add_special_file_path(@project, file_name: 'LICENSE') do - Add License - - unless @repository.contribution_guide - %li.missing - = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do - Add Contribution guide - - unless @repository.gitlab_ci_yml - %li.missing - = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do - Set up CI - - if koding_enabled? && @repository.koding_yml.blank? - %li.missing - = link_to 'Set up Koding', add_koding_stack_path(@project) - - if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? - %li.missing - = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do - Set up auto deploy - - - if @repository.commit - .project-last-commit - = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project + - if @repository.commit + .project-last-commit{ class: container_class } + = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project %div{ class: container_class } - if @project.archived? diff --git a/changelogs/unreleased/27934-left-align-nav.yml b/changelogs/unreleased/27934-left-align-nav.yml deleted file mode 100644 index 6c45e4ce175..00000000000 --- a/changelogs/unreleased/27934-left-align-nav.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Left align navigation -merge_request: -author: From 334f3283f53905c11b9eafc6b3de8f12f661adaf Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 16:42:12 -0300 Subject: [PATCH 080/129] add link to video: pages quick start from forked repo --- doc/pages/index.md | 2 +- doc/pages/pages_quick_start_guide.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/pages/index.md b/doc/pages/index.md index 11b2582841f..54a951f0a2a 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -12,7 +12,7 @@ - GitLab Pages from A to Z - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html) - [Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html) - - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](#LINK) + - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from scratch](#LINK) - [Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html) - Secure GitLab Pages Custom Domain with SSL/TLS Certificates diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/pages_quick_start_guide.md index f7b0c3c9520..945e350e235 100644 --- a/doc/pages/pages_quick_start_guide.md +++ b/doc/pages/pages_quick_start_guide.md @@ -41,7 +41,7 @@ Let's go over both options. To make things easy for you, we've created this [group](https://gitlab.com/pages) of default projects containing the most popular SSGs templates. -Watch the [video tutorial](#LINK) we've created for the steps below. +Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've created for the steps below. 1. Choose your SSG template 1. Fork a project from the [Pages group](https://gitlab.com/pages) From 69b2bc4d5987fea42da13db5f7d6b1e89d44ebf6 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 16:46:00 -0300 Subject: [PATCH 081/129] minor change [ci skip] --- ...ges_static_sites_domains_dns_records_ssl_tls_certificates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index 1a4e798df66..d32c8b76558 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -12,7 +12,7 @@ This is a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. -To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). +To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). > For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. From 601b5adf5768b548a5810ffe890b38f7f4980d89 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 21 Feb 2017 19:34:03 +0000 Subject: [PATCH 082/129] Use a btn-group to group all action buttons --- .../components/environment_actions.js.es6 | 38 +++++++++---------- .../components/environment_item.js.es6 | 25 +++--------- .../stylesheets/pages/environments.scss | 18 ++++++++- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6 index c5a714d9673..978d4dd8b6b 100644 --- a/app/assets/javascripts/environments/components/environment_actions.js.es6 +++ b/app/assets/javascripts/environments/components/environment_actions.js.es6 @@ -15,29 +15,29 @@ module.exports = Vue.component('actions-component', { }, template: ` -
- + + {{action.name}} + + + + + +
`, }); diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 24fd58a301a..ad9d1d21a79 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -505,39 +505,26 @@ module.exports = Vue.component('environment-item', {
-
- + -
-
- -
-
- -
-
- -
-
- diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 181dcb7721f..f789ae1ccd3 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -35,7 +35,6 @@ display: table-cell; } - .environments-name, .environments-commit, .environments-actions { width: 20%; @@ -45,6 +44,7 @@ width: 10%; } + .environments-name, .environments-deploy, .environments-build { width: 15%; @@ -62,6 +62,22 @@ } } + .btn-group { + + > a { + color: $gl-text-color-secondary; + } + + svg path { + fill: $gl-text-color-secondary; + } + + .dropdown { + outline: none; + } + } + + .commit-title { margin: 0; } From 1e73657e5b2f07fc5a904f24f979be244a054f97 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 16:58:09 -0300 Subject: [PATCH 083/129] add video to admin docs --- doc/administration/pages/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 8de0cc5af5c..1c444cf0d50 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -102,6 +102,8 @@ The Pages daemon doesn't listen to the outside world. 1. [Reconfigure GitLab][reconfigure] +Watch the [video tutorial][video-admin] for this configuration. + ### Wildcard domains with TLS support >**Requirements:** @@ -270,3 +272,4 @@ latest previous version. [reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure [restart]: ../restart_gitlab.md#installations-from-source [gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4 +[video-admin]: https://youtu.be/dD8c7WNcc6s From e27da0ffd7afe535a7b421885560b9f6f2e2cfc6 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 17:07:08 -0300 Subject: [PATCH 084/129] link index-guide to pages user doc --- doc/user/project/pages/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index 4c4f15aad40..ff42e383880 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -14,6 +14,8 @@ deploy static pages for your individual projects, your user or your group. Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlab-com) for specific information, if you are using GitLab.com to host your website. +Read through [All you Need to Know About GitLab Pages][pages-index-guide] for a list of all learning materials we have prepared for GitLab Pages (webpages, articles, guides, blog posts, video tutorials). + ## Getting started with GitLab Pages > **Note:** @@ -435,3 +437,4 @@ For a list of known issues, visit GitLab's [public issue tracker]. [public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Pages [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 [quick start guide]: ../../../ci/quick_start/README.md +[pages-index-guide]: ../../../pages/ From 4d6d5ce64fd5bce8fa3db6dcb14deb930589a008 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 17:14:18 -0300 Subject: [PATCH 085/129] add quick start section to user docs --- doc/user/project/pages/index.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index ff42e383880..e07b7e83f81 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -98,6 +98,13 @@ The steps to create a project page for a user or a group are identical: A user's project will be served under `http(s)://username.example.io/projectname` whereas a group's project under `http(s)://groupname.example.io/projectname`. +## Quick Start + +Read through [GitLab Pages Quick Start Guide][pages-quick] or watch the video tutorial on +[how to publish a website with GitLab Pages on GitLab.com from a forked project][video-pages-fork]. + +See also [All you Need to Know About GitLab Pages][pages-index-guide] for a list with all the resources we have for GitLab Pages. + ### Explore the contents of `.gitlab-ci.yml` The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that @@ -438,3 +445,5 @@ For a list of known issues, visit GitLab's [public issue tracker]. [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 [quick start guide]: ../../../ci/quick_start/README.md [pages-index-guide]: ../../../pages/ +[pages-quick]: ../../../pages/pages_quick_start_guide.html +[video-pages-fork]: https://youtu.be/TWqh9MtT4Bg From dde136ad8ec02e7a61d1197cbb209117b252dd8f Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Tue, 21 Feb 2017 18:24:08 -0300 Subject: [PATCH 086/129] add images --- doc/pages/images/add_certificate_to_pages.png | Bin 0 -> 58581 bytes doc/pages/images/choose_ci_template.png | Bin 0 -> 75580 bytes doc/pages/images/dns_a_record_example.png | Bin 0 -> 10681 bytes doc/pages/images/dns_cname_record_example.png | Bin 0 -> 11972 bytes doc/pages/images/remove_fork_relashionship.png | Bin 0 -> 39941 bytes doc/pages/images/setup_ci.png | Bin 0 -> 27380 bytes doc/pages/pages_quick_start_guide.md | 6 +++--- ..._domains_dns_records_ssl_tls_certificates.md | 6 +++--- 8 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 doc/pages/images/add_certificate_to_pages.png create mode 100644 doc/pages/images/choose_ci_template.png create mode 100644 doc/pages/images/dns_a_record_example.png create mode 100644 doc/pages/images/dns_cname_record_example.png create mode 100644 doc/pages/images/remove_fork_relashionship.png create mode 100644 doc/pages/images/setup_ci.png diff --git a/doc/pages/images/add_certificate_to_pages.png b/doc/pages/images/add_certificate_to_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..94d48fafa6e88806c89d09a5262cbef2cd294470 GIT binary patch literal 58581 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 literal 0 HcmV?d00001 diff --git a/doc/pages/images/choose_ci_template.png b/doc/pages/images/choose_ci_template.png new file mode 100644 index 0000000000000000000000000000000000000000..dea4ad2ed28e3c00f4fb80bb71feb744747e0275 GIT binary patch literal 75580 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 aE`^QnMGa;+G4S(CxMxyIl3&G*-~Jz)wGcx9 literal 0 HcmV?d00001 diff --git a/doc/pages/images/dns_cname_record_example.png b/doc/pages/images/dns_cname_record_example.png new file mode 100644 index 0000000000000000000000000000000000000000..93e011d743dd2bb99ac8f7d9c0221bd9409b4546 GIT binary patch literal 11972 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 literal 0 HcmV?d00001 diff --git a/doc/pages/images/remove_fork_relashionship.png b/doc/pages/images/remove_fork_relashionship.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4fab929904d359d5c891f1e968453316c5a5d1 GIT binary patch literal 39941 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

onIA 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

kHf)t@q}BfOzX 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�zEr1KTNhxXW5(@^Qu*)NTWtzV`5vUTN>C? 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 MMdxbqWy_HN0Lwr4qyPW_ literal 0 HcmV?d00001 diff --git a/doc/pages/images/setup_ci.png b/doc/pages/images/setup_ci.png new file mode 100644 index 0000000000000000000000000000000000000000..5569f6aacf5ee90ebd45929695c736731fc036eb GIT binary patch literal 27380 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 literal 0 HcmV?d00001 diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/pages_quick_start_guide.md index 945e350e235..b86b451ba5e 100644 --- a/doc/pages/pages_quick_start_guide.md +++ b/doc/pages/pages_quick_start_guide.md @@ -47,7 +47,7 @@ Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've created for the s 1. Fork a project from the [Pages group](https://gitlab.com/pages) 1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project** - ![remove fork relashionship]() + ![remove fork relashionship](images/remove_fork_relashionship.png) 1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **CI/CD Pipelines** 1. Trigger a build (push a change to any file) @@ -74,11 +74,11 @@ To turn a **project website** forked from the Pages group into a **user/group** 1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. 1. From the your **Project**'s page, click **Set up CI**: - ![add new file]() + ![setup GitLab CI](images/setup_ci.png) 1. Choose one of the templates from the dropbox menu. Pick up the template corresponding to the SSG you're using (or plain HTML). - ![gitlab-ci templates]() + ![gitlab-ci templates](images/choose_ci_template.png) Once you have both site files and `.gitlab-ci.yml` in your project's root, GitLab CI will build your site and deploy it with Pages. Once the first build passes, you see your site is live by navigating to your **Project**'s **Settings** > **Pages**, where you'll find its default URL. diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index d32c8b76558..c6e2cab0bc3 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -86,7 +86,7 @@ In case you want to point a root domain (`example.com`) to your GitLab Pages sit **Practical Example:** -![DNS A record pointing to GitLab.com Pages server]() +![DNS A record pointing to GitLab.com Pages server](images/dns_a_record_example.png) #### DNS CNAME record @@ -96,7 +96,7 @@ Notice that, despite it's a user or project website, the `CNAME` should point to **Practical Example:** -![DNS CNAME record pointing to GitLab.com project]() +![DNS CNAME record pointing to GitLab.com project](images/dns_cname_record_example.png) #### TL;DR @@ -138,7 +138,7 @@ Regardless the CA you choose, the steps to add your certificate to your Pages pr 1. An intermediary certificate 1. A public key -![Pages project - adding certificates]() +![Pages project - adding certificates](images/add_certificate_to_pages.png) These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**. From 93e3cfec3abc7c0eed741ba94779bf014cd9bcb7 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 22 Feb 2017 08:52:49 +1100 Subject: [PATCH 087/129] Added space indentation in models/merge_requests_closing_issues.rb --- app/models/merge_requests_closing_issues.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index 97210900bd5..daafb137be4 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -8,8 +8,8 @@ class MergeRequestsClosingIssues < ActiveRecord::Base class << self def count_for_collection(ids) group(:issue_id). - where(issue_id: ids). - pluck('issue_id', 'COUNT(*) as count') + where(issue_id: ids). + pluck('issue_id', 'COUNT(*) as count') end end end From 5cc838d325fb3fcdd7a86a43c67a442ce5feb3d9 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 22 Feb 2017 09:00:03 +1100 Subject: [PATCH 088/129] Refactored specs --- spec/features/issuables/issuable_list_spec.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 4ea801cd1ac..1fc1c20b8a3 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -60,15 +60,15 @@ describe 'issuable list', feature: true do create(:award_emoji, :downvote, awardable: issuable) create(:award_emoji, :upvote, awardable: issuable) - if issuable_type == :issue - issue = Issue.reorder(:iid).first - merge_request = create(:merge_request, - title: FFaker::Lorem.sentence, - source_project: project, - source_branch: FFaker::Name.name) + if issuable_type == :issue + issue = Issue.reorder(:iid).first + merge_request = create(:merge_request, + title: FFaker::Lorem.sentence, + source_project: project, + source_branch: FFaker::Name.name) - MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) if MergeRequestsClosingIssues.count.zero? - end + MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) if MergeRequestsClosingIssues.count.zero? + end end end end From 0494930d7a939297a071bb817ad0227da17dda1b Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 22 Feb 2017 09:08:20 +1100 Subject: [PATCH 089/129] Refactored specs --- spec/features/issuables/issuable_list_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 1fc1c20b8a3..0bf7977fb02 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -59,6 +59,7 @@ describe 'issuable list', feature: true do create(:award_emoji, :downvote, awardable: issuable) create(:award_emoji, :upvote, awardable: issuable) + end if issuable_type == :issue issue = Issue.reorder(:iid).first @@ -67,8 +68,7 @@ describe 'issuable list', feature: true do source_project: project, source_branch: FFaker::Name.name) - MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) if MergeRequestsClosingIssues.count.zero? - end + MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) end end end From ca68c8173312c788e8f02669cfde20fbcdd76a3c Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 17 Feb 2017 10:49:45 +0100 Subject: [PATCH 090/129] Update documentation --- doc/api/issues.md | 8 ++++---- doc/api/labels.md | 10 +++++----- doc/api/merge_requests.md | 8 ++++---- doc/api/v3_to_v4.md | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/api/issues.md b/doc/api/issues.md index f40e0938b0f..6cd701215e9 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -514,7 +514,7 @@ If the user is already subscribed to the issue, the status code `304` is returned. ``` -POST /projects/:id/issues/:issue_id/subscription +POST /projects/:id/issues/:issue_id/subscribe ``` | Attribute | Type | Required | Description | @@ -523,7 +523,7 @@ POST /projects/:id/issues/:issue_id/subscription | `issue_id` | integer | yes | The ID of a project's issue | ```bash -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscribe ``` Example response: @@ -569,7 +569,7 @@ from it. If the user is not subscribed to the issue, the status code `304` is returned. ``` -DELETE /projects/:id/issues/:issue_id/subscription +DELETE /projects/:id/issues/:issue_id/unsubscribe ``` | Attribute | Type | Required | Description | @@ -578,7 +578,7 @@ DELETE /projects/:id/issues/:issue_id/subscription | `issue_id` | integer | yes | The ID of a project's issue | ```bash -curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/unsubscribe ``` Example response: diff --git a/doc/api/labels.md b/doc/api/labels.md index 863b28c23b7..a1e7eb1a7b1 100644 --- a/doc/api/labels.md +++ b/doc/api/labels.md @@ -188,12 +188,12 @@ Example response: ## Subscribe to a label -Subscribes the authenticated user to a label to receive notifications. +Subscribes the authenticated user to a label to receive notifications. If the user is already subscribed to the label, the status code `304` is returned. ``` -POST /projects/:id/labels/:label_id/subscription +POST /projects/:id/labels/:label_id/subscribe ``` | Attribute | Type | Required | Description | @@ -202,7 +202,7 @@ POST /projects/:id/labels/:label_id/subscription | `label_id` | integer or string | yes | The ID or title of a project's label | ```bash -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscribe ``` Example response: @@ -228,7 +228,7 @@ from it. If the user is not subscribed to the label, the status code `304` is returned. ``` -DELETE /projects/:id/labels/:label_id/subscription +DELETE /projects/:id/labels/:label_id/unsubscribe ``` | Attribute | Type | Required | Description | @@ -237,7 +237,7 @@ DELETE /projects/:id/labels/:label_id/subscription | `label_id` | integer or string | yes | The ID or title of a project's label | ```bash -curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/unsubscribe ``` Example response: diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 6ee377125d6..2a99ae822d7 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -667,7 +667,7 @@ Subscribes the authenticated user to a merge request to receive notification. If status code `304` is returned. ``` -POST /projects/:id/merge_requests/:merge_request_id/subscription +POST /projects/:id/merge_requests/:merge_request_id/subscribe ``` | Attribute | Type | Required | Description | @@ -676,7 +676,7 @@ POST /projects/:id/merge_requests/:merge_request_id/subscription | `merge_request_id` | integer | yes | The ID of the merge request | ```bash -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe ``` Example response: @@ -741,7 +741,7 @@ notifications from that merge request. If the user is not subscribed to the merge request, the status code `304` is returned. ``` -DELETE /projects/:id/merge_requests/:merge_request_id/subscription +DELETE /projects/:id/merge_requests/:merge_request_id/unsubscribe ``` | Attribute | Type | Required | Description | @@ -750,7 +750,7 @@ DELETE /projects/:id/merge_requests/:merge_request_id/subscription | `merge_request_id` | integer | yes | The ID of the merge request | ```bash -curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscription +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/unsubscribe ``` Example response: diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index e24ee0da204..2dd8886ab97 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -29,5 +29,5 @@ changes are in V4: - Return pagination headers for all endpoints that return an array - Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` -- Make subscription API more RESTful. Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. +- Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. - Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) From b8c88a839da9722f17bc7a851af2fae15e1ab1c5 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 22 Feb 2017 10:40:58 +0100 Subject: [PATCH 091/129] Grapfiy the CI::Triggers API --- lib/ci/api/triggers.rb | 43 ++++++++++----------------- spec/requests/ci/api/triggers_spec.rb | 3 +- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb index 63b42113513..6e622601680 100644 --- a/lib/ci/api/triggers.rb +++ b/lib/ci/api/triggers.rb @@ -1,41 +1,30 @@ 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 + desc 'Trigger a GitLab CI project build' do + success Entities::TriggerRequest + end + params do + requires :id, type: Integer, desc: 'The ID of a CI project' + requires :ref, type: String, desc: "The name of project's branch or tag" + requires :token, type: String, desc: 'The unique token of the trigger' + optional :variables, type: Hash, desc: 'Optional build variables' + end post ":id/refs/:ref/trigger" do - required_attributes! [:token] - - project = Project.find_by(ci_id: params[:id].to_i) - trigger = Ci::Trigger.find_by_token(params[:token].to_s) + project = Project.find_by(ci_id: params[:id]) + trigger = Ci::Trigger.find_by_token(params[:token]) 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 + # Validate variables + variables = params[:variables].to_h + 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 # create request and trigger builds - trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) + trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref], variables) if trigger_request present trigger_request, with: Entities::TriggerRequest else diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index a30be767119..5321f8b134f 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -60,7 +60,8 @@ describe Ci::API::Triggers do it 'validates variables to be a hash' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('variables needs to be a hash') + + expect(json_response['error']).to eq('variables is invalid') end it 'validates variables needs to be a map of key-valued strings' do From f0766fdefaeadcc526d6a87e29b65f5bf7b26318 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 6 Feb 2017 20:15:25 +0100 Subject: [PATCH 092/129] extract pipeline mails layout --- .../images/mailers/gitlab_footer_logo.gif | Bin 0 -> 3654 bytes .../images/mailers/gitlab_header_logo.gif | Bin 0 -> 3040 bytes app/mailers/emails/pipelines.rb | 4 +- app/views/layouts/mailer.html.haml | 72 +++++ app/views/layouts/mailer.text.haml | 5 + .../notify/pipeline_failed_email.html.haml | 280 +++++++----------- .../notify/pipeline_failed_email.text.erb | 4 - .../notify/pipeline_success_email.html.haml | 230 +++++--------- .../notify/pipeline_success_email.text.erb | 4 - 9 files changed, 264 insertions(+), 335 deletions(-) create mode 100644 app/assets/images/mailers/gitlab_footer_logo.gif create mode 100644 app/assets/images/mailers/gitlab_header_logo.gif create mode 100644 app/views/layouts/mailer.html.haml create mode 100644 app/views/layouts/mailer.text.haml diff --git a/app/assets/images/mailers/gitlab_footer_logo.gif b/app/assets/images/mailers/gitlab_footer_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..3f4ef31947bc4a53d6b2d243d6e9f2c975c84b77 GIT binary patch literal 3654 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 z

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 +%html{ lang: "en" } + %head + %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ + %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ + %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ + %title= message.subject + :css + /* CLIENT-SPECIFIC STYLES */ + body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } + table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; } + img { -ms-interpolation-mode: bicubic; } + + /* iOS BLUE LINKS */ + a[x-apple-data-detectors] { + color: inherit !important; + text-decoration: none !important; + font-size: inherit !important; + font-family: inherit !important; + font-weight: inherit !important; + line-height: inherit !important; + } + + /* ANDROID MARGIN HACK */ + body { margin:0 !important; } + div[style*="margin: 16px 0"] { margin:0 !important; } + + @media only screen and (max-width: 639px) { + body, #body { + min-width: 320px !important; + } + table.wrapper { + width: 100% !important; + min-width: 320px !important; + } + table.wrapper > tbody > tr > td { + border-left: 0 !important; + border-right: 0 !important; + border-radius: 0 !important; + padding-left: 10px !important; + padding-right: 10px !important; + } + } + %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" } + %tbody + %tr.line + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   + %tr.header + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "50", src: image_url('mailers/gitlab_header_logo.gif'), width: "55" }/ + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } + %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } + %tbody + = yield + + %tr.footer + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } + %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ + %div + %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications + · + %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help + %div + You're receiving this email because of your account on + = succeed "." do + %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host diff --git a/app/views/layouts/mailer.text.haml b/app/views/layouts/mailer.text.haml new file mode 100644 index 00000000000..6a9c6ced9cc --- /dev/null +++ b/app/views/layouts/mailer.text.haml @@ -0,0 +1,5 @@ += yield + +You're receiving this email because of your account on #{Gitlab.config.gitlab.host}. +Manage all notifications: #{profile_notifications_url} +Help: #{help_url} diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index d9ebbaa2704..85a1aea3a61 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -1,179 +1,109 @@ - -%html{ lang: "en" } - %head - %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ - %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ - %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ - %title= message.subject - :css - /* CLIENT-SPECIFIC STYLES */ - body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } - table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; } - img { -ms-interpolation-mode: bicubic; } - - /* iOS BLUE LINKS */ - a[x-apple-data-detectors] { - color: inherit !important; - text-decoration: none !important; - font-size: inherit !important; - font-family: inherit !important; - font-weight: inherit !important; - line-height: inherit !important; - } - - /* ANDROID MARGIN HACK */ - body { margin:0 !important; } - div[style*="margin: 16px 0"] { margin:0 !important; } - - @media only screen and (max-width: 639px) { - body, #body { - min-width: 320px !important; - } - table.wrapper { - width: 100% !important; - min-width: 320px !important; - } - table.wrapper > tbody > tr > td { - border-left: 0 !important; - border-right: 0 !important; - border-radius: 0 !important; - padding-left: 10px !important; - padding-right: 10px !important; - } - } - %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } - %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" } +%tr.alert + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } %tbody - %tr.line - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   - %tr.header - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } - %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/ %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } - %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } + %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } + Your pipeline has failed. +%tr.spacer + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } +   +%tr.section + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } + - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name + - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) + %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } + = namespace_name + \/ + %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } + = @project.name + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } - %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } - %tbody - %tr.alert - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } - %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } - Your pipeline has failed. - %tr.spacer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } -   - %tr.section - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } - %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } - - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name - - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) - %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } - = namespace_name - \/ - %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } - = @project.name - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } - = @pipeline.ref - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } - = @pipeline.short_sha - - if @merge_request - in - %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } - = @merge_request.to_reference - .commit{ style: "color:#5c5c5c;font-weight:300;" } - = @pipeline.git_commit_message.truncate(50) - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - - commit = @pipeline.commit - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - - if commit.author - %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } - = commit.author.name - - else - %span - = commit.author_name - %tr.spacer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } -   - - failed = @pipeline.statuses.latest.failed - %tr.pre-section - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" } - Pipeline - %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } - = "\##{@pipeline.id}" - had - = failed.size - failed - #{'build'.pluralize(failed.size)}. - %tr.warning - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" } - Logs may contain sensitive data. Please consider before forwarding this email. - %tr.section - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" } - %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" } - %tbody - - failed.each do |build| - %tr.build-state - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" } - %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" } - = build.stage - %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } - = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build - %tr.build-log - - if build.has_trace? - %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" } - %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" } - = build.trace_html(last_lines: 10).html_safe - - else - %td{ colspan: "2" } - %tr.footer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } - %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ - %div - %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications - · - %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help - %div - You're receiving this email because of your account on - = succeed "." do - %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } + = @pipeline.ref + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } + = @pipeline.short_sha + - if @merge_request + in + %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } + = @merge_request.to_reference + .commit{ style: "color:#5c5c5c;font-weight:300;" } + = @pipeline.git_commit_message.truncate(50) + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } + %tbody + %tr + - commit = @pipeline.commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + - if commit.author + %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } + = commit.author.name + - else + %span + = commit.author_name +%tr.spacer + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } +   +- failed = @pipeline.statuses.latest.failed +%tr.pre-section + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" } + Pipeline + %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } + = "\##{@pipeline.id}" + had + = failed.size + failed + #{'build'.pluralize(failed.size)}. +%tr.warning + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" } + Logs may contain sensitive data. Please consider before forwarding this email. +%tr.section + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" } + %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" } + %tbody + - failed.each do |build| + %tr.build-state + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" } + %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" } + = build.stage + %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" } + = render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build + %tr.build-log + - if build.has_trace? + %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" } + %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" } + = build.trace_html(last_lines: 10).html_safe + - else + %td{ colspan: "2" } diff --git a/app/views/notify/pipeline_failed_email.text.erb b/app/views/notify/pipeline_failed_email.text.erb index ab91c7ef350..520a2fc7d68 100644 --- a/app/views/notify/pipeline_failed_email.text.erb +++ b/app/views/notify/pipeline_failed_email.text.erb @@ -27,7 +27,3 @@ Trace: <%= build.trace_with_state(last_lines: 10)[:text] %> <% end -%> <% end -%> - -You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>. -Manage all notifications: <%= profile_notifications_url %> -Help: <%= help_url %> diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index 8add2e18206..19d4add06f5 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -1,154 +1,84 @@ - -%html{ lang: "en" } - %head - %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/ - %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/ - %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ - %title= message.subject - :css - /* CLIENT-SPECIFIC STYLES */ - body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } - table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; } - img { -ms-interpolation-mode: bicubic; } - - /* iOS BLUE LINKS */ - a[x-apple-data-detectors] { - color: inherit !important; - text-decoration: none !important; - font-size: inherit !important; - font-family: inherit !important; - font-weight: inherit !important; - line-height: inherit !important; - } - - /* ANDROID MARGIN HACK */ - body { margin:0 !important; } - div[style*="margin: 16px 0"] { margin:0 !important; } - - @media only screen and (max-width: 639px) { - body, #body { - min-width: 320px !important; - } - table.wrapper { - width: 100% !important; - min-width: 320px !important; - } - table.wrapper > tbody > tr > td { - border-left: 0 !important; - border-right: 0 !important; - border-radius: 0 !important; - padding-left: 10px !important; - padding-right: 10px !important; - } - } - %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } - %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" } +%tr.success + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } %tbody - %tr.line - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   - %tr.header - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } - %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/ %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } - %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } + %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } + Your pipeline has passed. +%tr.spacer + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } +   +%tr.section + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } + %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } + - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name + - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) + %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } + = namespace_name + \/ + %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } + = @project.name + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } %tbody %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } - %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" } - %tbody - %tr.success - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" } - %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" } - Your pipeline has passed. - %tr.spacer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } -   - %tr.section - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" } - %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" } - - namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name - - namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner) - %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" } - = namespace_name - \/ - %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" } - = @project.name - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } - = @pipeline.ref - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } - = @pipeline.short_sha - - if @merge_request - in - %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } - = @merge_request.to_reference - .commit{ style: "color:#5c5c5c;font-weight:300;" } - = @pipeline.git_commit_message.truncate(50) - %tr - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } - %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } - %tbody - %tr - - commit = @pipeline.commit - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - - if commit.author - %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } - = commit.author.name - - else - %span - = commit.author_name - %tr.spacer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } -   - %tr.success-message - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" } - - build_count = @pipeline.statuses.latest.size - - stage_count = @pipeline.stages_count - Pipeline - %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } - = "\##{@pipeline.id}" - successfully completed - #{build_count} #{'build'.pluralize(build_count)} - in - #{stage_count} #{'stage'.pluralize(stage_count)}. - %tr.footer - %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } - %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ - %div - %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications - · - %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help - %div - You're receiving this email because of your account on - = succeed "." do - %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" } + = @pipeline.ref + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } + %tbody + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } + = @pipeline.short_sha + - if @merge_request + in + %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" } + = @merge_request.to_reference + .commit{ style: "color:#5c5c5c;font-weight:300;" } + = @pipeline.git_commit_message.truncate(50) + %tr + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" } + %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" } + %tbody + %tr + - commit = @pipeline.commit + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } + %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } + - if commit.author + %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } + = commit.author.name + - else + %span + = commit.author_name +%tr.spacer + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" } +   +%tr.success-message + %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" } + - build_count = @pipeline.statuses.latest.size + - stage_count = @pipeline.stages_count + Pipeline + %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" } + = "\##{@pipeline.id}" + successfully completed + #{build_count} #{'build'.pluralize(build_count)} + in + #{stage_count} #{'stage'.pluralize(stage_count)}. diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb index 40e5e306426..0970a3a4e09 100644 --- a/app/views/notify/pipeline_success_email.text.erb +++ b/app/views/notify/pipeline_success_email.text.erb @@ -18,7 +18,3 @@ Commit Author: <%= commit.author_name %> <% build_count = @pipeline.statuses.latest.size -%> <% stage_count = @pipeline.stages_count -%> Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) successfully completed <%= build_count %> <%= 'build'.pluralize(build_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>. - -You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>. -Manage all notifications: <%= profile_notifications_url %> -Help: <%= help_url %> From 0df104a389438665df2c7248ae022cdbb767f838 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 7 Feb 2017 16:06:27 +0100 Subject: [PATCH 093/129] use custom brand logo in pipeline mails --- app/helpers/emails_helper.rb | 14 +++++++++++++ app/views/layouts/mailer.html.haml | 2 +- spec/helpers/emails_helper_spec.rb | 32 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 2843ad96efa..3beddff9206 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -1,4 +1,6 @@ module EmailsHelper + include AppearancesHelper + # Google Actions # https://developers.google.com/gmail/markup/reference/go-to-action def email_action(url) @@ -49,4 +51,16 @@ module EmailsHelper msg = "This link is valid for #{password_reset_token_valid_time}. " msg << "After it expires, you can #{link_tag}." end + + def header_logo + if brand_item && brand_item.header_logo? + brand_header_logo + else + image_tag( + image_url('mailers/gitlab_header_logo.gif'), + size: "55x50", + alt: "GitLab" + ) + end + end end diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index 39133f8cdb3..53268cc22f8 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -48,7 +48,7 @@ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }   %tr.header %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } - %img{ alt: "GitLab", height: "50", src: image_url('mailers/gitlab_header_logo.gif'), width: "55" }/ + = header_logo %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" } %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" } diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index 3223556e1d3..b9519e387eb 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -43,4 +43,36 @@ describe EmailsHelper do end end end + + describe '#header_logo' do + context 'there is a brand item with a logo' do + it 'returns the brand header logo' do + appearance = create :appearance, header_logo: fixture_file_upload( + Rails.root.join('spec/fixtures/dk.png') + ) + + expect(header_logo).to eq( + %{Dk} + ) + end + end + + context 'there is a brand item without a logo' do + it 'returns the default header logo' do + create :appearance, header_logo: nil + + expect(header_logo).to eq( + %{GitLab} + ) + end + end + + context 'there is no brand item' do + it 'returns the default header logo' do + expect(header_logo).to eq( + %{GitLab} + ) + end + end + end end From 84420c89d5fbf98a19fb831a4489e9d5441e18ad Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 7 Feb 2017 18:23:58 +0100 Subject: [PATCH 094/129] add documentation for custom brand logo in email --- doc/README.md | 1 + .../branded_page_and_email_header.md | 15 +++++++++++++++ .../appearance.png | Bin 0 -> 10253 bytes .../custom_brand_header.png | Bin 0 -> 10014 bytes .../custom_email_header.png | Bin 0 -> 37472 bytes 5 files changed, 16 insertions(+) create mode 100644 doc/customization/branded_page_and_email_header.md create mode 100644 doc/customization/branded_page_and_email_header/appearance.png create mode 100644 doc/customization/branded_page_and_email_header/custom_brand_header.png create mode 100644 doc/customization/branded_page_and_email_header/custom_email_header.png diff --git a/doc/README.md b/doc/README.md index 1943d656aa7..c082b91a6b1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -50,6 +50,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. +- [Header logo](customization/branded_page_and_email_header.md) Change the logo on the overall page and email header. - [Reply by email](administration/reply_by_email.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. - [Git LFS configuration](workflow/lfs/lfs_administration.md) diff --git a/doc/customization/branded_page_and_email_header.md b/doc/customization/branded_page_and_email_header.md new file mode 100644 index 00000000000..9a0f0b382fa --- /dev/null +++ b/doc/customization/branded_page_and_email_header.md @@ -0,0 +1,15 @@ +# Changing the logo on the overall page and email header + +Navigate to the **Admin** area and go to the **Appearance** page. + +Upload the custom logo (**Header logo**) in the section **Navigation bar**. + +![appearance](branded_page_and_email_header/appearance.png) + +After saving the page, your GitLab navigation bar will contain the custom logo: + +![custom_brand_header](branded_page_and_email_header/custom_brand_header.png) + +The GitLab pipeline emails will also have the custom logo: + +![custom_email_header](branded_page_and_email_header/custom_email_header.png) diff --git a/doc/customization/branded_page_and_email_header/appearance.png b/doc/customization/branded_page_and_email_header/appearance.png new file mode 100644 index 0000000000000000000000000000000000000000..abbba6f9ac9facf1b58d2caf1c3aa09830cf0342 GIT binary patch literal 10253 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 literal 0 HcmV?d00001 diff --git a/doc/customization/branded_page_and_email_header/custom_brand_header.png b/doc/customization/branded_page_and_email_header/custom_brand_header.png new file mode 100644 index 0000000000000000000000000000000000000000..7390f8a5e4e82cf3a5fc9f8127e3d7a17f54d764 GIT binary patch literal 10014 zcmcI~cQ{*p*swl*N~={>R9ma1W-5xJcB@A1)(#OXA$Wu5dEWPXzw3Ix>;2<9mrKrl&iUQvUcY;u_m6ee8R)Om)6mc`XgpHU zr=d9mq@FvRrKSG&_2>9PLvw>hL*;>?&!^>7m}AO|=Qz*3EM-y~_;X98((sqisZI(q zXSHDWmi?ae^M81D%cfW4(i=W}^3LxPt;bxaGQhu(n|!+W z?rquK^71X4=}$|k4-@TPGHsr^|6(9X#c8Na@E8=tmybHO z!KS1Qn)rBlc%H}X4AHeo1$Hjw9aPVho@Zj(!x;+afPn%uBeKG<;a$~}#~6{0kim(! z-VTHgLKiz}EOU;l-h6b!whh9~!?Rt}Ze2d0L-#ZK21475LJXAP15xd!8!jNB4Uw)XHf~IzoP>WDRV{Pvm_I+5`J^+9Dv%)3d(c-e=+}xl%Veuex$9Z-x z_qDC3LrLA-QQj0!DvyVn{}o%pYuA-;bz1{C$`C^gd0=hNIBI<1|H9`~5`ZtFk9238 zDxTOXb%sqO7ZX+CO6kixOR6g058|Qcdb!RX%Xmbp6utSvlhy2Z{c$IDaF>@r&709Ijp4GvUizG+QKq(tl{8YW% z_n!}k`jS6Q`EL0t>~9Y6k^MMm$xw=k#u>c?hdTqn8d`6q2yfVk83}sP$WZmw3L$*`Yo<6oYvoX~1+lJ!scn zp@HO?np)S806!M?cc3R8RUpYbQzhLb5i>aSUf3r~uo%uq) z7a%NOW)UKQv4m0BllS)_!&PnQjHbA8ELZ{5L;dw!7W0l=ih*Ul{6&R~e7f?QOmy2I zN>tN@>x5#8a>Abd2>Wg`TJ1Oc9v)s6*J*D>Jwy`B}Zm^>k z)BO%pF;-`c?Cf0l)gB>Yu@rlvt&k~-T`B!uP2q32H#~DXM~PDFbX^fz7%GmXIov+zU;4BJ zgqzP|w59!&&5Y$UKi6EB;E*&Ly*u!J-Ae7NTaK+U^(wU2zcb8-_@g6QodsCD66rsK z!uOv|Gzl6`JO8s9m4KbL@s{)9$NiKc^LewA38j_&O$4A^ol$^psgikMAf}--eXvDa z&0PTEon;$Qc;|1G+TpkACZH%-9sK|)|M#rdK6$_KGwjZt&!>9SS0{5Rap>G;n*)vX zgB|m6%{G|R)BBlo@ovKe2W338OCpItV6tae9uz-x9S zH}kUAKj+P7M?ldy!_5J;i%w^Hq|9`E-CM>rgLE(oOhD14>Gld)lI_;ycZa|& zM+fPJn9iCz42hIr||1| z&0+v~!mSNQr=s$U+Z@$uGZtA}$p4n9MO!PU1rE|;;>|Zz9q2S;x~5inp!wqq5j(oO z?4%VlIC&AhO9n~Y=Dlh)#OL@4ZSLv|dhsI7vw61N-EVbhw{M+{?WwBNk%#l1kd-Q} zU{jY*#!QUY)IpQ@gx+qQ&_h-G=u96!R^`P2*7eY zoH`Pe&<>MOL|{=sac4_Ar0?if;MR_`VG^qF0A<-&H?w-0x))=LLoqxc5Yt1oJ0+FO z2EAv6o%0fOP0WfCUC%H){20t9kZL^UT9fKxiHy7qe70e?STYTHDTZxw)2fmKTMoB> zu>*OOP9erWxt~Ofy3VG$Mu-ys3a+GS%HJ4wuKs;p1XeEh60&B^_h2p~x{;CUQkABH zkek(Z>`bE$&dZ6FFUOI^Kg{suEydHpS|g5B=Qp|h(*gB6p}Y`oEtMUJchePEaz2ME z^qw=Tizb*|J`PNEy(x#QsjW}e5JNY`x5jZVuZj?1ODTm}gmfuWd+G*qwxm4ydrNoW zjN~)yZr{X|+sIYdSxnrqR?)UeNZ@4XxYHDlsCI1o=maVc&nb6n$%!)_6T@1%3i;0X zN)y*w-AfBJkipx~!mpX7SJ*tsZeoVHo?B+d7!<1ru#!u3oEb>G4sKgFYN*iwh(!EN z+DOyEkaV}uuhI&?_4d~B7L$YQ?r;okU-?NS8klR&$uYeJb``;V=6PbETfm)b)51&7 z#?APSwKyyy`kIK6?NxeOUd`;unZ*#y(2Q!~in1;;I9fkH3~rJ*IIpOf;OL9&WXU2_ z_#KcJ?R?$hvTT6r$fKzK%g+UUIJ@`**@K<%TI2KvK-fhfh0G6Q4yn=*pentTeN^}& zRNcU|Z#tpStz>Hc%~5cvSHv_Iz=54qPMsmz1N~I202hAvxZho;q4?hrlo$>&m}1z!?-G-Dttd zm4|sIzBjVS3YX?8J_|oY@tYmYLj)V$vQBa&$Wf3U*>wOLgK_Jl*HR~&{u4=e3iopH z#Si`+(zE|TFQp?UGKHbIdSvD7j(x7P#IlA zLW90*17_X$_iW?7t&o}JZj?U;?*?fZ{+(mkMvS4ZPCQu+9aTJW#0oDJ(@pTHIJdX6 zhxJ+^0yPfgwGOB0sc=kVEhe!0aMzQtL5}WWXJT5FKL@8-etjI2!Yl~ITCURC2S2$8 z5#ucp-pq&Yq}oL3!&P(6bgJ;DQ2!+_tLs(<7Anu082QXOcO9nq$5W#G4R;3W40o1b zB`E-`oT8G7N__<%_>W4u<7AP?t2o3P2GNQmEfy-(nuHwgH$?Z)F)@izo!TFj=l_1D zN;TRe;N4B( zuOKvY;=63+4mQw;6GJ{q0GY~`ZXNrZqr{WvIrhXZjx0^(dr900*T)I5g}_O=dAz7j z_k^0`MP8{V3Fq;mU$S(u?b(;gq`{`9^E(`uj$Z+r#a#^>>AHB9S{0lepD{PUfBXa7 z$38&*`48}_q{{?zIF9A>e1ASilf4-{oOYFG>W^#SL* zB(~o!ltoybO#S18%FlWlIr<$0x70M+`+Bh$LcC^9a?ru_bmo;SR}2MNs-ea-TS6@K zW~QcgLY4>46yGQ{?t%SP-G$dPb!88y4p-vUvSJq2;*>_3^d7A$p9p2%Ahz8)FOQ~o zpmJT}u9A{oCDl_1vY3T0XSpb9R{pxY3iH;a5>qBZ@z?~O2<4Sx2=v@nF*EYDX(B`I zYh%SwD$V=RbaJ=-aqKu;lb+gtVm~WH!%h`>11-MXn-4r3dLsSayi58tQndKXehf`E ztD49n#(!A6nN8z(p^0zvdN5`x0Se+i(aHUE;Hi3U@DvnWt8lfEm~u;O&xN%?apr+suZAuGu{Rxro(Cs9B9UeM&;B=xM+ZNY ze;OFtutGN0@_%z685TRMJQcDT#V^Bo; zz{kgrp7_U^s9gqTymhodRODPNOa*_vT^$^oGn(DRE~C-e=6|e!u-aTjZ95uD#vU`=?KjR|3kuDTOV6R->i8d*@EBDP!>aB2JT$ zq(s~*9Jh+CWHRjR&@5-&-eaw-omH)r0Czl*ziMc0pll9rh+)tGaQ%iW(%DGQD6GDP z`l<)<)6jfoN2WrrMd!trlAuoa9WMbf07=F#gV#6gyVAm5LIkrP+o{pTBx@$@&c)j5 zaSVU^>OIk!vACQ)<@@3ySO4p4B$>TT*RI@+*hEQhYqgArM5FHHYpF^s`a7oObDq{7 z_>r5Of@uPGvremPCy1sZuuTj3O)ccrh{fPkd#IynXmsiT%0OLn<05zXl5*~JqyyS^ zo>i6=C}O3YrI;Q-{j*|Dj%EW`7#sWO%c*z7gg6ykr48h3V+FBp8@Dxkc!1GNjoKQq zS@|$!IrRvu$d>`a;QfuHDweeve|PqwogH1WFwgTD-%?2>^Rk6=QRIMivf5omR^uUv zCsyVK4k}RyXpcwNqINSVnY=Gta|)hS2ec5jv^wVB=>~u;GmygIR>$dJwWON|OLkVx>(#e2Tdp%b*gMTQ7z%C<7jn(%57m%qmIQD zldK zA*^HGhQwd-`tC%JTO9qJeKI6hmx;i(?mB=1F;JE`-0_1v$GaPA92m7*oq zmS(k=j08PQb3)QE?y};{{rItjbV(wj4yxIQ2={$traM|reM`=q$HB;{5am$GIz!^f zM%%;<-=9g{UEMuhfH$yVn>W2!J>TTz*QB9@77`ztDhtU+A#!*W{_;Y)W;mX#4 ztmN=p)}X{rpNWV7=c^nsrVbQ^fCi|&ye2I>kVQ8#O5j<3{3nT(yjOGTWb|tL0vvnpsM(P(l`Twn*&$e9O4` znrvWnaM7B8iICm*EcU*OY%gB#LUpDhikA%afB8`xqKXcmvNK2q(~c2|rpG ztLWLYtoj2`AYDmpHrWg&hjT?MWeLo=JPOeYPdm)m4xqe?2E#pO+WfMoliZMC{{u!2 zQtJR8XPIqf+o!%wGI#CxPy^I=P2wS=faPHGd!m`yIciALW3LX+V@(;ED=FDLW35qs z!KI4O8-E$30W$9^5MyAh6>}f|p|X|OVIwx*#I;jdrU%QSFOxKE?6})m4 z-g6##dR*=9H9eF1;qvAnEs%|kI=`gsY+xWXC4l*3RA_(H(@+A5?)vxp(j|r^^?<3Z zuJ>moEhv6B^7D;2ZU7{%$BS8@%u1)Qw@9_256;xPMnb{5LoNgkKAv~pY4+FDK7U(m ztn7UB_;vrzfOt~^w#jjY@)}w+eO0~h6+~^wX=NBu+F$;h(3TgKFKwAc5(5Zu0TpoE zXT9!q)Z*Rzwn{zuG(ZyI{R96#IXl~`1JY+pBzS4Rv8zX$FcNU&L-j@?9l-DOd~Q~> zLs2gaclyt8(ua871|7h6`W+235LudYOOfVU4Q&!cPeGP`CjZeroP?|_oRk)%yr)d( zS>lD1ow4rbG!zWbF7V44PVtscD0Tl8Ynyemivs~roys{M+EHrXfW4sb(8Q5dPhE0_ zJpmVMt!tm%J``VQ4+KY>Ty$n1TVU_A<^jksMePNQ8Tm$6QFn27T^}X9M+|^wN%`Pn z-H~cgNc{E887Q+mJ4k0)UzyNuqK0Agm}_E{teA6KY9?rnxl&{re%t7F)E9?3G!xoS z*P1eOaqqsjuw?aJ1&hHVe!9bD)}z#(kulHZ4>;~kAn(uC z)ce`>FGVd#4xrJG_T6NQZc_lLVL>`^V0RYGJmvA`7h1bI8gWwiRX zL@x9%QJDSKuj@n-dw6oSBOoKJY&Xd*+YE+oS>rjURqfP%HzG#ThdVX@j|2M<=4y_k z;+%m8TxTi=*0e`F@K&`o1A*}Nt1zvu0LCxPHtwr1hjfJIjy4U={X?iZ7Af!cpgrV_ zh)*uOzTYbaFP<)IJQZZ&K9Jrq6e$v-wdz2pn`cxfDer#pS(#3^^uXUO)pgxHl$D+{ zaa?{w6LK%3nXW1MTnI}t;)&qhtgLoFym;ws-?fm;iX7H>GK!ZzB(ofz;?vbD;UNqj z>$6`Tllj7wu801}L=NCh7Uye^6R2kXP@$;GoP^aEQxqk4ud7zu9SzUP(kW30f*gpYaW`&)V}2OqNH-P%SOjSlrT zw2~DUT3fh9v^FY47hfYYcrhldsr!Zq_SFUajNU?@7B;)(w5K!D0NrZKw_!5nT4OJL z7QPZ7T$2eWLTy&~mh)w78^~DB7$_>Ia$!y$gb0Fo=Cd<&B;Er2JcD|s!`J_%9ys?? z*QLd30PmK)#%)>E)1FKp=>rfHnC_6eo`oO}aXI+6#VhAz<$HHN8Yu|-@NkS3P*TuS zyO#0Ic+K%SyxJH0XOw53u&@BW_;>O};mgthWsSITP(|*ALl1JGS(R{cH#WR&BmqL- zvC3GNUf`juwv1wTW-XOKvspKLRpZH+!ZQ(VNMeycbw3nKTfV1V&EhOEYZ>#s4xZ_6 z_!&)#4}KHW82RY&fRQ`o(El+j&^++4!D^VoW%%tVChvyoI14#?t!L^%O(%76` zfDBg7r3E_y6|(8()~EWDc1pQzmc*EMX17B=J1{1`$tDONza3)BqM%SXt88|@eLxne zhScUf5Ac{*UT9S%&pzog48cfRN^D->JO?+vZ-r*kxhjT86v5oRYf1kqHa>w>;x6G# zr@chsTvJX#J5xIUb8{9T_7lkr@1{NQz5}B(TE6VjGUKg_jDI<86Va#t(h4b$HfP+Q zHL&r}*n1&bAPcrM7!2qe2U=``R*`v|^EvGLa|{Xrb8X2G#VmEbhFm>*p?%e9zU9W7 z7DZq&koy4vK({%MfNwsUmIx(1d=ers<^#m^ATSD|s& zS(ajiiL5hnJl`roDSrvZ_@&9Fa22+YB#EZ`!WGd&E&n&qQ;UWfBfNNll?3ZyV^~eG zoY~%%+KkkikD}A!eLFSvx%x-7cMF>(o^ht84t>1l;_odnV;GDfj1^CiWjN7YP}GXU zyR-~uytp~a$SgL#x8w^H^!G{C%L z_5zag<<(VH!2VMVbHpg6KD()7|gZ1V4QLLMLew?nivjGvA$xbWiptF8@r@=SfXGtaVGsqp2I*;pkM zbd3+o0CzZY`I~R%e}?hr=H4~RU~*rK24%7U)vFeJoCWx;o`1LSmAUizb%uu1CjVSY zL=ES?OK}5&CHGc3s-AYnFJ}`*#9WM$2hETd1YcqgX#JzU4y{2)rvtQ#s@x<2ZIMF1 zTE65@wlg;SnYT<5lfF%Fu>oAHhf1KRR?__;t2G1-&8whiTFp8AvVk|U5zWMSv)F9D zqoQa2KA3GTHaVr?b2iCy&(1WX_d!d~=t`H9ZO@YO+LdvgcbNVtS6jWMALtKMmu#PL zx6h`FGu5@mJ2YmfF?zNZ0MxUC*r|o;^#~)(dX@X@ms#OU=((KpvK(oBHzg)_U=K~c z4&mLb;r_gzRGtjzk0lQ$xv11dCTU}0ZQfx3Y^g~+eZhNMDk=t9icz~d8urr9S2h93 zP?u!0RV!<^GD`iS_uR?s^YMtCli20sy zN2i51?z&Jh5^Qyp3>yc$tbEz2ay!#*VYQZO7U?L2Nh*6}sg{2;fm#nOzEX@eM9cJ4 z_|2^|Xb5qV9o765162Kk*56s)GcThTbDsZEo;~f!AG114Qq)4lb<|tb&g|p{W2U>j z=&OS~4nqB!ZsR@?`)Z|$AeVz4pBR5>BKbF~!bTi+^T*ywndpmKllQXNI!!8x0(6_- z+ij=Jn+Rr=lHjqHly$>kEzl_j=`vP-$G?l^v|+X89DiPS^}e<6jiNH6dIZ^s(}CPz zC0zYROOtK&ld)fbsR5xbK`=ETKHA5M0bZK1z-3|CU>hnaH6N`sYU6FDD{z6l|6sJ7 zb*&W5yt%lHs(w@a4l`~1Tdh>Vp(Ln2r+9km3dEtIg>LBo1JWTfXKp6{Sqw&ov8XDTsqi+F9z@8{-KIv8HqqPcanPF(8Gx~jG zTnEn_nsX}e`fJm@uw~f-P9#C@X{>ssd-*hc(pV)c518q)^0PaUpYaY*3W6uOsMqIHZbqdwSPYEAOIDIdij$e!FNwUUd#FqEv3fJnf zY3aoynSu@GuvN~zZFYW!0LjufrN_d6#JDX&nz46AGRTWgc?x)ho_j(H-Q4ad} zfpr}p-G<_xs$tEpaRzt`d8Bq)S8W4wVCt`1;(DA!0u<5plmg3bsa5Dfi?(@n!$FkkaEi((Wxu z%&B&@&H!D8@gSUG9;dop`J{Z5RMav$x+v#&CFH@XD88jGvd*Q?5BIk6`P4`YDZ7e< zQ?OlLeZY5Ipup@zvN!K(RmjS_-_IKWt6r9hDLxOMeAM7TZ7!C1;>-0We{QdI#X~{q zz_V~S*ph_Imz$#e&KpsHOQq!I{`Z(hchk2mz0&ux@qOpch*WU^L*Lc^u5nVSYf7x+ zu`d=IPJuL7RV4BlSW)Z0=nu_V1_WrLBx@SS5Og7WrujO_**dnWDfy}pE@7euklQn3 zaGg`Qt@|5ZOh;9&E?Ic#7RxzW;gTV?4@}X8m2yKGNN=m{J?eyO7Wfs@@jleX5R7$^ zy|l^YAO+UcH?zGvKXvs7yApUSWudaj6iGqC%w9h=EcC`+;Z1nr=X~R8!H99+1o`wg zQ?e&k!FHc)tU&$P0_JW;4D1{eyEZ@|-MLgE4->G1x^p!`Ui+Bo-hN!6AYY8c!<8I` z>Q+eiJCZ*pvxXI;4+~>J`wIDrX~&WIbOX2kFmGv2$eA3rIcu(qfBCexsCk^A2!Eqj-#ejOnJM{Ph`4W&_898w& z@ekhdj>7waYAsdl8PX#Yj}kmNM&Ey>9K3o?A)z&P<-qGl?nJkL@8+4$6%!!*<=s>N zbcEe_t(nIpXPvmY5x)EVf(#~yL)apk`?p;7K9ZSKD~ zY5%uv>tL2wQ4f&8mT#e>b(eul5C7@v|4)5)HGtB?jg5_3&!yoj-Ly}g=tJJ<7Z?uo zG_H23dmfe*P&@e(#e2Ba{u-R0<9Q{{hV%b)=Mm}YCP4Vo=3N@USTGQ)|7VA_ICb9e zIOFk16X?`n-eW<#XU`rWv~w4%xRl7JqtFTkdpJHzFIlr_>S>$a!PE0fH~$kI2n2c} zoS-j`Q2BmECr6)Jn%OVB0LHcdmI&n(OVF%h5s8owTBqCHHji0AOQT~r*T-EH)GEQJ z|G!LXj)yw`(@01s1cM78YyC3#cm7k$)I{Js`X=r8xG&vrhy;Vd$D^v_8JEaq<7UbK i)wt*w(f>(A^dY13LnFToKjibHyBeyxDkaKK-~0zHf1jTK literal 0 HcmV?d00001 diff --git a/doc/customization/branded_page_and_email_header/custom_email_header.png b/doc/customization/branded_page_and_email_header/custom_email_header.png new file mode 100644 index 0000000000000000000000000000000000000000..705698ef4a8ca046c47b1702a5a731753d63c7dd GIT binary patch literal 37472 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 Ivaf^x8xA~MP5=M^ literal 0 HcmV?d00001 From 26da2c45ac4071f7a33a34c89162709d5a1bc470 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 7 Feb 2017 22:17:59 +0100 Subject: [PATCH 095/129] add changelog entry --- changelogs/unreleased/feature-brand-logo-in-emails.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/feature-brand-logo-in-emails.yml diff --git a/changelogs/unreleased/feature-brand-logo-in-emails.yml b/changelogs/unreleased/feature-brand-logo-in-emails.yml new file mode 100644 index 00000000000..a7674b9b25e --- /dev/null +++ b/changelogs/unreleased/feature-brand-logo-in-emails.yml @@ -0,0 +1,4 @@ +--- +title: Brand header logo for pipeline emails +merge_request: 9049 +author: Alexis Reigel From c1e94479bd565a3deebe7452f186815157082cbb Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 17 Feb 2017 10:05:13 +0100 Subject: [PATCH 096/129] restrict height of the custom brand logo in emails --- app/helpers/emails_helper.rb | 5 ++++- spec/helpers/emails_helper_spec.rb | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 3beddff9206..a6d9e37ac76 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -54,7 +54,10 @@ module EmailsHelper def header_logo if brand_item && brand_item.header_logo? - brand_header_logo + image_tag( + brand_item.header_logo, + style: 'height: 50px' + ) else image_tag( image_url('mailers/gitlab_header_logo.gif'), diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index b9519e387eb..cd112dbb2fb 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -52,7 +52,7 @@ describe EmailsHelper do ) expect(header_logo).to eq( - %{Dk} + %{Dk} ) end end From ad88cf3e1747f1214d6d5de45862cdac98b11504 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 10 Feb 2017 10:32:59 +0000 Subject: [PATCH 097/129] updated ci skip regex to accept underscores and hyphens --- app/services/ci/create_pipeline_service.rb | 3 +- .../ci/create_pipeline_service_spec.rb | 83 ++++++++----------- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index e3bc9847200..38a85e9fc42 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -59,7 +59,8 @@ module Ci private def skip_ci? - pipeline.git_commit_message =~ /\[(ci skip|skip ci)\]/i if pipeline.git_commit_message + return false unless pipeline.git_commit_message + pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i end def commit diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index ceaca96e25b..8459a3d8cfb 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -79,60 +79,34 @@ describe Ci::CreatePipelineService, services: true do context 'when commit contains a [ci skip] directive' do let(:message) { "some message[ci skip]" } - let(:messageFlip) { "some message[skip ci]" } - let(:capMessage) { "some message[CI SKIP]" } - let(:capMessageFlip) { "some message[SKIP CI]" } + + ci_messages = [ + "some message[ci skip]", + "some message[skip ci]", + "some message[CI SKIP]", + "some message[SKIP CI]", + "some message[ci_skip]", + "some message[skip_ci]", + "some message[ci-skip]", + "some message[skip-ci]" + ] before do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } end - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: message }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) + ci_messages.each do |ci_message| + it "skips builds creation if the commit message is #{ci_message}" do + commits = [{ message: ci_message }] + pipeline = execute(ref: 'refs/heads/master', + before: '00000000', + after: project.commit.id, + commits: commits) - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") - end - - it "skips builds creation if there is [skip ci] tag in commit message" do - commits = [{ message: messageFlip }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) - - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") - end - - it "skips builds creation if there is [CI SKIP] tag in commit message" do - commits = [{ message: capMessage }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) - - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") - end - - it "skips builds creation if there is [SKIP CI] tag in commit message" do - commits = [{ message: capMessageFlip }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) - - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end end it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do @@ -148,6 +122,19 @@ describe Ci::CreatePipelineService, services: true do expect(pipeline.builds.first.name).to eq("rspec") end + it "does not skip builds creation if the commit message is nil" do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { nil } + + commits = [{ message: nil }] + pipeline = execute(ref: 'refs/heads/master', + before: '00000000', + after: project.commit.id, + commits: commits) + + expect(pipeline).to be_persisted + expect(pipeline.builds.first.name).to eq("rspec") + end + it "fails builds creation if there is [ci skip] tag in commit message and yaml is invalid" do stub_ci_pipeline_yaml_file('invalid: file: fiile') commits = [{ message: message }] From 3bf60f8a64d7f6fd0d642378e6ed70785b65405c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 12:34:16 +0100 Subject: [PATCH 098/129] Change images location (s/images/img) --- doc/pages/images/add_certificate_to_pages.png | Bin 58581 -> 0 bytes doc/pages/images/choose_ci_template.png | Bin 75580 -> 0 bytes doc/pages/images/dns_a_record_example.png | Bin 10681 -> 0 bytes doc/pages/images/dns_cname_record_example.png | Bin 11972 -> 0 bytes doc/pages/images/remove_fork_relashionship.png | Bin 39941 -> 0 bytes doc/pages/images/setup_ci.png | Bin 27380 -> 0 bytes doc/pages/img/add_certificate_to_pages.png | Bin 0 -> 14608 bytes doc/pages/img/choose_ci_template.png | Bin 0 -> 23532 bytes doc/pages/img/dns_a_record_example.png | Bin 0 -> 4709 bytes doc/pages/img/dns_cname_record_example.png | Bin 0 -> 5004 bytes doc/pages/img/remove_fork_relashionship.png | Bin 0 -> 13646 bytes doc/pages/img/setup_ci.png | Bin 0 -> 10033 bytes doc/pages/pages_quick_start_guide.md | 12 ++++++------ ..._domains_dns_records_ssl_tls_certificates.md | 6 +++--- 14 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 doc/pages/images/add_certificate_to_pages.png delete mode 100644 doc/pages/images/choose_ci_template.png delete mode 100644 doc/pages/images/dns_a_record_example.png delete mode 100644 doc/pages/images/dns_cname_record_example.png delete mode 100644 doc/pages/images/remove_fork_relashionship.png delete mode 100644 doc/pages/images/setup_ci.png create mode 100644 doc/pages/img/add_certificate_to_pages.png create mode 100644 doc/pages/img/choose_ci_template.png create mode 100644 doc/pages/img/dns_a_record_example.png create mode 100644 doc/pages/img/dns_cname_record_example.png create mode 100644 doc/pages/img/remove_fork_relashionship.png create mode 100644 doc/pages/img/setup_ci.png diff --git a/doc/pages/images/add_certificate_to_pages.png b/doc/pages/images/add_certificate_to_pages.png deleted file mode 100644 index 94d48fafa6e88806c89d09a5262cbef2cd294470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58581 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 diff --git a/doc/pages/images/choose_ci_template.png b/doc/pages/images/choose_ci_template.png deleted file mode 100644 index dea4ad2ed28e3c00f4fb80bb71feb744747e0275..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75580 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 aE`^QnMGa;+G4S(CxMxyIl3&G*-~Jz)wGcx9 diff --git a/doc/pages/images/dns_cname_record_example.png b/doc/pages/images/dns_cname_record_example.png deleted file mode 100644 index 93e011d743dd2bb99ac8f7d9c0221bd9409b4546..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11972 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 diff --git a/doc/pages/images/remove_fork_relashionship.png b/doc/pages/images/remove_fork_relashionship.png deleted file mode 100644 index 8c4fab929904d359d5c891f1e968453316c5a5d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39941 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

onIA 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

kHf)t@q}BfOzX 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�zEr1KTNhxXW5(@^Qu*)NTWtzV`5vUTN>C? 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 MMdxbqWy_HN0Lwr4qyPW_ diff --git a/doc/pages/images/setup_ci.png b/doc/pages/images/setup_ci.png deleted file mode 100644 index 5569f6aacf5ee90ebd45929695c736731fc036eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27380 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 diff --git a/doc/pages/img/add_certificate_to_pages.png b/doc/pages/img/add_certificate_to_pages.png new file mode 100644 index 0000000000000000000000000000000000000000..d92a981dc6037962f043956be97ecc5e45378869 GIT binary patch literal 14608 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 literal 0 HcmV?d00001 diff --git a/doc/pages/img/choose_ci_template.png b/doc/pages/img/choose_ci_template.png new file mode 100644 index 0000000000000000000000000000000000000000..0697542abc863152a37e1ebe3b25a7beb5c773fc GIT binary patch literal 23532 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 literal 0 HcmV?d00001 diff --git a/doc/pages/img/dns_a_record_example.png b/doc/pages/img/dns_a_record_example.png new file mode 100644 index 0000000000000000000000000000000000000000..b923730388a8dfa2bac3abc66490bd65b2475f6f GIT binary patch literal 4709 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 literal 0 HcmV?d00001 diff --git a/doc/pages/img/setup_ci.png b/doc/pages/img/setup_ci.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce0431f4d4bb4076bef87e0680c383a362ff79f GIT binary patch literal 10033 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 Sr#=G!06A%8sVWKMAO8>AE09?L literal 0 HcmV?d00001 diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/pages_quick_start_guide.md index b86b451ba5e..b0a33136d06 100644 --- a/doc/pages/pages_quick_start_guide.md +++ b/doc/pages/pages_quick_start_guide.md @@ -46,8 +46,8 @@ Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've created for the s 1. Choose your SSG template 1. Fork a project from the [Pages group](https://gitlab.com/pages) 1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project** - - ![remove fork relashionship](images/remove_fork_relashionship.png) + + ![remove fork relashionship](img/remove_fork_relashionship.png) 1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **CI/CD Pipelines** 1. Trigger a build (push a change to any file) @@ -73,17 +73,17 @@ To turn a **project website** forked from the Pages group into a **user/group** 1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the [examples above](#practical-examples). 1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. 1. From the your **Project**'s page, click **Set up CI**: - - ![setup GitLab CI](images/setup_ci.png) + + ![setup GitLab CI](img/setup_ci.png) 1. Choose one of the templates from the dropbox menu. Pick up the template corresponding to the SSG you're using (or plain HTML). - ![gitlab-ci templates](images/choose_ci_template.png) + ![gitlab-ci templates](img/choose_ci_template.png) Once you have both site files and `.gitlab-ci.yml` in your project's root, GitLab CI will build your site and deploy it with Pages. Once the first build passes, you see your site is live by navigating to your **Project**'s **Settings** > **Pages**, where you'll find its default URL. > **Notes:** -> +> > - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) to our examples. > > - The second step _"Clone it to your local computer"_, can be done differently, achieving the same results: instead of cloning the bare repository to you local computer and moving your site files into it, you can run `git init` in your local website directory, add the remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, then add, commit, and push. diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md index c6e2cab0bc3..2564d87ac3a 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md @@ -86,7 +86,7 @@ In case you want to point a root domain (`example.com`) to your GitLab Pages sit **Practical Example:** -![DNS A record pointing to GitLab.com Pages server](images/dns_a_record_example.png) +![DNS A record pointing to GitLab.com Pages server](img/dns_a_record_example.png) #### DNS CNAME record @@ -96,7 +96,7 @@ Notice that, despite it's a user or project website, the `CNAME` should point to **Practical Example:** -![DNS CNAME record pointing to GitLab.com project](images/dns_cname_record_example.png) +![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png) #### TL;DR @@ -138,7 +138,7 @@ Regardless the CA you choose, the steps to add your certificate to your Pages pr 1. An intermediary certificate 1. A public key -![Pages project - adding certificates](images/add_certificate_to_pages.png) +![Pages project - adding certificates](img/add_certificate_to_pages.png) These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**. From bb483230e60692e203775346ac3ad4407e3864a5 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Thu, 12 Jan 2017 14:00:06 +0000 Subject: [PATCH 099/129] added specs for version check image --- .../javascripts/version_check_image.js.es6 | 16 ++++----- spec/features/help_pages_spec.rb | 26 ++++++++++++++ spec/helpers/version_check_helper_spec.rb | 34 +++++++++++++++++++ spec/javascripts/.eslintrc | 3 +- .../helpers/class_spec_helper.js.es6 | 2 ++ .../version_check_image_spec.js.es6 | 33 ++++++++++++++++++ 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 spec/helpers/version_check_helper_spec.rb create mode 100644 spec/javascripts/version_check_image_spec.js.es6 diff --git a/app/assets/javascripts/version_check_image.js.es6 b/app/assets/javascripts/version_check_image.js.es6 index 1fa2b5ac399..d4f716acb72 100644 --- a/app/assets/javascripts/version_check_image.js.es6 +++ b/app/assets/javascripts/version_check_image.js.es6 @@ -1,10 +1,10 @@ -(() => { - class VersionCheckImage { - static bindErrorEvent(imageElement) { - imageElement.off('error').on('error', () => imageElement.hide()); - } +class VersionCheckImage { + static bindErrorEvent(imageElement) { + imageElement.off('error').on('error', () => imageElement.hide()); } +} - window.gl = window.gl || {}; - gl.VersionCheckImage = VersionCheckImage; -})(); +window.gl = window.gl || {}; +gl.VersionCheckImage = VersionCheckImage; + +module.exports = VersionCheckImage; diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 40a1fced8d8..e0b2404e60a 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -33,4 +33,30 @@ describe 'Help Pages', feature: true do it_behaves_like 'help page', prefix: '/gitlab' end end + + context 'in a production environment with version check enabled', js: true do + before do + allow(Rails.env).to receive(:production?) { true } + allow(current_application_settings).to receive(:version_check_enabled) { true } + allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' } + + login_as :user + visit help_path + end + + it 'should display a version check image' do + expect(find('.js-version-status-badge')).to be_visible + end + + it 'should have a src url' do + expect(find('.js-version-status-badge')['src']).to match(/\/version-check-url/) + end + + it 'should hide the version check image if the image request fails' do + # We use '--load-images=no' with poltergeist so we must trigger manually + execute_script("$('.js-version-status-badge').trigger('error');") + + expect(find('.js-version-status-badge', visible: false)).not_to be_visible + end + end end diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb new file mode 100644 index 00000000000..889fe441171 --- /dev/null +++ b/spec/helpers/version_check_helper_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe VersionCheckHelper do + describe '#version_status_badge' do + it 'should return nil if not dev environment and not enabled' do + allow(Rails.env).to receive(:production?) { false } + allow(current_application_settings).to receive(:version_check_enabled) { false } + + expect(helper.version_status_badge).to be(nil) + end + + context 'when production and enabled' do + before do + allow(Rails.env).to receive(:production?) { true } + allow(current_application_settings).to receive(:version_check_enabled) { true } + allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' } + + @image_tag = helper.version_status_badge + end + + it 'should return an image tag' do + expect(@image_tag).to match(/^

'); + }); + + it('registers an error event', function () { + spyOn($.prototype, 'on'); + spyOn($.prototype, 'off').and.callFake(function () { return this; }); + + VersionCheckImage.bindErrorEvent(this.imageElement); + + expect($.prototype.off).toHaveBeenCalledWith('error'); + expect($.prototype.on).toHaveBeenCalledWith('error', jasmine.any(Function)); + }); + + it('hides the imageElement on error', function () { + spyOn($.prototype, 'hide'); + + VersionCheckImage.bindErrorEvent(this.imageElement); + + this.imageElement.trigger('error'); + + expect($.prototype.hide).toHaveBeenCalled(); + }); + }); +}); From 87fe6a27dd4c17cf6b202de809faa821712a3e17 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Wed, 22 Feb 2017 17:07:17 +0530 Subject: [PATCH 100/129] Document when current coverage configuration option was introduced * Introduced in v8.17 [skip ci] --- .../28524-gitlab-ci-yml-coverage-key-is-unknown.yml | 4 ++++ doc/ci/yaml/README.md | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml diff --git a/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml b/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml new file mode 100644 index 00000000000..eda5764c13e --- /dev/null +++ b/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml @@ -0,0 +1,4 @@ +--- +title: Document when current coverage configuration option was introduced +merge_request: 9443 +author: diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index a73598df812..dd3ba1283f8 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1003,6 +1003,9 @@ job: ### coverage +**Notes:** +- [Introduced][ce-7447] in GitLab 8.17. + `coverage` allows you to configure how code coverage will be extracted from the job output. @@ -1361,3 +1364,4 @@ CI with various languages. [ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669 [variables]: ../variables/README.md [ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983 +[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447 From 699aab02d09cee93570c41ba19251f7a772f8171 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 13:03:05 +0100 Subject: [PATCH 101/129] Generalize the naming of the getting started docs --- ...ficates.md => getting_started_part_one.md} | 10 ++-- ...ab-ci.md => getting_started_part_three.md} | 12 ++--- ...t_guide.md => getting_started_part_two.md} | 10 ++-- doc/pages/index.md | 46 +++++++++---------- doc/user/project/pages/index.md | 2 +- 5 files changed, 40 insertions(+), 40 deletions(-) rename doc/pages/{pages_static_sites_domains_dns_records_ssl_tls_certificates.md => getting_started_part_one.md} (94%) rename doc/pages/{pages_creating_and_tweaking_gitlab-ci.md => getting_started_part_three.md} (92%) rename doc/pages/{pages_quick_start_guide.md => getting_started_part_two.md} (91%) diff --git a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md b/doc/pages/getting_started_part_one.md similarity index 94% rename from doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md rename to doc/pages/getting_started_part_one.md index 2564d87ac3a..8a98afeeb86 100644 --- a/doc/pages/pages_static_sites_domains_dns_records_ssl_tls_certificates.md +++ b/doc/pages/getting_started_part_one.md @@ -5,8 +5,8 @@ > Level: beginner - **Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates** -- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ -- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ +- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ ---- @@ -145,7 +145,7 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New #### What's what? - A PEM certificate is the certificate generated by the CA, which needs to be added to the field **Certificate (PEM)**. -- An [intermediary certificate][] (aka "root certificate") is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. +- An [intermediary certificate][] \(aka "root certificate"\) is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. - A public key is an encrypted key which validates your PEM against your domain. #### Now what? @@ -160,5 +160,5 @@ Now that you hopefully understand why you need all of this, it's simple: ## Further Reading -- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ -- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ +- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ +- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ diff --git a/doc/pages/pages_creating_and_tweaking_gitlab-ci.md b/doc/pages/getting_started_part_three.md similarity index 92% rename from doc/pages/pages_creating_and_tweaking_gitlab-ci.md rename to doc/pages/getting_started_part_three.md index 8e1877c4981..0bc6a601a09 100644 --- a/doc/pages/pages_creating_and_tweaking_gitlab-ci.md +++ b/doc/pages/getting_started_part_three.md @@ -4,15 +4,15 @@ > > Level: intermediate +- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ +- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ - **Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages** -- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ ----- +--- ### Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages -[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves inumerous purposes, to build, test, and deploy your app from GitLab through [Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) methods. You will need it to build your website with GitLab Pages, and deploy it to the Pages server. +[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves numerous purposes, to build, test, and deploy your app from GitLab through [Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) methods. You will need it to build your website with GitLab Pages, and deploy it to the Pages server. What this file actually does is telling the [GitLab Runner](https://docs.gitlab.com/runner/) to run scripts as you would do from the command line. The Runner acts as your terminal. GitLab CI tells the Runner which commands to run. Both are built-in in GitLab, and you don't need to set up anything for them to work. @@ -275,5 +275,5 @@ What you can do with GitLab CI is pretty much up to your creativity. Once you ge ## Further Reading -- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html)_ +- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ +- Read through _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ diff --git a/doc/pages/pages_quick_start_guide.md b/doc/pages/getting_started_part_two.md similarity index 91% rename from doc/pages/pages_quick_start_guide.md rename to doc/pages/getting_started_part_two.md index b0a33136d06..b1d58b5b024 100644 --- a/doc/pages/pages_quick_start_guide.md +++ b/doc/pages/getting_started_part_two.md @@ -4,9 +4,9 @@ > > Level: beginner +- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ - **Part 2: Quick Start Guide - Setting Up GitLab Pages** -- _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ +- _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ ---- @@ -84,7 +84,7 @@ Once you have both site files and `.gitlab-ci.yml` in your project's root, GitLa > **Notes:** > -> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci_.html). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) to our examples. +> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) to our examples. > > - The second step _"Clone it to your local computer"_, can be done differently, achieving the same results: instead of cloning the bare repository to you local computer and moving your site files into it, you can run `git init` in your local website directory, add the remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, then add, commit, and push. @@ -108,5 +108,5 @@ baseurl: "" ## Further Reading -- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html)_ -- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html)_ +- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ +- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ diff --git a/doc/pages/index.md b/doc/pages/index.md index 54a951f0a2a..fe328748668 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -2,39 +2,39 @@ ## Product -- [Product Webpage](https://pages.gitlab.io) -- [We're Bringing GitLab Pages to CE](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/) -- [Pages Group - Templates](https://gitlab.com/pages) +- [Product webpage](https://pages.gitlab.io) +- [We're bringing GitLab Pages to CE](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/) +- [Pages group - templates](https://gitlab.com/pages) -## Getting Started +## Getting started -- Comprehensive Step-by-Step Guide: [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) +- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) a comprehensive step-by-step guide - GitLab Pages from A to Z - - [Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](pages_static_sites_domains_dns_records_ssl_tls_certificates.html) - - [Part 2: Quick Start Guide - Setting Up GitLab Pages](pages_quick_start_guide.html) - - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) - - Video tutorial: [How to Publish a Website with GitLab Pages on GitLab.com: from scratch](#LINK) - - [Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](pages_creating_and_tweaking_gitlab-ci.html) -- Secure GitLab Pages Custom Domain with SSL/TLS Certificates + - [Part 1: Static sites, domains, DNS records, and SSL/TLS certificates](getting_started_part_one.md) + - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) + - Video tutorial: [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) + - Video tutorial: [How to publish a website with GitLab Pages on GitLab.com: from scratch](#LINK) + - [Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md) +- Secure GitLab Pages custom domain with SSL/TLS certificates - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) - [StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/) -- Static Site Generators - Blog Posts Series - - [SSGs Part 1: Static vs Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) - - [SSGs Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) - - [SSGs Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) +- Static Site Generators - Blog posts series + - [SSGs part 1: Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) + - [SSGs part 2: Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) + - [SSGs part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) - [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) -## Advanced Use +## Advanced use - Blog Posts: - - [GitLab CI: Run jobs Sequentially, in Parallel, or Build a Custom Pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) - - [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) - - [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) - - [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) + - [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) + - [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) + - [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) + - [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) -## General Documentation +## General documentation -- [User docs](../user/project/pages/) -- [Admin docs](administration.html) +- [User docs](../user/project/pages/index.md) +- [Admin docs](../administration/pages/index.md) - Video tutorial - [How to Enable GitLab Pages for GitLab CE and EE](https://youtu.be/dD8c7WNcc6s) diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index e07b7e83f81..816600964de 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -445,5 +445,5 @@ For a list of known issues, visit GitLab's [public issue tracker]. [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 [quick start guide]: ../../../ci/quick_start/README.md [pages-index-guide]: ../../../pages/ -[pages-quick]: ../../../pages/pages_quick_start_guide.html +[pages-quick]: ../../../pages/getting_started_part_one.md [video-pages-fork]: https://youtu.be/TWqh9MtT4Bg From d3425933dddf4e849199c06dd3ce00c212d0c6da Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Tue, 21 Feb 2017 20:21:31 +0530 Subject: [PATCH 102/129] Add housekeeping endpoint for Projects API --- .../27032-add-a-house-keeping-api-call.yml | 4 ++ doc/api/projects.md | 14 ++++++ lib/api/projects.rb | 13 +++++ spec/requests/api/projects_spec.rb | 49 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 changelogs/unreleased/27032-add-a-house-keeping-api-call.yml diff --git a/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml b/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml new file mode 100644 index 00000000000..a9f70e339c0 --- /dev/null +++ b/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml @@ -0,0 +1,4 @@ +--- +title: Add housekeeping endpoint for Projects API +merge_request: 9421 +author: diff --git a/doc/api/projects.md b/doc/api/projects.md index e9ef03a0c0c..872f570e0f6 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1195,3 +1195,17 @@ Parameters: | `query` | string | yes | A string contained in the project name | | `order_by` | string | no | Return requests ordered by `id`, `name`, `created_at` or `last_activity_at` fields | | `sort` | string | no | Return requests sorted in `asc` or `desc` order | + +## Start the Housekeeping task for a Project + +>**Note:** This feature was introduced in GitLab 9.0 + +``` +POST /projects/:id/housekeeping +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 366e5679edd..f1cb1b22143 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -374,6 +374,19 @@ module API present paginate(users), with: Entities::UserBasic end + + desc 'Start the housekeeping task for a project' do + detail 'This feature was introduced in GitLab 9.0.' + end + post ':id/housekeeping' do + authorize_admin_project + + begin + ::Projects::HousekeepingService.new(user_project).execute + rescue ::Projects::HousekeepingService::LeaseTaken => error + conflict!(error.message) + end + end end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 4e90aae9279..2f1181b2e8c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1422,4 +1422,53 @@ describe API::Projects, api: true do end end end + + describe 'POST /projects/:id/housekeeping' do + let(:housekeeping) { Projects::HousekeepingService.new(project) } + + before do + allow(Projects::HousekeepingService).to receive(:new).with(project).and_return(housekeeping) + end + + context 'when authenticated as owner' do + it 'starts the housekeeping process' do + expect(housekeeping).to receive(:execute).once + + post api("/projects/#{project.id}/housekeeping", user) + + expect(response).to have_http_status(201) + end + + context 'when housekeeping lease is taken' do + it 'returns conflict' do + expect(housekeeping).to receive(:execute).once.and_raise(Projects::HousekeepingService::LeaseTaken) + + post api("/projects/#{project.id}/housekeeping", user) + + expect(response).to have_http_status(409) + expect(json_response['message']).to match(/Somebody already triggered housekeeping for this project/) + end + end + end + + context 'when authenticated as developer' do + before do + project_member2 + end + + it 'returns forbidden error' do + post api("/projects/#{project.id}/housekeeping", user3) + + expect(response).to have_http_status(403) + end + end + + context 'when unauthenticated' do + it 'returns authentication error' do + post api("/projects/#{project.id}/housekeeping") + + expect(response).to have_http_status(401) + end + end + end end From 3d013487cea47272ff40d3f9a4b02960d2c2591f Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 22 Feb 2017 11:03:04 +0100 Subject: [PATCH 103/129] Grapify the CI::Runners API --- lib/ci/api/runners.rb | 44 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index bcc82969eb3..2a611a67eaf 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -1,44 +1,36 @@ module Ci module API - # Runners API class Runners < Grape::API resource :runners do - # Delete runner - # Parameters: - # token (required) - The unique token of runner - # - # Example Request: - # GET /runners/delete + desc 'Delete a runner' + params do + requires :token, type: String, desc: 'The unique token of the runner' + end 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 + desc 'Register a new runner' do + success Entities::Runner + end + params do + requires :token, type: String, desc: 'The unique token of the runner' + optional :description, type: String, desc: 'The description of the runner' + optional :tag_list, type: Array[String], desc: 'A list of tags the runner should run for' + optional :run_untagged, type: Boolean, desc: 'Flag if the runner should execute untagged jobs' + optional :locked, type: Boolean, desc: 'Lock this runner for this specific project' + end post "register" do - required_attributes! [:token] - - attributes = attributes_for_keys( - [:description, :tag_list, :run_untagged, :locked] - ) + runner_params = declared(params, include_missing: false) runner = if runner_registration_token_valid? # Create shared runner. Requires admin access - Ci::Runner.create(attributes.merge(is_shared: true)) - elsif project = Project.find_by(runners_token: params[:token]) + Ci::Runner.create(runner_params.merge(is_shared: true)) + elsif project = Project.find_by(runners_token: runner_params[:token]) # Create a specific runner for project. - project.runners.create(attributes) + project.runners.create(runner_params) end return forbidden! unless runner From 80033d8c326aa2b4c4e4ed7da7ab7e174af27451 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 22 Feb 2017 11:33:23 -0300 Subject: [PATCH 104/129] Update CHANGELOG.md for 8.17.0 [ci skip] --- CHANGELOG.md | 180 ++++++++++++++++++ changelogs/unreleased/17662-rename-builds.yml | 4 - ...uire-from-request_profiler-initializer.yml | 4 - ...ect-better-blank-state-for-labels-view.yml | 4 - .../21518_recaptcha_spam_issues.yml | 4 - .../22007-unify-projects-search.yml | 4 - ...ing-a-branch-matching-a-wildcard-fails.yml | 4 - ...974-trigger-service-events-through-api.yml | 4 - ...-notify-automerge-user-of-failed-build.yml | 4 - .../23634-remove-project-grouping.yml | 4 - ...sable-storing-of-sensitive-information.yml | 4 - .../unreleased/24147-delete-env-button.yml | 4 - ...606-force-password-reset-on-next-login.yml | 4 - .../unreleased/24716-fix-ctrl-click-links.yml | 4 - ...5_refactor_merge_request_build_service.yml | 4 - ...o-search-by-commit-hash-within-project.yml | 4 - changelogs/unreleased/24923_nested_tasks.yml | 4 - ...w-doesn-t-show-organization-membership.yml | 4 - .../25312-search-input-cmd-click-issue.yml | 4 - ...0-remove-flash-warning-from-login-page.yml | 4 - .../25460-replace-word-users-with-members.yml | 4 - ...ipate-obstacles-to-removing-turbolinks.yml | 4 - ...-update-when-new-pipeline-is-triggered.yml | 4 - ...cons-to-svg-to-propperly-position-them.yml | 4 - ...25989-fix-rogue-scrollbars-on-comments.yml | 4 - .../unreleased/26059-segoe-ui-vertical.yml | 4 - .../unreleased/26068_tasklist_issue.yml | 4 - .../26117-sort-pipeline-for-commit.yml | 4 - ...and-minus-signs-as-part-of-line-number.yml | 4 - .../26445-accessible-piplelines-buttons.yml | 4 - .../unreleased/26447-fix-tab-list-order.yml | 4 - .../26468-fix-users-sort-in-admin-area.yml | 4 - .../26787-add-copy-icon-hover-state.yml | 4 - ...ible-when-there-are-no-lines-to-unfold.yml | 5 - .../26852-fix-slug-for-openshift.yml | 4 - ...move-hover-animation-from-row-elements.yml | 4 - .../26881-backup-fails-if-data-changes.yml | 4 - ...920-hover-cursor-on-pagination-element.yml | 4 - .../26947-build-status-self-link.yml | 4 - ...ipeline-status-icon-linking-in-widgets.yml | 4 - .../27013-regression-in-commit-title-bar.yml | 4 - .../27014-fix-pipeline-tooltip-wrapping.yml | 4 - ...21-line-numbers-now-in-copy-pasta-data.yml | 4 - ...update-builds-link-in-project-settings.yml | 4 - .../27240-make-progress-bars-consistent.yml | 4 - ...-mini-pipeline-graph-glitch-upon-hover.yml | 4 - .../27291-unify-mr-diff-file-buttons.yml | 4 - ...parator-line-in-edit-projects-settings.yml | 4 - ...-no-line-spacing-in-firefox-and-safari.yml | 4 - .../27352-search-label-filter-header.yml | 4 - ...95-reduce-group-activity-sql-queries-2.yml | 4 - ...7395-reduce-group-activity-sql-queries.yml | 4 - .../27484-environment-show-name.yml | 4 - .../unreleased/27488-fix-jwt-version.yml | 4 - .../27494-environment-list-column-headers.yml | 4 - ...avatar-border-flicker-mention-dropdown.yml | 4 - .../unreleased/27632_fix_mr_widget_url.yml | 4 - ...er-the-side-panel-in-the-merge-request.yml | 4 - .../unreleased/27656-doc-ci-enable-ci.yml | 4 - ...alization-graph-page-with-roboto-fonts.yml | 4 - .../27822-default-bulk-assign-labels.yml | 4 - ...it-comments-are-shared-across-projects.yml | 4 - ...elines-table-not-showing-commit-branch.yml | 4 - ...acter-is-part-of-an-autocompleted-text.yml | 4 - .../27922-cmd-click-todo-doesn-t-work.yml | 5 - ...925-fix-mr-stray-pipelines-api-request.yml | 4 - ...-merge-request-pipelines-displays-json.yml | 4 - .../27939-fix-current-build-arrow.yml | 4 - ...-list-on-profile-page-is-aligned-right.yml | 4 - ...-icon-and-text-in-merge-request-widget.yml | 4 - ...-mr-notification-use-pipeline-language.yml | 4 - changelogs/unreleased/27963-tooltips-jobs.yml | 4 - ...ranch-ref-switcher-input-filter-broken.yml | 4 - .../27987-skipped-pipeline-mr-graph.yml | 4 - .../27991-success-with-warnings-caret.yml | 4 - ...mprove-blockquote-formatting-on-emails.yml | 4 - .../unreleased/28032-tooltips-file-name.yml | 5 - ...-add-pagination-to-admin-abuse-reports.yml | 4 - ...x-notification-when-group-set-to-watch.yml | 4 - changelogs/unreleased/8-15-stable.yml | 4 - .../unreleased/8082-permalink-to-file.yml | 4 - changelogs/unreleased/9-0-api-changes.yml | 4 - ...ommand-for-target-merge-request-branch.yml | 4 - .../unreleased/add_project_update_hook.yml | 4 - .../api-remove-snippets-expires-at.yml | 4 - .../unreleased/babel-all-the-things.yml | 5 - ..._doing_offline_update_check-help_page-.yml | 4 - changelogs/unreleased/change_queue_weight.yml | 4 - .../clipboard-button-commit-sha.yml | 3 - .../contribution-calendar-scroll.yml | 4 - changelogs/unreleased/cop-gem-fetcher.yml | 4 - changelogs/unreleased/copy-as-md.yml | 4 - ...-autologin-on-email-confirmation-links.yml | 4 - changelogs/unreleased/display-project-id.yml | 4 - changelogs/unreleased/document-how-to-vue.yml | 4 - .../unreleased/dynamic-todos-fixture.yml | 4 - .../dz-nested-groups-improvements-2.yml | 4 - .../empty-selection-reply-shortcut.yml | 4 - .../unreleased/fe-commit-mr-pipelines.yml | 4 - ...success-warning-icons-in-stages-builds.yml | 4 - changelogs/unreleased/fix-27479.yml | 4 - .../unreleased/fix-api-mr-permissions.yml | 4 - .../unreleased/fix-ar-connection-leaks.yml | 4 - changelogs/unreleased/fix-ci-build-policy.yml | 4 - .../unreleased/fix-deleting-project-again.yml | 4 - changelogs/unreleased/fix-depr-warn.yml | 4 - ...ackwards-compatibility-coverage-ci-yml.yml | 4 - .../fix-guest-access-posting-to-notes.yml | 4 - .../unreleased/fix-import-encrypt-atts.yml | 4 - .../unreleased/fix-import-group-members.yml | 4 - .../fix-job-to-pipeline-renaming.yml | 4 - .../fix-references-header-parsing.yml | 5 - changelogs/unreleased/fix-scroll-test.yml | 4 - ...-users-deleting-public-deployment-keys.yml | 4 - .../fix_broken_diff_discussions.yml | 4 - ...g_message_in_admin_background_job_page.yml | 4 - .../unreleased/fwn-to-find-by-full-path.yml | 4 - ...tification_service_spec-to-make-it-DRY.yml | 4 - .../unreleased/git_to_html_redirection.yml | 4 - .../unreleased/go-go-gadget-webpack.yml | 4 - .../unreleased/group-label-sidebar-link.yml | 4 - .../unreleased/hardcode-title-system-note.yml | 4 - .../unreleased/improve-ci-example-php-doc.yml | 4 - .../improve-handleLocationHash-tests.yml | 4 - .../unreleased/issuable-sidebar-bug.yml | 4 - changelogs/unreleased/issue-20428.yml | 4 - .../issue-sidebar-empty-assignee.yml | 4 - changelogs/unreleased/issue_19262.yml | 4 - changelogs/unreleased/issue_23317.yml | 4 - changelogs/unreleased/issue_27211.yml | 4 - .../unreleased/jej-pages-picked-from-ee.yml | 4 - changelogs/unreleased/label-promotion.yml | 4 - .../unreleased/lfs-noauth-public-repo.yml | 4 - changelogs/unreleased/markdown-plantuml.yml | 4 - .../unreleased/merge-request-tabs-fixture.yml | 4 - ...linged-discussion-with-no-avatar-26854.yml | 4 - .../unreleased/mr-tabs-container-offset.yml | 4 - changelogs/unreleased/newline-eslint-rule.yml | 4 - .../no-sidebar-on-action-btn-click.yml | 4 - changelogs/unreleased/no_project_notes.yml | 4 - .../unreleased/pms-lowercase-system-notes.yml | 4 - ...redesign-searchbar-admin-project-26794.yml | 4 - ...irect-to-commit-when-only-commit-found.yml | 5 - changelogs/unreleased/relative-url-assets.yml | 4 - .../unreleased/remove-deploy-key-endpoint.yml | 4 - ...e-issue-and-mr-counts-from-labels-page.yml | 4 - changelogs/unreleased/route-map.yml | 4 - .../unreleased/rs-warden-blocked-users.yml | 4 - .../sh-add-index-to-ci-trigger-requests.yml | 4 - changelogs/unreleased/sh-add-labels-index.yml | 4 - changelogs/unreleased/slash-commands-typo.yml | 4 - .../small-screen-fullscreen-button.yml | 4 - .../snippets-search-performance.yml | 4 - .../tc-only-mr-button-if-allowed.yml | 4 - .../unreleased/terminal-max-session-time.yml | 4 - changelogs/unreleased/updated-pages-0-3-1.yml | 4 - changelogs/unreleased/upgrade-babel-v6.yml | 4 - changelogs/unreleased/upgrade-omniauth.yml | 4 - .../unreleased/upgrade-webpack-v2-2.yml | 4 - changelogs/unreleased/wip-mr-from-commits.yml | 4 - .../unreleased/zj-format-chat-messages.yml | 4 - .../zj-remove-deprecated-ci-service.yml | 4 - .../unreleased/zj-requeue-pending-delete.yml | 4 - 163 files changed, 180 insertions(+), 653 deletions(-) delete mode 100644 changelogs/unreleased/17662-rename-builds.yml delete mode 100644 changelogs/unreleased/20452-remove-require-from-request_profiler-initializer.yml delete mode 100644 changelogs/unreleased/20852-getting-started-project-better-blank-state-for-labels-view.yml delete mode 100644 changelogs/unreleased/21518_recaptcha_spam_issues.yml delete mode 100644 changelogs/unreleased/22007-unify-projects-search.yml delete mode 100644 changelogs/unreleased/22638-creating-a-branch-matching-a-wildcard-fails.yml delete mode 100644 changelogs/unreleased/22974-trigger-service-events-through-api.yml delete mode 100644 changelogs/unreleased/23524-notify-automerge-user-of-failed-build.yml delete mode 100644 changelogs/unreleased/23634-remove-project-grouping.yml delete mode 100644 changelogs/unreleased/23767-disable-storing-of-sensitive-information.yml delete mode 100644 changelogs/unreleased/24147-delete-env-button.yml delete mode 100644 changelogs/unreleased/24606-force-password-reset-on-next-login.yml delete mode 100644 changelogs/unreleased/24716-fix-ctrl-click-links.yml delete mode 100644 changelogs/unreleased/24795_refactor_merge_request_build_service.yml delete mode 100644 changelogs/unreleased/24833-Allow-to-search-by-commit-hash-within-project.yml delete mode 100644 changelogs/unreleased/24923_nested_tasks.yml delete mode 100644 changelogs/unreleased/25134-mobile-issue-view-doesn-t-show-organization-membership.yml delete mode 100644 changelogs/unreleased/25312-search-input-cmd-click-issue.yml delete mode 100644 changelogs/unreleased/25360-remove-flash-warning-from-login-page.yml delete mode 100644 changelogs/unreleased/25460-replace-word-users-with-members.yml delete mode 100644 changelogs/unreleased/25624-anticipate-obstacles-to-removing-turbolinks.yml delete mode 100644 changelogs/unreleased/25811-pipeline-number-and-url-do-not-update-when-new-pipeline-is-triggered.yml delete mode 100644 changelogs/unreleased/25910-convert-manual-action-icons-to-svg-to-propperly-position-them.yml delete mode 100644 changelogs/unreleased/25989-fix-rogue-scrollbars-on-comments.yml delete mode 100644 changelogs/unreleased/26059-segoe-ui-vertical.yml delete mode 100644 changelogs/unreleased/26068_tasklist_issue.yml delete mode 100644 changelogs/unreleased/26117-sort-pipeline-for-commit.yml delete mode 100644 changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml delete mode 100644 changelogs/unreleased/26445-accessible-piplelines-buttons.yml delete mode 100644 changelogs/unreleased/26447-fix-tab-list-order.yml delete mode 100644 changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml delete mode 100644 changelogs/unreleased/26787-add-copy-icon-hover-state.yml delete mode 100644 changelogs/unreleased/26824-diff-unfold-link-is-still-visible-when-there-are-no-lines-to-unfold.yml delete mode 100644 changelogs/unreleased/26852-fix-slug-for-openshift.yml delete mode 100644 changelogs/unreleased/26863-Remove-hover-animation-from-row-elements.yml delete mode 100644 changelogs/unreleased/26881-backup-fails-if-data-changes.yml delete mode 100644 changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml delete mode 100644 changelogs/unreleased/26947-build-status-self-link.yml delete mode 100644 changelogs/unreleased/26982-improve-pipeline-status-icon-linking-in-widgets.yml delete mode 100644 changelogs/unreleased/27013-regression-in-commit-title-bar.yml delete mode 100644 changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml delete mode 100644 changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml delete mode 100644 changelogs/unreleased/27178-update-builds-link-in-project-settings.yml delete mode 100644 changelogs/unreleased/27240-make-progress-bars-consistent.yml delete mode 100644 changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml delete mode 100644 changelogs/unreleased/27291-unify-mr-diff-file-buttons.yml delete mode 100644 changelogs/unreleased/27321-double-separator-line-in-edit-projects-settings.yml delete mode 100644 changelogs/unreleased/27332-mini-pipeline-graph-with-many-stages-has-no-line-spacing-in-firefox-and-safari.yml delete mode 100644 changelogs/unreleased/27352-search-label-filter-header.yml delete mode 100644 changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml delete mode 100644 changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml delete mode 100644 changelogs/unreleased/27484-environment-show-name.yml delete mode 100644 changelogs/unreleased/27488-fix-jwt-version.yml delete mode 100644 changelogs/unreleased/27494-environment-list-column-headers.yml delete mode 100644 changelogs/unreleased/27602-fix-avatar-border-flicker-mention-dropdown.yml delete mode 100644 changelogs/unreleased/27632_fix_mr_widget_url.yml delete mode 100644 changelogs/unreleased/27639-emoji-panel-under-the-side-panel-in-the-merge-request.yml delete mode 100644 changelogs/unreleased/27656-doc-ci-enable-ci.yml delete mode 100644 changelogs/unreleased/27774-text-color-contrast-is-barely-readable-for-pipelines-visualization-graph-page-with-roboto-fonts.yml delete mode 100644 changelogs/unreleased/27822-default-bulk-assign-labels.yml delete mode 100644 changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml delete mode 100644 changelogs/unreleased/27880-pipelines-table-not-showing-commit-branch.yml delete mode 100644 changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml delete mode 100644 changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml delete mode 100644 changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml delete mode 100644 changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml delete mode 100644 changelogs/unreleased/27939-fix-current-build-arrow.yml delete mode 100644 changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml delete mode 100644 changelogs/unreleased/27947-missing-margin-between-loading-icon-and-text-in-merge-request-widget.yml delete mode 100644 changelogs/unreleased/27955-mr-notification-use-pipeline-language.yml delete mode 100644 changelogs/unreleased/27963-tooltips-jobs.yml delete mode 100644 changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml delete mode 100644 changelogs/unreleased/27987-skipped-pipeline-mr-graph.yml delete mode 100644 changelogs/unreleased/27991-success-with-warnings-caret.yml delete mode 100644 changelogs/unreleased/28029-improve-blockquote-formatting-on-emails.yml delete mode 100644 changelogs/unreleased/28032-tooltips-file-name.yml delete mode 100644 changelogs/unreleased/28059-add-pagination-to-admin-abuse-reports.yml delete mode 100644 changelogs/unreleased/395-fix-notification-when-group-set-to-watch.yml delete mode 100644 changelogs/unreleased/8-15-stable.yml delete mode 100644 changelogs/unreleased/8082-permalink-to-file.yml delete mode 100644 changelogs/unreleased/9-0-api-changes.yml delete mode 100644 changelogs/unreleased/Add-a-shash-command-for-target-merge-request-branch.yml delete mode 100644 changelogs/unreleased/add_project_update_hook.yml delete mode 100644 changelogs/unreleased/api-remove-snippets-expires-at.yml delete mode 100644 changelogs/unreleased/babel-all-the-things.yml delete mode 100644 changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml delete mode 100644 changelogs/unreleased/change_queue_weight.yml delete mode 100644 changelogs/unreleased/clipboard-button-commit-sha.yml delete mode 100644 changelogs/unreleased/contribution-calendar-scroll.yml delete mode 100644 changelogs/unreleased/cop-gem-fetcher.yml delete mode 100644 changelogs/unreleased/copy-as-md.yml delete mode 100644 changelogs/unreleased/disable-autologin-on-email-confirmation-links.yml delete mode 100644 changelogs/unreleased/display-project-id.yml delete mode 100644 changelogs/unreleased/document-how-to-vue.yml delete mode 100644 changelogs/unreleased/dynamic-todos-fixture.yml delete mode 100644 changelogs/unreleased/dz-nested-groups-improvements-2.yml delete mode 100644 changelogs/unreleased/empty-selection-reply-shortcut.yml delete mode 100644 changelogs/unreleased/fe-commit-mr-pipelines.yml delete mode 100644 changelogs/unreleased/feature-success-warning-icons-in-stages-builds.yml delete mode 100644 changelogs/unreleased/fix-27479.yml delete mode 100644 changelogs/unreleased/fix-api-mr-permissions.yml delete mode 100644 changelogs/unreleased/fix-ar-connection-leaks.yml delete mode 100644 changelogs/unreleased/fix-ci-build-policy.yml delete mode 100644 changelogs/unreleased/fix-deleting-project-again.yml delete mode 100644 changelogs/unreleased/fix-depr-warn.yml delete mode 100644 changelogs/unreleased/fix-gb-backwards-compatibility-coverage-ci-yml.yml delete mode 100644 changelogs/unreleased/fix-guest-access-posting-to-notes.yml delete mode 100644 changelogs/unreleased/fix-import-encrypt-atts.yml delete mode 100644 changelogs/unreleased/fix-import-group-members.yml delete mode 100644 changelogs/unreleased/fix-job-to-pipeline-renaming.yml delete mode 100644 changelogs/unreleased/fix-references-header-parsing.yml delete mode 100644 changelogs/unreleased/fix-scroll-test.yml delete mode 100644 changelogs/unreleased/fix-users-deleting-public-deployment-keys.yml delete mode 100644 changelogs/unreleased/fix_broken_diff_discussions.yml delete mode 100644 changelogs/unreleased/fix_sidekiq_concurrency_warning_message_in_admin_background_job_page.yml delete mode 100644 changelogs/unreleased/fwn-to-find-by-full-path.yml delete mode 100644 changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml delete mode 100644 changelogs/unreleased/git_to_html_redirection.yml delete mode 100644 changelogs/unreleased/go-go-gadget-webpack.yml delete mode 100644 changelogs/unreleased/group-label-sidebar-link.yml delete mode 100644 changelogs/unreleased/hardcode-title-system-note.yml delete mode 100644 changelogs/unreleased/improve-ci-example-php-doc.yml delete mode 100644 changelogs/unreleased/improve-handleLocationHash-tests.yml delete mode 100644 changelogs/unreleased/issuable-sidebar-bug.yml delete mode 100644 changelogs/unreleased/issue-20428.yml delete mode 100644 changelogs/unreleased/issue-sidebar-empty-assignee.yml delete mode 100644 changelogs/unreleased/issue_19262.yml delete mode 100644 changelogs/unreleased/issue_23317.yml delete mode 100644 changelogs/unreleased/issue_27211.yml delete mode 100644 changelogs/unreleased/jej-pages-picked-from-ee.yml delete mode 100644 changelogs/unreleased/label-promotion.yml delete mode 100644 changelogs/unreleased/lfs-noauth-public-repo.yml delete mode 100644 changelogs/unreleased/markdown-plantuml.yml delete mode 100644 changelogs/unreleased/merge-request-tabs-fixture.yml delete mode 100644 changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml delete mode 100644 changelogs/unreleased/mr-tabs-container-offset.yml delete mode 100644 changelogs/unreleased/newline-eslint-rule.yml delete mode 100644 changelogs/unreleased/no-sidebar-on-action-btn-click.yml delete mode 100644 changelogs/unreleased/no_project_notes.yml delete mode 100644 changelogs/unreleased/pms-lowercase-system-notes.yml delete mode 100644 changelogs/unreleased/redesign-searchbar-admin-project-26794.yml delete mode 100644 changelogs/unreleased/redirect-to-commit-when-only-commit-found.yml delete mode 100644 changelogs/unreleased/relative-url-assets.yml delete mode 100644 changelogs/unreleased/remove-deploy-key-endpoint.yml delete mode 100644 changelogs/unreleased/remove-issue-and-mr-counts-from-labels-page.yml delete mode 100644 changelogs/unreleased/route-map.yml delete mode 100644 changelogs/unreleased/rs-warden-blocked-users.yml delete mode 100644 changelogs/unreleased/sh-add-index-to-ci-trigger-requests.yml delete mode 100644 changelogs/unreleased/sh-add-labels-index.yml delete mode 100644 changelogs/unreleased/slash-commands-typo.yml delete mode 100644 changelogs/unreleased/small-screen-fullscreen-button.yml delete mode 100644 changelogs/unreleased/snippets-search-performance.yml delete mode 100644 changelogs/unreleased/tc-only-mr-button-if-allowed.yml delete mode 100644 changelogs/unreleased/terminal-max-session-time.yml delete mode 100644 changelogs/unreleased/updated-pages-0-3-1.yml delete mode 100644 changelogs/unreleased/upgrade-babel-v6.yml delete mode 100644 changelogs/unreleased/upgrade-omniauth.yml delete mode 100644 changelogs/unreleased/upgrade-webpack-v2-2.yml delete mode 100644 changelogs/unreleased/wip-mr-from-commits.yml delete mode 100644 changelogs/unreleased/zj-format-chat-messages.yml delete mode 100644 changelogs/unreleased/zj-remove-deprecated-ci-service.yml delete mode 100644 changelogs/unreleased/zj-requeue-pending-delete.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b8cf2ad83..7f5b101ad6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,186 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.17.0 (2017-02-22) + +- API: Fix file downloading. !0 (8267) +- Changed composer installer script in the CI PHP example doc. !4342 (Jeffrey Cafferata) +- Display fullscreen button on small screens. !5302 (winniehell) +- Add system hook for when a project is updated (other than rename/transfer). !5711 (Tommy Beadle) +- Fix notifications when set at group level. !6813 (Alexandre Maia) +- Project labels can now be promoted to group labels. !7242 (Olaf Tomalka) +- use webpack to bundle frontend assets and use karma for frontend testing. !7288 +- Adds back ability to stop all environments. !7379 +- Added labels empty state. !7443 +- Add ability to define a coverage regex in the .gitlab-ci.yml. !7447 (Leandro Camargo) +- Disable automatic login after clicking email confirmation links. !7472 +- Search feature: redirects to commit page if query is commit sha and only commit found. !8028 (YarNayar) +- Create a TODO for user who set auto-merge when a build fails, merge conflict occurs. !8056 (twonegatives) +- Don't group issues by project on group-level and dashboard issue indexes. !8111 (Bernardo Castro) +- Mark MR as WIP when pushing WIP commits. !8124 (Jurre Stender @jurre) +- Flag multiple empty lines in eslint, fix offenses. !8137 +- Add sorting pipeline for a commit. !8319 (Takuya Noguchi) +- Adds service trigger events to api. !8324 +- Update pipeline and commit links when CI status is updated. !8351 +- Hide version check image if there is no internet connection. !8355 (Ken Ding) +- Prevent removal of input fields if it is the parent dropdown element. !8397 +- Introduce maximum session time for terminal websocket connection. !8413 +- Allow creating protected branches when user can merge to such branch. !8458 +- Refactor MergeRequests::BuildService. !8462 (Rydkin Maxim) +- Added GitLab Pages to CE. !8463 +- Support notes when a project is not specified (personal snippet notes). !8468 +- Use warning icon in mini-graph if stage passed conditionally. !8503 +- Don’t count tasks that are not defined as list items correctly. !8526 +- Reformat messages ChatOps. !8528 +- Copy commit SHA to clipboard. !8547 +- Improve button accessibility on pipelines page. !8561 +- Display project ID in project settings. !8572 (winniehell) +- PlantUML support for Markdown. !8588 (Horacio Sanson) +- Fix reply by email without sub-addressing for some clients from Microsoft and Apple. !8620 +- Fix nested tasks in ordered list. !8626 +- Fix Sort by Recent Sign-in in Admin Area. !8637 (Poornima M) +- Avoid repeated dashes in $CI_ENVIRONMENT_SLUG. !8638 +- Only show Merge Request button when user can create a MR. !8639 +- Prevent copying of line numbers in parallel diff view. !8706 +- Improve build policy and access abilities. !8711 +- API: Remove /projects/:id/keys/.. endpoints. !8716 (Robert Schilling) +- API: Remove deprecated 'expires_at' from project snippets. !8723 (Robert Schilling) +- Add `copy` backup strategy to combat file changed errors. !8728 +- adds avatar for discussion note. !8734 +- Add link verification to badge partial in order to render a badge without a link. !8740 +- Reduce hits to LDAP on Git HTTP auth by reordering auth mechanisms. !8752 +- prevent diff unfolding link from appearing when there are no more lines to show. !8761 +- Redesign searchbar in admin project list. !8776 +- Rename Builds to Pipelines, CI/CD Pipelines, or Jobs everywhere. !8787 +- dismiss sidebar on repo buttons click. !8798 (Adam Pahlevi) +- fixed small mini pipeline graph line glitch. !8804 +- Make all system notes lowercase. !8807 +- Support unauthenticated LFS object downloads for public projects. !8824 (Ben Boeckel) +- Add read-only full_path and full_name attributes to Group API. !8827 +- allow relative url change without recompiling frontend assets. !8831 +- Use vue.js Pipelines table in commit and merge request view. !8844 +- Use reCaptcha when an issue is identified as a spam. !8846 +- resolve deprecation warnings. !8855 (Adam Pahlevi) +- Cop for gem fetched from a git source. !8856 (Adam Pahlevi) +- Remove flash warning from login page. !8864 (Gerald J. Padilla) +- Adds documentation for how to use Vue.js. !8866 +- Add 'View on [env]' link to blobs and individual files in diffs. !8867 +- Replace word user with member. !8872 +- Change the reply shortcut to focus the field even without a selection. !8873 (Brian Hall) +- Unify MR diff file button style. !8874 +- Unify projects search by removing /projects/:search endpoint. !8877 +- Fix disable storing of sensitive information when importing a new repo. !8885 (Bernard Pietraga) +- Fix pipeline graph vertical spacing in Firefox and Safari. !8886 +- Fix filtered search user autocomplete for gitlab instances that are hosted on a subdirectory. !8891 +- Fix Ctrl+Click support for Todos and Merge Request page tabs. !8898 +- Fix wrong call to ProjectCacheWorker.perform. !8910 +- Don't perform Devise trackable updates on blocked User records. !8915 +- Add ability to export project inherited group members to Import/Export. !8923 +- replace `find_with_namespace` with `find_by_full_path`. !8949 (Adam Pahlevi) +- Fixes flickering of avatar border in mention dropdown. !8950 +- Remove unnecessary queries for .atom and .json in Dashboard::ProjectsController#index. !8956 +- Fix deleting projects with pipelines and builds. !8960 +- Fix broken anchor links when special characters are used. !8961 (Andrey Krivko) +- Ensure autogenerated title does not cause failing spec. !8963 (brian m. carlson) +- Update doc for enabling or disabling GitLab CI. !8965 (Takuya Noguchi) +- Remove deprecated MR and Issue endpoints and preserve V3 namespace. !8967 +- Fixed "substract" typo on /help/user/project/slash_commands. !8976 (Jason Aquino) +- Preserve backward compatibility CI/CD and disallow setting `coverage` regexp in global context. !8981 +- use babel to transpile all non-vendor javascript assets regardless of file extension. !8988 +- Fix MR widget url. !8989 +- Fixes hover cursor on pipeline pagenation. !9003 +- Layer award emoji dropdown over the right sidebar. !9004 +- Do not display deploy keys in user's own ssh keys list. !9024 +- upgrade babel 5.8.x to babel 6.22.x. !9072 +- upgrade to webpack v2.2. !9078 +- Trigger autocomplete after selecting a slash command. !9117 +- Add space between text and loading icon in Megre Request Widget. !9119 +- Fix job to pipeline renaming. !9147 +- Replace static fixture for merge_request_tabs_spec.js. !9172 (winniehell) +- Replace static fixture for right_sidebar_spec.js. !9211 (winniehell) +- Show merge errors in merge request widget. !9229 +- Increase process_commit queue weight from 2 to 3. !9326 (blackst0ne) +- Don't require lib/gitlab/request_profiler/middleware.rb in config/initializers/request_profiler.rb. +- Force new password after password reset via API. (George Andrinopoulos) +- Allows to search within project by commit hash. (YarNayar) +- Show organisation membership and delete comment on smaller viewports, plus change comment author name to username. +- Remove turbolinks. +- Convert pipeline action icons to svg to have them propperly positioned. +- Remove rogue scrollbars for issue comments with inline elements. +- Align Segoe UI label text. +- Color + and - signs in diffs to increase code legibility. +- Fix tab index order on branch commits list page. (Ryan Harris) +- Add hover style to copy icon on commit page header. (Ryan Harris) +- Remove hover animation from row elements. +- Improve pipeline status icon linking in widgets. +- Fix commit title bar and repository view copy clipboard button order on last commit in repository view. +- Fix mini-pipeline stage tooltip text wrapping. +- Updated builds info link on the project settings page. (Ryan Harris) +- 27240 Make progress bars consistent. +- Only render hr when user can't archive project. +- 27352-search-label-filter-header. +- Include :author, :project, and :target in Event.with_associations. +- Don't instantiate AR objects in Event.in_projects. +- Don't capitalize environment name in show page. +- Update and pin the `jwt` gem to ~> 1.5.6. +- Edited the column header for the environments list from created to updated and added created to environments detail page colum header titles. +- Give ci status text on pipeline graph a better font-weight. +- Add default labels to bulk assign dropdowns. +- Only return target project's comments for a commit. +- Fixes Pipelines table is not showing branch name for commit. +- Fix regression where cmd-click stopped working for todos and merge request tabs. +- Fix stray pipelines API request when showing MR. +- Fix Merge request pipelines displays JSON. +- Fix current build arrow indicator. +- Fix contribution activity alignment. +- Show Pipeline(not Job) in MR desktop notification. +- Fix tooltips in mini pipeline graph. +- Display loading indicator when filtering ref switcher dropdown. +- Show pipeline graph in MR widget if there are any stages. +- Fix icon colors in merge request widget mini graph. +- Improve blockquote formatting in notification emails. +- Adds container to tooltip in order to make it work with overflow:hidden in parent element. +- Restore pagination to admin abuse reports. +- Ensure export files are removed after a namespace is deleted. +- Add `y` keyboard shortcut to move to file permalink. +- Adds /target_branch slash command functionality for merge requests. (YarNayar) +- Patch Asciidocs rendering to block XSS. +- contribution calendar scrolls from right to left. +- Copying a rendered issue/comment will paste into GFM textareas as actual GFM. +- Don't delete assigned MRs/issues when user is deleted. +- Remove new branch button for confidential issues. +- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling) +- Don't connect in Gitlab::Database.adapter_name. +- Prevent users from creating notes on resources they can't access. +- Ignore encrypted attributes in Import/Export. +- Change rspec test to guarantee window is resized before visiting page. +- Prevent users from deleting system deploy keys via the project deploy key API. +- Fix XSS vulnerability in SVG attachments. +- Make MR-review-discussions more reliable. +- fix incorrect sidekiq concurrency count in admin background page. (wendy0402) +- Make notification_service spec DRYer by making test reusable. (YarNayar) +- Redirect http://someproject.git to http://someproject. (blackst0ne) +- Fixed group label links in issue/merge request sidebar. +- Improve gl.utils.handleLocationHash tests. +- Fixed Issuable sidebar not closing on smaller/mobile sized screens. +- Resets assignee dropdown when sidebar is open. +- Disallow system notes for closed issuables. +- Fix timezone on issue boards due date. +- Remove unused js response from refs controller. +- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. +- Fixed merge requests tab extra margin when fixed to window. +- Patch XSS vulnerability in RDOC support. +- Refresh authorizations when transferring projects. +- Remove issue and MR counts from labels index. +- Don't use backup Active Record connections for Sidekiq. +- Add index to ci_trigger_requests for commit_id. +- Add indices to improve loading of labels page. +- Reduced query count for snippet search. +- Update GitLab Pages to v0.3.1. +- Upgrade omniauth gem to 1.3.2. +- Remove deprecated GitlabCiService. +- Requeue pending deletion projects. + ## 8.16.6 (2017-02-17) - API: Fix file downloading. !0 (8267) diff --git a/changelogs/unreleased/17662-rename-builds.yml b/changelogs/unreleased/17662-rename-builds.yml deleted file mode 100644 index 12f2998d1c8..00000000000 --- a/changelogs/unreleased/17662-rename-builds.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Rename Builds to Pipelines, CI/CD Pipelines, or Jobs everywhere -merge_request: 8787 -author: diff --git a/changelogs/unreleased/20452-remove-require-from-request_profiler-initializer.yml b/changelogs/unreleased/20452-remove-require-from-request_profiler-initializer.yml deleted file mode 100644 index 965d0648adf..00000000000 --- a/changelogs/unreleased/20452-remove-require-from-request_profiler-initializer.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't require lib/gitlab/request_profiler/middleware.rb in config/initializers/request_profiler.rb -merge_request: -author: diff --git a/changelogs/unreleased/20852-getting-started-project-better-blank-state-for-labels-view.yml b/changelogs/unreleased/20852-getting-started-project-better-blank-state-for-labels-view.yml deleted file mode 100644 index eda872049fd..00000000000 --- a/changelogs/unreleased/20852-getting-started-project-better-blank-state-for-labels-view.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added labels empty state -merge_request: 7443 -author: diff --git a/changelogs/unreleased/21518_recaptcha_spam_issues.yml b/changelogs/unreleased/21518_recaptcha_spam_issues.yml deleted file mode 100644 index bd6c9d7521e..00000000000 --- a/changelogs/unreleased/21518_recaptcha_spam_issues.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use reCaptcha when an issue is identified as a spam -merge_request: 8846 -author: diff --git a/changelogs/unreleased/22007-unify-projects-search.yml b/changelogs/unreleased/22007-unify-projects-search.yml deleted file mode 100644 index f43c1925ad0..00000000000 --- a/changelogs/unreleased/22007-unify-projects-search.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Unify projects search by removing /projects/:search endpoint -merge_request: 8877 -author: diff --git a/changelogs/unreleased/22638-creating-a-branch-matching-a-wildcard-fails.yml b/changelogs/unreleased/22638-creating-a-branch-matching-a-wildcard-fails.yml deleted file mode 100644 index 2c6883bcf7b..00000000000 --- a/changelogs/unreleased/22638-creating-a-branch-matching-a-wildcard-fails.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow creating protected branches when user can merge to such branch -merge_request: 8458 -author: diff --git a/changelogs/unreleased/22974-trigger-service-events-through-api.yml b/changelogs/unreleased/22974-trigger-service-events-through-api.yml deleted file mode 100644 index 57106e8c676..00000000000 --- a/changelogs/unreleased/22974-trigger-service-events-through-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds service trigger events to api -merge_request: 8324 -author: diff --git a/changelogs/unreleased/23524-notify-automerge-user-of-failed-build.yml b/changelogs/unreleased/23524-notify-automerge-user-of-failed-build.yml deleted file mode 100644 index 268be6b9b83..00000000000 --- a/changelogs/unreleased/23524-notify-automerge-user-of-failed-build.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Create a TODO for user who set auto-merge when a build fails, merge conflict occurs -merge_request: 8056 -author: twonegatives diff --git a/changelogs/unreleased/23634-remove-project-grouping.yml b/changelogs/unreleased/23634-remove-project-grouping.yml deleted file mode 100644 index dde8b2d1815..00000000000 --- a/changelogs/unreleased/23634-remove-project-grouping.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't group issues by project on group-level and dashboard issue indexes. -merge_request: 8111 -author: Bernardo Castro diff --git a/changelogs/unreleased/23767-disable-storing-of-sensitive-information.yml b/changelogs/unreleased/23767-disable-storing-of-sensitive-information.yml deleted file mode 100644 index 587ef4f9a73..00000000000 --- a/changelogs/unreleased/23767-disable-storing-of-sensitive-information.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix disable storing of sensitive information when importing a new repo -merge_request: 8885 -author: Bernard Pietraga diff --git a/changelogs/unreleased/24147-delete-env-button.yml b/changelogs/unreleased/24147-delete-env-button.yml deleted file mode 100644 index 14e80cacbfb..00000000000 --- a/changelogs/unreleased/24147-delete-env-button.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds back ability to stop all environments -merge_request: 7379 -author: diff --git a/changelogs/unreleased/24606-force-password-reset-on-next-login.yml b/changelogs/unreleased/24606-force-password-reset-on-next-login.yml deleted file mode 100644 index fd671d04a9f..00000000000 --- a/changelogs/unreleased/24606-force-password-reset-on-next-login.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Force new password after password reset via API -merge_request: -author: George Andrinopoulos diff --git a/changelogs/unreleased/24716-fix-ctrl-click-links.yml b/changelogs/unreleased/24716-fix-ctrl-click-links.yml deleted file mode 100644 index 13de5db5e41..00000000000 --- a/changelogs/unreleased/24716-fix-ctrl-click-links.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Ctrl+Click support for Todos and Merge Request page tabs -merge_request: 8898 -author: diff --git a/changelogs/unreleased/24795_refactor_merge_request_build_service.yml b/changelogs/unreleased/24795_refactor_merge_request_build_service.yml deleted file mode 100644 index b735fb57649..00000000000 --- a/changelogs/unreleased/24795_refactor_merge_request_build_service.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Refactor MergeRequests::BuildService -merge_request: 8462 -author: Rydkin Maxim diff --git a/changelogs/unreleased/24833-Allow-to-search-by-commit-hash-within-project.yml b/changelogs/unreleased/24833-Allow-to-search-by-commit-hash-within-project.yml deleted file mode 100644 index be66c370f36..00000000000 --- a/changelogs/unreleased/24833-Allow-to-search-by-commit-hash-within-project.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Allows to search within project by commit hash' -merge_request: -author: YarNayar diff --git a/changelogs/unreleased/24923_nested_tasks.yml b/changelogs/unreleased/24923_nested_tasks.yml deleted file mode 100644 index de35cad3dd6..00000000000 --- a/changelogs/unreleased/24923_nested_tasks.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix nested tasks in ordered list -merge_request: 8626 -author: diff --git a/changelogs/unreleased/25134-mobile-issue-view-doesn-t-show-organization-membership.yml b/changelogs/unreleased/25134-mobile-issue-view-doesn-t-show-organization-membership.yml deleted file mode 100644 index d35ad0be0db..00000000000 --- a/changelogs/unreleased/25134-mobile-issue-view-doesn-t-show-organization-membership.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Show organisation membership and delete comment on smaller viewports, plus change comment author name to username -merge_request: -author: diff --git a/changelogs/unreleased/25312-search-input-cmd-click-issue.yml b/changelogs/unreleased/25312-search-input-cmd-click-issue.yml deleted file mode 100644 index 56e03a48692..00000000000 --- a/changelogs/unreleased/25312-search-input-cmd-click-issue.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent removal of input fields if it is the parent dropdown element -merge_request: 8397 -author: diff --git a/changelogs/unreleased/25360-remove-flash-warning-from-login-page.yml b/changelogs/unreleased/25360-remove-flash-warning-from-login-page.yml deleted file mode 100644 index 50a5c879446..00000000000 --- a/changelogs/unreleased/25360-remove-flash-warning-from-login-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove flash warning from login page -merge_request: 8864 -author: Gerald J. Padilla diff --git a/changelogs/unreleased/25460-replace-word-users-with-members.yml b/changelogs/unreleased/25460-replace-word-users-with-members.yml deleted file mode 100644 index dac90eaa34d..00000000000 --- a/changelogs/unreleased/25460-replace-word-users-with-members.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace word user with member -merge_request: 8872 -author: diff --git a/changelogs/unreleased/25624-anticipate-obstacles-to-removing-turbolinks.yml b/changelogs/unreleased/25624-anticipate-obstacles-to-removing-turbolinks.yml deleted file mode 100644 index d7f950d7be9..00000000000 --- a/changelogs/unreleased/25624-anticipate-obstacles-to-removing-turbolinks.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove turbolinks. -merge_request: !8570 -author: diff --git a/changelogs/unreleased/25811-pipeline-number-and-url-do-not-update-when-new-pipeline-is-triggered.yml b/changelogs/unreleased/25811-pipeline-number-and-url-do-not-update-when-new-pipeline-is-triggered.yml deleted file mode 100644 index f74e9fa8b6d..00000000000 --- a/changelogs/unreleased/25811-pipeline-number-and-url-do-not-update-when-new-pipeline-is-triggered.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update pipeline and commit links when CI status is updated -merge_request: 8351 -author: diff --git a/changelogs/unreleased/25910-convert-manual-action-icons-to-svg-to-propperly-position-them.yml b/changelogs/unreleased/25910-convert-manual-action-icons-to-svg-to-propperly-position-them.yml deleted file mode 100644 index 9506692dd40..00000000000 --- a/changelogs/unreleased/25910-convert-manual-action-icons-to-svg-to-propperly-position-them.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Convert pipeline action icons to svg to have them propperly positioned -merge_request: -author: diff --git a/changelogs/unreleased/25989-fix-rogue-scrollbars-on-comments.yml b/changelogs/unreleased/25989-fix-rogue-scrollbars-on-comments.yml deleted file mode 100644 index e67a9c0da15..00000000000 --- a/changelogs/unreleased/25989-fix-rogue-scrollbars-on-comments.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove rogue scrollbars for issue comments with inline elements -merge_request: -author: diff --git a/changelogs/unreleased/26059-segoe-ui-vertical.yml b/changelogs/unreleased/26059-segoe-ui-vertical.yml deleted file mode 100644 index fc3f1af5b61..00000000000 --- a/changelogs/unreleased/26059-segoe-ui-vertical.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Align Segoe UI label text -merge_request: -author: diff --git a/changelogs/unreleased/26068_tasklist_issue.yml b/changelogs/unreleased/26068_tasklist_issue.yml deleted file mode 100644 index c938351b8a7..00000000000 --- a/changelogs/unreleased/26068_tasklist_issue.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don’t count tasks that are not defined as list items correctly -merge_request: 8526 -author: diff --git a/changelogs/unreleased/26117-sort-pipeline-for-commit.yml b/changelogs/unreleased/26117-sort-pipeline-for-commit.yml deleted file mode 100644 index b2f5294d380..00000000000 --- a/changelogs/unreleased/26117-sort-pipeline-for-commit.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add sorting pipeline for a commit -merge_request: 8319 -author: Takuya Noguchi diff --git a/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml b/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml deleted file mode 100644 index 565672917b2..00000000000 --- a/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Color + and - signs in diffs to increase code legibility -merge_request: -author: diff --git a/changelogs/unreleased/26445-accessible-piplelines-buttons.yml b/changelogs/unreleased/26445-accessible-piplelines-buttons.yml deleted file mode 100644 index fb5274e5253..00000000000 --- a/changelogs/unreleased/26445-accessible-piplelines-buttons.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve button accessibility on pipelines page -merge_request: 8561 -author: diff --git a/changelogs/unreleased/26447-fix-tab-list-order.yml b/changelogs/unreleased/26447-fix-tab-list-order.yml deleted file mode 100644 index 351c53bd076..00000000000 --- a/changelogs/unreleased/26447-fix-tab-list-order.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix tab index order on branch commits list page -merge_request: -author: Ryan Harris diff --git a/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml b/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml deleted file mode 100644 index 87ae8233c4a..00000000000 --- a/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Sort by Recent Sign-in in Admin Area -merge_request: 8637 -author: Poornima M diff --git a/changelogs/unreleased/26787-add-copy-icon-hover-state.yml b/changelogs/unreleased/26787-add-copy-icon-hover-state.yml deleted file mode 100644 index 31f1812c6f8..00000000000 --- a/changelogs/unreleased/26787-add-copy-icon-hover-state.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add hover style to copy icon on commit page header -merge_request: -author: Ryan Harris diff --git a/changelogs/unreleased/26824-diff-unfold-link-is-still-visible-when-there-are-no-lines-to-unfold.yml b/changelogs/unreleased/26824-diff-unfold-link-is-still-visible-when-there-are-no-lines-to-unfold.yml deleted file mode 100644 index 182a9ae126b..00000000000 --- a/changelogs/unreleased/26824-diff-unfold-link-is-still-visible-when-there-are-no-lines-to-unfold.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: prevent diff unfolding link from appearing when there are no more lines to - show -merge_request: 8761 -author: diff --git a/changelogs/unreleased/26852-fix-slug-for-openshift.yml b/changelogs/unreleased/26852-fix-slug-for-openshift.yml deleted file mode 100644 index fb65b068b23..00000000000 --- a/changelogs/unreleased/26852-fix-slug-for-openshift.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Avoid repeated dashes in $CI_ENVIRONMENT_SLUG -merge_request: 8638 -author: diff --git a/changelogs/unreleased/26863-Remove-hover-animation-from-row-elements.yml b/changelogs/unreleased/26863-Remove-hover-animation-from-row-elements.yml deleted file mode 100644 index 8dfabf87c2a..00000000000 --- a/changelogs/unreleased/26863-Remove-hover-animation-from-row-elements.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove hover animation from row elements -merge_request: -author: diff --git a/changelogs/unreleased/26881-backup-fails-if-data-changes.yml b/changelogs/unreleased/26881-backup-fails-if-data-changes.yml deleted file mode 100644 index 00bf105560b..00000000000 --- a/changelogs/unreleased/26881-backup-fails-if-data-changes.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add `copy` backup strategy to combat file changed errors -merge_request: 8728 -author: diff --git a/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml b/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml deleted file mode 100644 index ea567437ac2..00000000000 --- a/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes hover cursor on pipeline pagenation -merge_request: 9003 -author: diff --git a/changelogs/unreleased/26947-build-status-self-link.yml b/changelogs/unreleased/26947-build-status-self-link.yml deleted file mode 100644 index 15c5821874e..00000000000 --- a/changelogs/unreleased/26947-build-status-self-link.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add link verification to badge partial in order to render a badge without a link -merge_request: 8740 -author: diff --git a/changelogs/unreleased/26982-improve-pipeline-status-icon-linking-in-widgets.yml b/changelogs/unreleased/26982-improve-pipeline-status-icon-linking-in-widgets.yml deleted file mode 100644 index c5c57af5aaf..00000000000 --- a/changelogs/unreleased/26982-improve-pipeline-status-icon-linking-in-widgets.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve pipeline status icon linking in widgets -merge_request: -author: diff --git a/changelogs/unreleased/27013-regression-in-commit-title-bar.yml b/changelogs/unreleased/27013-regression-in-commit-title-bar.yml deleted file mode 100644 index 7cb5e4b273d..00000000000 --- a/changelogs/unreleased/27013-regression-in-commit-title-bar.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix commit title bar and repository view copy clipboard button order on last commit in repository view -merge_request: -author: diff --git a/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml b/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml deleted file mode 100644 index f0301c849b6..00000000000 --- a/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix mini-pipeline stage tooltip text wrapping -merge_request: -author: diff --git a/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml b/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml deleted file mode 100644 index b5584749098..00000000000 --- a/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent copying of line numbers in parallel diff view -merge_request: 8706 -author: diff --git a/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml b/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml deleted file mode 100644 index 52406bba464..00000000000 --- a/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Updated builds info link on the project settings page -merge_request: -author: Ryan Harris diff --git a/changelogs/unreleased/27240-make-progress-bars-consistent.yml b/changelogs/unreleased/27240-make-progress-bars-consistent.yml deleted file mode 100644 index 3f902fb324e..00000000000 --- a/changelogs/unreleased/27240-make-progress-bars-consistent.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 27240 Make progress bars consistent -merge_request: -author: diff --git a/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml b/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml deleted file mode 100644 index 9456251025b..00000000000 --- a/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: fixed small mini pipeline graph line glitch -merge_request: 8804 -author: diff --git a/changelogs/unreleased/27291-unify-mr-diff-file-buttons.yml b/changelogs/unreleased/27291-unify-mr-diff-file-buttons.yml deleted file mode 100644 index 293aab67d39..00000000000 --- a/changelogs/unreleased/27291-unify-mr-diff-file-buttons.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Unify MR diff file button style -merge_request: 8874 -author: diff --git a/changelogs/unreleased/27321-double-separator-line-in-edit-projects-settings.yml b/changelogs/unreleased/27321-double-separator-line-in-edit-projects-settings.yml deleted file mode 100644 index 502927cd160..00000000000 --- a/changelogs/unreleased/27321-double-separator-line-in-edit-projects-settings.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Only render hr when user can't archive project. -merge_request: !8917 -author: diff --git a/changelogs/unreleased/27332-mini-pipeline-graph-with-many-stages-has-no-line-spacing-in-firefox-and-safari.yml b/changelogs/unreleased/27332-mini-pipeline-graph-with-many-stages-has-no-line-spacing-in-firefox-and-safari.yml deleted file mode 100644 index 79316abbaf7..00000000000 --- a/changelogs/unreleased/27332-mini-pipeline-graph-with-many-stages-has-no-line-spacing-in-firefox-and-safari.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix pipeline graph vertical spacing in Firefox and Safari -merge_request: 8886 -author: diff --git a/changelogs/unreleased/27352-search-label-filter-header.yml b/changelogs/unreleased/27352-search-label-filter-header.yml deleted file mode 100644 index 191b530aee8..00000000000 --- a/changelogs/unreleased/27352-search-label-filter-header.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 27352-search-label-filter-header -merge_request: -author: diff --git a/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml b/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml deleted file mode 100644 index f3ce1709518..00000000000 --- a/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Include :author, :project, and :target in Event.with_associations -merge_request: -author: diff --git a/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml b/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml deleted file mode 100644 index 3f6d922f2a0..00000000000 --- a/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't instantiate AR objects in Event.in_projects -merge_request: -author: diff --git a/changelogs/unreleased/27484-environment-show-name.yml b/changelogs/unreleased/27484-environment-show-name.yml deleted file mode 100644 index dc400d65006..00000000000 --- a/changelogs/unreleased/27484-environment-show-name.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't capitalize environment name in show page -merge_request: -author: diff --git a/changelogs/unreleased/27488-fix-jwt-version.yml b/changelogs/unreleased/27488-fix-jwt-version.yml deleted file mode 100644 index 5135ff0fd60..00000000000 --- a/changelogs/unreleased/27488-fix-jwt-version.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update and pin the `jwt` gem to ~> 1.5.6 -merge_request: -author: diff --git a/changelogs/unreleased/27494-environment-list-column-headers.yml b/changelogs/unreleased/27494-environment-list-column-headers.yml deleted file mode 100644 index 798c01f3238..00000000000 --- a/changelogs/unreleased/27494-environment-list-column-headers.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Edited the column header for the environments list from created to updated and added created to environments detail page colum header titles -merge_request: -author: diff --git a/changelogs/unreleased/27602-fix-avatar-border-flicker-mention-dropdown.yml b/changelogs/unreleased/27602-fix-avatar-border-flicker-mention-dropdown.yml deleted file mode 100644 index a5bb37ec8a9..00000000000 --- a/changelogs/unreleased/27602-fix-avatar-border-flicker-mention-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes flickering of avatar border in mention dropdown -merge_request: 8950 -author: diff --git a/changelogs/unreleased/27632_fix_mr_widget_url.yml b/changelogs/unreleased/27632_fix_mr_widget_url.yml deleted file mode 100644 index 958621a43a1..00000000000 --- a/changelogs/unreleased/27632_fix_mr_widget_url.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix MR widget url -merge_request: 8989 -author: diff --git a/changelogs/unreleased/27639-emoji-panel-under-the-side-panel-in-the-merge-request.yml b/changelogs/unreleased/27639-emoji-panel-under-the-side-panel-in-the-merge-request.yml deleted file mode 100644 index 0531ef2c038..00000000000 --- a/changelogs/unreleased/27639-emoji-panel-under-the-side-panel-in-the-merge-request.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Layer award emoji dropdown over the right sidebar -merge_request: 9004 -author: diff --git a/changelogs/unreleased/27656-doc-ci-enable-ci.yml b/changelogs/unreleased/27656-doc-ci-enable-ci.yml deleted file mode 100644 index e6315d683d4..00000000000 --- a/changelogs/unreleased/27656-doc-ci-enable-ci.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update doc for enabling or disabling GitLab CI -merge_request: 8965 -author: Takuya Noguchi diff --git a/changelogs/unreleased/27774-text-color-contrast-is-barely-readable-for-pipelines-visualization-graph-page-with-roboto-fonts.yml b/changelogs/unreleased/27774-text-color-contrast-is-barely-readable-for-pipelines-visualization-graph-page-with-roboto-fonts.yml deleted file mode 100644 index aa89d9f9850..00000000000 --- a/changelogs/unreleased/27774-text-color-contrast-is-barely-readable-for-pipelines-visualization-graph-page-with-roboto-fonts.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Give ci status text on pipeline graph a better font-weight -merge_request: -author: diff --git a/changelogs/unreleased/27822-default-bulk-assign-labels.yml b/changelogs/unreleased/27822-default-bulk-assign-labels.yml deleted file mode 100644 index ee2431869f0..00000000000 --- a/changelogs/unreleased/27822-default-bulk-assign-labels.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add default labels to bulk assign dropdowns -merge_request: -author: diff --git a/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml b/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml deleted file mode 100644 index 89e2bdc69bc..00000000000 --- a/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Only return target project's comments for a commit -merge_request: -author: diff --git a/changelogs/unreleased/27880-pipelines-table-not-showing-commit-branch.yml b/changelogs/unreleased/27880-pipelines-table-not-showing-commit-branch.yml deleted file mode 100644 index 4251754618b..00000000000 --- a/changelogs/unreleased/27880-pipelines-table-not-showing-commit-branch.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes Pipelines table is not showing branch name for commit -merge_request: -author: diff --git a/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml b/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml deleted file mode 100644 index 52b7e96682d..00000000000 --- a/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Trigger autocomplete after selecting a slash command -merge_request: 9117 -author: diff --git a/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml b/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml deleted file mode 100644 index 79a54429ee8..00000000000 --- a/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix regression where cmd-click stopped working for todos and merge request - tabs -merge_request: -author: diff --git a/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml b/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml deleted file mode 100644 index f7bdb62b7f3..00000000000 --- a/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix stray pipelines API request when showing MR -merge_request: -author: diff --git a/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml b/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml deleted file mode 100644 index b7505e28401..00000000000 --- a/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Merge request pipelines displays JSON -merge_request: -author: diff --git a/changelogs/unreleased/27939-fix-current-build-arrow.yml b/changelogs/unreleased/27939-fix-current-build-arrow.yml deleted file mode 100644 index 280ab090f2c..00000000000 --- a/changelogs/unreleased/27939-fix-current-build-arrow.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix current build arrow indicator -merge_request: -author: diff --git a/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml b/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml deleted file mode 100644 index fcbd48b0357..00000000000 --- a/changelogs/unreleased/27943-contribution-list-on-profile-page-is-aligned-right.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix contribution activity alignment -merge_request: -author: diff --git a/changelogs/unreleased/27947-missing-margin-between-loading-icon-and-text-in-merge-request-widget.yml b/changelogs/unreleased/27947-missing-margin-between-loading-icon-and-text-in-merge-request-widget.yml deleted file mode 100644 index 1dfabd3813b..00000000000 --- a/changelogs/unreleased/27947-missing-margin-between-loading-icon-and-text-in-merge-request-widget.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add space between text and loading icon in Megre Request Widget -merge_request: 9119 -author: diff --git a/changelogs/unreleased/27955-mr-notification-use-pipeline-language.yml b/changelogs/unreleased/27955-mr-notification-use-pipeline-language.yml deleted file mode 100644 index d9f78db4bec..00000000000 --- a/changelogs/unreleased/27955-mr-notification-use-pipeline-language.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Show Pipeline(not Job) in MR desktop notification -merge_request: -author: diff --git a/changelogs/unreleased/27963-tooltips-jobs.yml b/changelogs/unreleased/27963-tooltips-jobs.yml deleted file mode 100644 index ba418d86433..00000000000 --- a/changelogs/unreleased/27963-tooltips-jobs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix tooltips in mini pipeline graph -merge_request: -author: diff --git a/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml b/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml deleted file mode 100644 index 6fa13395a7d..00000000000 --- a/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display loading indicator when filtering ref switcher dropdown -merge_request: -author: diff --git a/changelogs/unreleased/27987-skipped-pipeline-mr-graph.yml b/changelogs/unreleased/27987-skipped-pipeline-mr-graph.yml deleted file mode 100644 index e4287d6276c..00000000000 --- a/changelogs/unreleased/27987-skipped-pipeline-mr-graph.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Show pipeline graph in MR widget if there are any stages -merge_request: -author: diff --git a/changelogs/unreleased/27991-success-with-warnings-caret.yml b/changelogs/unreleased/27991-success-with-warnings-caret.yml deleted file mode 100644 index 703d34a5ede..00000000000 --- a/changelogs/unreleased/27991-success-with-warnings-caret.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix icon colors in merge request widget mini graph -merge_request: -author: diff --git a/changelogs/unreleased/28029-improve-blockquote-formatting-on-emails.yml b/changelogs/unreleased/28029-improve-blockquote-formatting-on-emails.yml deleted file mode 100644 index be2a0afbc52..00000000000 --- a/changelogs/unreleased/28029-improve-blockquote-formatting-on-emails.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve blockquote formatting in notification emails -merge_request: -author: diff --git a/changelogs/unreleased/28032-tooltips-file-name.yml b/changelogs/unreleased/28032-tooltips-file-name.yml deleted file mode 100644 index 9fe11e7c2b6..00000000000 --- a/changelogs/unreleased/28032-tooltips-file-name.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds container to tooltip in order to make it work with overflow:hidden in - parent element -merge_request: -author: diff --git a/changelogs/unreleased/28059-add-pagination-to-admin-abuse-reports.yml b/changelogs/unreleased/28059-add-pagination-to-admin-abuse-reports.yml deleted file mode 100644 index 1b2e678bbed..00000000000 --- a/changelogs/unreleased/28059-add-pagination-to-admin-abuse-reports.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Restore pagination to admin abuse reports -merge_request: -author: diff --git a/changelogs/unreleased/395-fix-notification-when-group-set-to-watch.yml b/changelogs/unreleased/395-fix-notification-when-group-set-to-watch.yml deleted file mode 100644 index 11d1f55172b..00000000000 --- a/changelogs/unreleased/395-fix-notification-when-group-set-to-watch.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix notifications when set at group level -merge_request: 6813 -author: Alexandre Maia diff --git a/changelogs/unreleased/8-15-stable.yml b/changelogs/unreleased/8-15-stable.yml deleted file mode 100644 index 75502e139e7..00000000000 --- a/changelogs/unreleased/8-15-stable.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ensure export files are removed after a namespace is deleted -merge_request: -author: diff --git a/changelogs/unreleased/8082-permalink-to-file.yml b/changelogs/unreleased/8082-permalink-to-file.yml deleted file mode 100644 index 136d2108c63..00000000000 --- a/changelogs/unreleased/8082-permalink-to-file.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add `y` keyboard shortcut to move to file permalink -merge_request: -author: diff --git a/changelogs/unreleased/9-0-api-changes.yml b/changelogs/unreleased/9-0-api-changes.yml deleted file mode 100644 index 2f0f1887257..00000000000 --- a/changelogs/unreleased/9-0-api-changes.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove deprecated MR and Issue endpoints and preserve V3 namespace -merge_request: 8967 -author: diff --git a/changelogs/unreleased/Add-a-shash-command-for-target-merge-request-branch.yml b/changelogs/unreleased/Add-a-shash-command-for-target-merge-request-branch.yml deleted file mode 100644 index 9fd6ea5bc52..00000000000 --- a/changelogs/unreleased/Add-a-shash-command-for-target-merge-request-branch.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds /target_branch slash command functionality for merge requests -merge_request: -author: YarNayar diff --git a/changelogs/unreleased/add_project_update_hook.yml b/changelogs/unreleased/add_project_update_hook.yml deleted file mode 100644 index 915c9538843..00000000000 --- a/changelogs/unreleased/add_project_update_hook.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add system hook for when a project is updated (other than rename/transfer) -merge_request: 5711 -author: Tommy Beadle diff --git a/changelogs/unreleased/api-remove-snippets-expires-at.yml b/changelogs/unreleased/api-remove-snippets-expires-at.yml deleted file mode 100644 index 67603bfab3b..00000000000 --- a/changelogs/unreleased/api-remove-snippets-expires-at.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'API: Remove deprecated ''expires_at'' from project snippets' -merge_request: 8723 -author: Robert Schilling diff --git a/changelogs/unreleased/babel-all-the-things.yml b/changelogs/unreleased/babel-all-the-things.yml deleted file mode 100644 index fda1c3bd562..00000000000 --- a/changelogs/unreleased/babel-all-the-things.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: use babel to transpile all non-vendor javascript assets regardless of file - extension -merge_request: 8988 -author: diff --git a/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml b/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml deleted file mode 100644 index 77750b55e7e..00000000000 --- a/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Hide version check image if there is no internet connection -merge_request: 8355 -author: Ken Ding diff --git a/changelogs/unreleased/change_queue_weight.yml b/changelogs/unreleased/change_queue_weight.yml deleted file mode 100644 index e4c650e8f79..00000000000 --- a/changelogs/unreleased/change_queue_weight.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Increase process_commit queue weight from 2 to 3 -merge_request: 9326 -author: blackst0ne diff --git a/changelogs/unreleased/clipboard-button-commit-sha.yml b/changelogs/unreleased/clipboard-button-commit-sha.yml deleted file mode 100644 index 6aa4a5664e7..00000000000 --- a/changelogs/unreleased/clipboard-button-commit-sha.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: 'Copy commit SHA to clipboard' -merge_request: 8547 diff --git a/changelogs/unreleased/contribution-calendar-scroll.yml b/changelogs/unreleased/contribution-calendar-scroll.yml deleted file mode 100644 index a504d59e61c..00000000000 --- a/changelogs/unreleased/contribution-calendar-scroll.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: contribution calendar scrolls from right to left -merge_request: -author: diff --git a/changelogs/unreleased/cop-gem-fetcher.yml b/changelogs/unreleased/cop-gem-fetcher.yml deleted file mode 100644 index 506815a5b54..00000000000 --- a/changelogs/unreleased/cop-gem-fetcher.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Cop for gem fetched from a git source -merge_request: 8856 -author: Adam Pahlevi diff --git a/changelogs/unreleased/copy-as-md.yml b/changelogs/unreleased/copy-as-md.yml deleted file mode 100644 index 637e9dc36e2..00000000000 --- a/changelogs/unreleased/copy-as-md.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Copying a rendered issue/comment will paste into GFM textareas as actual GFM -merge_request: -author: diff --git a/changelogs/unreleased/disable-autologin-on-email-confirmation-links.yml b/changelogs/unreleased/disable-autologin-on-email-confirmation-links.yml deleted file mode 100644 index 6dd0d748001..00000000000 --- a/changelogs/unreleased/disable-autologin-on-email-confirmation-links.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Disable automatic login after clicking email confirmation links -merge_request: 7472 -author: diff --git a/changelogs/unreleased/display-project-id.yml b/changelogs/unreleased/display-project-id.yml deleted file mode 100644 index 8705ed28400..00000000000 --- a/changelogs/unreleased/display-project-id.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display project ID in project settings -merge_request: 8572 -author: winniehell diff --git a/changelogs/unreleased/document-how-to-vue.yml b/changelogs/unreleased/document-how-to-vue.yml deleted file mode 100644 index 863e41b6413..00000000000 --- a/changelogs/unreleased/document-how-to-vue.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds documentation for how to use Vue.js -merge_request: 8866 -author: diff --git a/changelogs/unreleased/dynamic-todos-fixture.yml b/changelogs/unreleased/dynamic-todos-fixture.yml deleted file mode 100644 index 580bc729e3c..00000000000 --- a/changelogs/unreleased/dynamic-todos-fixture.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace static fixture for right_sidebar_spec.js -merge_request: 9211 -author: winniehell diff --git a/changelogs/unreleased/dz-nested-groups-improvements-2.yml b/changelogs/unreleased/dz-nested-groups-improvements-2.yml deleted file mode 100644 index 8e4eb7f1fff..00000000000 --- a/changelogs/unreleased/dz-nested-groups-improvements-2.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add read-only full_path and full_name attributes to Group API -merge_request: 8827 -author: diff --git a/changelogs/unreleased/empty-selection-reply-shortcut.yml b/changelogs/unreleased/empty-selection-reply-shortcut.yml deleted file mode 100644 index 5a42c98a800..00000000000 --- a/changelogs/unreleased/empty-selection-reply-shortcut.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Change the reply shortcut to focus the field even without a selection. -merge_request: 8873 -author: Brian Hall diff --git a/changelogs/unreleased/fe-commit-mr-pipelines.yml b/changelogs/unreleased/fe-commit-mr-pipelines.yml deleted file mode 100644 index b5cc6bbf8b6..00000000000 --- a/changelogs/unreleased/fe-commit-mr-pipelines.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use vue.js Pipelines table in commit and merge request view -merge_request: 8844 -author: diff --git a/changelogs/unreleased/feature-success-warning-icons-in-stages-builds.yml b/changelogs/unreleased/feature-success-warning-icons-in-stages-builds.yml deleted file mode 100644 index 5fba0332881..00000000000 --- a/changelogs/unreleased/feature-success-warning-icons-in-stages-builds.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use warning icon in mini-graph if stage passed conditionally -merge_request: 8503 -author: diff --git a/changelogs/unreleased/fix-27479.yml b/changelogs/unreleased/fix-27479.yml deleted file mode 100644 index cc72a830695..00000000000 --- a/changelogs/unreleased/fix-27479.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove new branch button for confidential issues -merge_request: -author: diff --git a/changelogs/unreleased/fix-api-mr-permissions.yml b/changelogs/unreleased/fix-api-mr-permissions.yml deleted file mode 100644 index 33b677b1f29..00000000000 --- a/changelogs/unreleased/fix-api-mr-permissions.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't allow project guests to subscribe to merge requests through the API -merge_request: -author: Robert Schilling diff --git a/changelogs/unreleased/fix-ar-connection-leaks.yml b/changelogs/unreleased/fix-ar-connection-leaks.yml deleted file mode 100644 index 9da715560ad..00000000000 --- a/changelogs/unreleased/fix-ar-connection-leaks.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't connect in Gitlab::Database.adapter_name -merge_request: -author: diff --git a/changelogs/unreleased/fix-ci-build-policy.yml b/changelogs/unreleased/fix-ci-build-policy.yml deleted file mode 100644 index 26003713ed4..00000000000 --- a/changelogs/unreleased/fix-ci-build-policy.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve build policy and access abilities -merge_request: 8711 -author: diff --git a/changelogs/unreleased/fix-deleting-project-again.yml b/changelogs/unreleased/fix-deleting-project-again.yml deleted file mode 100644 index e13215f22a7..00000000000 --- a/changelogs/unreleased/fix-deleting-project-again.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix deleting projects with pipelines and builds -merge_request: 8960 -author: diff --git a/changelogs/unreleased/fix-depr-warn.yml b/changelogs/unreleased/fix-depr-warn.yml deleted file mode 100644 index 61817027720..00000000000 --- a/changelogs/unreleased/fix-depr-warn.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: resolve deprecation warnings -merge_request: 8855 -author: Adam Pahlevi diff --git a/changelogs/unreleased/fix-gb-backwards-compatibility-coverage-ci-yml.yml b/changelogs/unreleased/fix-gb-backwards-compatibility-coverage-ci-yml.yml deleted file mode 100644 index df7e3776700..00000000000 --- a/changelogs/unreleased/fix-gb-backwards-compatibility-coverage-ci-yml.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Preserve backward compatibility CI/CD and disallow setting `coverage` regexp in global context -merge_request: 8981 -author: diff --git a/changelogs/unreleased/fix-guest-access-posting-to-notes.yml b/changelogs/unreleased/fix-guest-access-posting-to-notes.yml deleted file mode 100644 index 81377c0c6f0..00000000000 --- a/changelogs/unreleased/fix-guest-access-posting-to-notes.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent users from creating notes on resources they can't access -merge_request: -author: diff --git a/changelogs/unreleased/fix-import-encrypt-atts.yml b/changelogs/unreleased/fix-import-encrypt-atts.yml deleted file mode 100644 index e34d895570b..00000000000 --- a/changelogs/unreleased/fix-import-encrypt-atts.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ignore encrypted attributes in Import/Export -merge_request: -author: diff --git a/changelogs/unreleased/fix-import-group-members.yml b/changelogs/unreleased/fix-import-group-members.yml deleted file mode 100644 index fe580af31b3..00000000000 --- a/changelogs/unreleased/fix-import-group-members.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add ability to export project inherited group members to Import/Export -merge_request: 8923 -author: diff --git a/changelogs/unreleased/fix-job-to-pipeline-renaming.yml b/changelogs/unreleased/fix-job-to-pipeline-renaming.yml deleted file mode 100644 index d5f34b4b25d..00000000000 --- a/changelogs/unreleased/fix-job-to-pipeline-renaming.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix job to pipeline renaming -merge_request: 9147 -author: diff --git a/changelogs/unreleased/fix-references-header-parsing.yml b/changelogs/unreleased/fix-references-header-parsing.yml deleted file mode 100644 index b927279cdf4..00000000000 --- a/changelogs/unreleased/fix-references-header-parsing.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix reply by email without sub-addressing for some clients from - Microsoft and Apple -merge_request: 8620 -author: diff --git a/changelogs/unreleased/fix-scroll-test.yml b/changelogs/unreleased/fix-scroll-test.yml deleted file mode 100644 index e98ac755b88..00000000000 --- a/changelogs/unreleased/fix-scroll-test.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Change rspec test to guarantee window is resized before visiting page -merge_request: -author: diff --git a/changelogs/unreleased/fix-users-deleting-public-deployment-keys.yml b/changelogs/unreleased/fix-users-deleting-public-deployment-keys.yml deleted file mode 100644 index c9edd1de86c..00000000000 --- a/changelogs/unreleased/fix-users-deleting-public-deployment-keys.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent users from deleting system deploy keys via the project deploy key API -merge_request: -author: diff --git a/changelogs/unreleased/fix_broken_diff_discussions.yml b/changelogs/unreleased/fix_broken_diff_discussions.yml deleted file mode 100644 index 4551212759f..00000000000 --- a/changelogs/unreleased/fix_broken_diff_discussions.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make MR-review-discussions more reliable -merge_request: -author: diff --git a/changelogs/unreleased/fix_sidekiq_concurrency_warning_message_in_admin_background_job_page.yml b/changelogs/unreleased/fix_sidekiq_concurrency_warning_message_in_admin_background_job_page.yml deleted file mode 100644 index e09d03bb608..00000000000 --- a/changelogs/unreleased/fix_sidekiq_concurrency_warning_message_in_admin_background_job_page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: fix incorrect sidekiq concurrency count in admin background page -merge_request: -author: wendy0402 diff --git a/changelogs/unreleased/fwn-to-find-by-full-path.yml b/changelogs/unreleased/fwn-to-find-by-full-path.yml deleted file mode 100644 index 1427e4e7624..00000000000 --- a/changelogs/unreleased/fwn-to-find-by-full-path.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: replace `find_with_namespace` with `find_by_full_path` -merge_request: 8949 -author: Adam Pahlevi diff --git a/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml b/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml deleted file mode 100644 index f60417d185e..00000000000 --- a/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make notification_service spec DRYer by making test reusable -merge_request: -author: YarNayar diff --git a/changelogs/unreleased/git_to_html_redirection.yml b/changelogs/unreleased/git_to_html_redirection.yml deleted file mode 100644 index b2959c02c07..00000000000 --- a/changelogs/unreleased/git_to_html_redirection.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Redirect http://someproject.git to http://someproject -merge_request: -author: blackst0ne diff --git a/changelogs/unreleased/go-go-gadget-webpack.yml b/changelogs/unreleased/go-go-gadget-webpack.yml deleted file mode 100644 index 7f372ccb428..00000000000 --- a/changelogs/unreleased/go-go-gadget-webpack.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: use webpack to bundle frontend assets and use karma for frontend testing -merge_request: 7288 -author: diff --git a/changelogs/unreleased/group-label-sidebar-link.yml b/changelogs/unreleased/group-label-sidebar-link.yml deleted file mode 100644 index c11c2d4ede1..00000000000 --- a/changelogs/unreleased/group-label-sidebar-link.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed group label links in issue/merge request sidebar -merge_request: -author: diff --git a/changelogs/unreleased/hardcode-title-system-note.yml b/changelogs/unreleased/hardcode-title-system-note.yml deleted file mode 100644 index 1b0a63efa51..00000000000 --- a/changelogs/unreleased/hardcode-title-system-note.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ensure autogenerated title does not cause failing spec -merge_request: 8963 -author: brian m. carlson diff --git a/changelogs/unreleased/improve-ci-example-php-doc.yml b/changelogs/unreleased/improve-ci-example-php-doc.yml deleted file mode 100644 index 39a85e3d261..00000000000 --- a/changelogs/unreleased/improve-ci-example-php-doc.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Changed composer installer script in the CI PHP example doc -merge_request: 4342 -author: Jeffrey Cafferata diff --git a/changelogs/unreleased/improve-handleLocationHash-tests.yml b/changelogs/unreleased/improve-handleLocationHash-tests.yml deleted file mode 100644 index 8ae3dfe079c..00000000000 --- a/changelogs/unreleased/improve-handleLocationHash-tests.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve gl.utils.handleLocationHash tests -merge_request: -author: diff --git a/changelogs/unreleased/issuable-sidebar-bug.yml b/changelogs/unreleased/issuable-sidebar-bug.yml deleted file mode 100644 index 4086292eb89..00000000000 --- a/changelogs/unreleased/issuable-sidebar-bug.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed Issuable sidebar not closing on smaller/mobile sized screens -merge_request: -author: diff --git a/changelogs/unreleased/issue-20428.yml b/changelogs/unreleased/issue-20428.yml deleted file mode 100644 index 60da1c14702..00000000000 --- a/changelogs/unreleased/issue-20428.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add ability to define a coverage regex in the .gitlab-ci.yml -merge_request: 7447 -author: Leandro Camargo diff --git a/changelogs/unreleased/issue-sidebar-empty-assignee.yml b/changelogs/unreleased/issue-sidebar-empty-assignee.yml deleted file mode 100644 index 263af75b9e9..00000000000 --- a/changelogs/unreleased/issue-sidebar-empty-assignee.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Resets assignee dropdown when sidebar is open -merge_request: -author: diff --git a/changelogs/unreleased/issue_19262.yml b/changelogs/unreleased/issue_19262.yml deleted file mode 100644 index 5dea1493f23..00000000000 --- a/changelogs/unreleased/issue_19262.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Disallow system notes for closed issuables -merge_request: -author: diff --git a/changelogs/unreleased/issue_23317.yml b/changelogs/unreleased/issue_23317.yml deleted file mode 100644 index 788ae159f5e..00000000000 --- a/changelogs/unreleased/issue_23317.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix timezone on issue boards due date -merge_request: -author: diff --git a/changelogs/unreleased/issue_27211.yml b/changelogs/unreleased/issue_27211.yml deleted file mode 100644 index ad48fec5d85..00000000000 --- a/changelogs/unreleased/issue_27211.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove unused js response from refs controller -merge_request: -author: diff --git a/changelogs/unreleased/jej-pages-picked-from-ee.yml b/changelogs/unreleased/jej-pages-picked-from-ee.yml deleted file mode 100644 index ee4a43a93db..00000000000 --- a/changelogs/unreleased/jej-pages-picked-from-ee.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added GitLab Pages to CE -merge_request: 8463 -author: diff --git a/changelogs/unreleased/label-promotion.yml b/changelogs/unreleased/label-promotion.yml deleted file mode 100644 index 2ab997bf420..00000000000 --- a/changelogs/unreleased/label-promotion.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Project labels can now be promoted to group labels" -merge_request: 7242 -author: Olaf Tomalka diff --git a/changelogs/unreleased/lfs-noauth-public-repo.yml b/changelogs/unreleased/lfs-noauth-public-repo.yml deleted file mode 100644 index 60f62d7691b..00000000000 --- a/changelogs/unreleased/lfs-noauth-public-repo.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Support unauthenticated LFS object downloads for public projects -merge_request: 8824 -author: Ben Boeckel diff --git a/changelogs/unreleased/markdown-plantuml.yml b/changelogs/unreleased/markdown-plantuml.yml deleted file mode 100644 index c855f0cbcf7..00000000000 --- a/changelogs/unreleased/markdown-plantuml.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: PlantUML support for Markdown -merge_request: 8588 -author: Horacio Sanson diff --git a/changelogs/unreleased/merge-request-tabs-fixture.yml b/changelogs/unreleased/merge-request-tabs-fixture.yml deleted file mode 100644 index 289cd7b604a..00000000000 --- a/changelogs/unreleased/merge-request-tabs-fixture.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace static fixture for merge_request_tabs_spec.js -merge_request: 9172 -author: winniehell diff --git a/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml b/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml deleted file mode 100644 index f32b3aea3c8..00000000000 --- a/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: adds avatar for discussion note -merge_request: 8734 -author: diff --git a/changelogs/unreleased/mr-tabs-container-offset.yml b/changelogs/unreleased/mr-tabs-container-offset.yml deleted file mode 100644 index c5df8abfcf2..00000000000 --- a/changelogs/unreleased/mr-tabs-container-offset.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed merge requests tab extra margin when fixed to window -merge_request: -author: diff --git a/changelogs/unreleased/newline-eslint-rule.yml b/changelogs/unreleased/newline-eslint-rule.yml deleted file mode 100644 index 5ce080b6912..00000000000 --- a/changelogs/unreleased/newline-eslint-rule.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Flag multiple empty lines in eslint, fix offenses. -merge_request: 8137 -author: diff --git a/changelogs/unreleased/no-sidebar-on-action-btn-click.yml b/changelogs/unreleased/no-sidebar-on-action-btn-click.yml deleted file mode 100644 index 09e0b3a12d8..00000000000 --- a/changelogs/unreleased/no-sidebar-on-action-btn-click.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: dismiss sidebar on repo buttons click -merge_request: 8798 -author: Adam Pahlevi diff --git a/changelogs/unreleased/no_project_notes.yml b/changelogs/unreleased/no_project_notes.yml deleted file mode 100644 index 6106c027360..00000000000 --- a/changelogs/unreleased/no_project_notes.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Support notes when a project is not specified (personal snippet notes) -merge_request: 8468 -author: diff --git a/changelogs/unreleased/pms-lowercase-system-notes.yml b/changelogs/unreleased/pms-lowercase-system-notes.yml deleted file mode 100644 index c2fa1a7fad0..00000000000 --- a/changelogs/unreleased/pms-lowercase-system-notes.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make all system notes lowercase -merge_request: 8807 -author: diff --git a/changelogs/unreleased/redesign-searchbar-admin-project-26794.yml b/changelogs/unreleased/redesign-searchbar-admin-project-26794.yml deleted file mode 100644 index 547a7c6755c..00000000000 --- a/changelogs/unreleased/redesign-searchbar-admin-project-26794.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Redesign searchbar in admin project list -merge_request: 8776 -author: diff --git a/changelogs/unreleased/redirect-to-commit-when-only-commit-found.yml b/changelogs/unreleased/redirect-to-commit-when-only-commit-found.yml deleted file mode 100644 index e0f7e11b6d1..00000000000 --- a/changelogs/unreleased/redirect-to-commit-when-only-commit-found.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Search feature: redirects to commit page if query is commit sha and only commit - found' -merge_request: 8028 -author: YarNayar diff --git a/changelogs/unreleased/relative-url-assets.yml b/changelogs/unreleased/relative-url-assets.yml deleted file mode 100644 index 0877664aca4..00000000000 --- a/changelogs/unreleased/relative-url-assets.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: allow relative url change without recompiling frontend assets -merge_request: 8831 -author: diff --git a/changelogs/unreleased/remove-deploy-key-endpoint.yml b/changelogs/unreleased/remove-deploy-key-endpoint.yml deleted file mode 100644 index 3ff69adb4d3..00000000000 --- a/changelogs/unreleased/remove-deploy-key-endpoint.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'API: Remove /projects/:id/keys/.. endpoints' -merge_request: 8716 -author: Robert Schilling diff --git a/changelogs/unreleased/remove-issue-and-mr-counts-from-labels-page.yml b/changelogs/unreleased/remove-issue-and-mr-counts-from-labels-page.yml deleted file mode 100644 index b75b4644ba3..00000000000 --- a/changelogs/unreleased/remove-issue-and-mr-counts-from-labels-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove issue and MR counts from labels index -merge_request: -author: diff --git a/changelogs/unreleased/route-map.yml b/changelogs/unreleased/route-map.yml deleted file mode 100644 index 9b6df0c54af..00000000000 --- a/changelogs/unreleased/route-map.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add 'View on [env]' link to blobs and individual files in diffs -merge_request: 8867 -author: diff --git a/changelogs/unreleased/rs-warden-blocked-users.yml b/changelogs/unreleased/rs-warden-blocked-users.yml deleted file mode 100644 index c0c23fb6f11..00000000000 --- a/changelogs/unreleased/rs-warden-blocked-users.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't perform Devise trackable updates on blocked User records -merge_request: 8915 -author: diff --git a/changelogs/unreleased/sh-add-index-to-ci-trigger-requests.yml b/changelogs/unreleased/sh-add-index-to-ci-trigger-requests.yml deleted file mode 100644 index bab76812a17..00000000000 --- a/changelogs/unreleased/sh-add-index-to-ci-trigger-requests.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add index to ci_trigger_requests for commit_id -merge_request: -author: diff --git a/changelogs/unreleased/sh-add-labels-index.yml b/changelogs/unreleased/sh-add-labels-index.yml deleted file mode 100644 index b948a75081c..00000000000 --- a/changelogs/unreleased/sh-add-labels-index.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add indices to improve loading of labels page -merge_request: -author: diff --git a/changelogs/unreleased/slash-commands-typo.yml b/changelogs/unreleased/slash-commands-typo.yml deleted file mode 100644 index e6ffb94bd08..00000000000 --- a/changelogs/unreleased/slash-commands-typo.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed "substract" typo on /help/user/project/slash_commands -merge_request: 8976 -author: Jason Aquino diff --git a/changelogs/unreleased/small-screen-fullscreen-button.yml b/changelogs/unreleased/small-screen-fullscreen-button.yml deleted file mode 100644 index f4c269bc473..00000000000 --- a/changelogs/unreleased/small-screen-fullscreen-button.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display fullscreen button on small screens -merge_request: 5302 -author: winniehell diff --git a/changelogs/unreleased/snippets-search-performance.yml b/changelogs/unreleased/snippets-search-performance.yml deleted file mode 100644 index 2895478abfd..00000000000 --- a/changelogs/unreleased/snippets-search-performance.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Reduced query count for snippet search -merge_request: -author: diff --git a/changelogs/unreleased/tc-only-mr-button-if-allowed.yml b/changelogs/unreleased/tc-only-mr-button-if-allowed.yml deleted file mode 100644 index a7f5dcb560c..00000000000 --- a/changelogs/unreleased/tc-only-mr-button-if-allowed.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Only show Merge Request button when user can create a MR -merge_request: 8639 -author: diff --git a/changelogs/unreleased/terminal-max-session-time.yml b/changelogs/unreleased/terminal-max-session-time.yml deleted file mode 100644 index db1e66770d1..00000000000 --- a/changelogs/unreleased/terminal-max-session-time.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Introduce maximum session time for terminal websocket connection -merge_request: 8413 -author: diff --git a/changelogs/unreleased/updated-pages-0-3-1.yml b/changelogs/unreleased/updated-pages-0-3-1.yml deleted file mode 100644 index 8622b823c86..00000000000 --- a/changelogs/unreleased/updated-pages-0-3-1.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update GitLab Pages to v0.3.1 -merge_request: -author: diff --git a/changelogs/unreleased/upgrade-babel-v6.yml b/changelogs/unreleased/upgrade-babel-v6.yml deleted file mode 100644 index 55f9b3e407c..00000000000 --- a/changelogs/unreleased/upgrade-babel-v6.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: upgrade babel 5.8.x to babel 6.22.x -merge_request: 9072 -author: diff --git a/changelogs/unreleased/upgrade-omniauth.yml b/changelogs/unreleased/upgrade-omniauth.yml deleted file mode 100644 index 7e0334566dc..00000000000 --- a/changelogs/unreleased/upgrade-omniauth.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Upgrade omniauth gem to 1.3.2 -merge_request: -author: diff --git a/changelogs/unreleased/upgrade-webpack-v2-2.yml b/changelogs/unreleased/upgrade-webpack-v2-2.yml deleted file mode 100644 index 6a49859d68c..00000000000 --- a/changelogs/unreleased/upgrade-webpack-v2-2.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: upgrade to webpack v2.2 -merge_request: 9078 -author: diff --git a/changelogs/unreleased/wip-mr-from-commits.yml b/changelogs/unreleased/wip-mr-from-commits.yml deleted file mode 100644 index 0083798be08..00000000000 --- a/changelogs/unreleased/wip-mr-from-commits.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Mark MR as WIP when pushing WIP commits -merge_request: 8124 -author: Jurre Stender @jurre diff --git a/changelogs/unreleased/zj-format-chat-messages.yml b/changelogs/unreleased/zj-format-chat-messages.yml deleted file mode 100644 index 2494884f5c9..00000000000 --- a/changelogs/unreleased/zj-format-chat-messages.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Reformat messages ChatOps -merge_request: 8528 -author: diff --git a/changelogs/unreleased/zj-remove-deprecated-ci-service.yml b/changelogs/unreleased/zj-remove-deprecated-ci-service.yml deleted file mode 100644 index 044f4ae627d..00000000000 --- a/changelogs/unreleased/zj-remove-deprecated-ci-service.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove deprecated GitlabCiService -merge_request: -author: diff --git a/changelogs/unreleased/zj-requeue-pending-delete.yml b/changelogs/unreleased/zj-requeue-pending-delete.yml deleted file mode 100644 index 464c5948f8c..00000000000 --- a/changelogs/unreleased/zj-requeue-pending-delete.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Requeue pending deletion projects -merge_request: -author: From 12ac140a119b6802ebdfc3c596264f7fc292d4df Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 22 Feb 2017 11:36:02 -0300 Subject: [PATCH 105/129] Update VERSION to 8.18.0-pre --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5c99c061a47..64de8316674 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.17.0-pre +8.18.0-pre From cf521c95761540f273804d23a1150dbb0af4e63b Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 22 Feb 2017 15:43:01 +0100 Subject: [PATCH 106/129] Allow setting of a custom connection pool host This allows you to set a custom host when calling Gitlab::Database.create_connection_pool. This is necessary for load balancing as in this case we want to inherit all settings except for the hostname. --- lib/gitlab/database.rb | 7 ++++++- spec/lib/gitlab/database_spec.rb | 21 ++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index a47d7e98a62..d160cadc2d0 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -79,11 +79,16 @@ module Gitlab end end - def self.create_connection_pool(pool_size) + # pool_size - The size of the DB pool. + # host - An optional host name to use instead of the default one. + def self.create_connection_pool(pool_size, host = nil) # See activerecord-4.2.7.1/lib/active_record/connection_adapters/connection_specification.rb env = Rails.env original_config = ActiveRecord::Base.configurations + env_config = original_config[env].merge('pool' => pool_size) + env_config['host'] = host if host + config = original_config.merge(env => env_config) spec = diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index f01c42aff91..edd01d032c8 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -119,9 +119,24 @@ describe Gitlab::Database, lib: true do it 'creates a new connection pool with specific pool size' do pool = described_class.create_connection_pool(5) - expect(pool) - .to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool) - expect(pool.spec.config[:pool]).to eq(5) + begin + expect(pool) + .to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool) + + expect(pool.spec.config[:pool]).to eq(5) + ensure + pool.disconnect! + end + end + + it 'allows setting of a custom hostname' do + pool = described_class.create_connection_pool(5, '127.0.0.1') + + begin + expect(pool.spec.config[:host]).to eq('127.0.0.1') + ensure + pool.disconnect! + end end end From 27e4b9050caedcc8808dec683246051526eb5450 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 16:24:55 +0100 Subject: [PATCH 107/129] Add links to previous/next articles --- doc/pages/getting_started_part_one.md | 9 ++++----- doc/pages/getting_started_part_three.md | 7 +++---- doc/pages/getting_started_part_two.md | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md index 8a98afeeb86..8ae4779108c 100644 --- a/doc/pages/getting_started_part_one.md +++ b/doc/pages/getting_started_part_one.md @@ -26,7 +26,7 @@ GitLab Pages only supports static websites, meaning, your output files must be H To create your static site, you can either hardcode in HTML, CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/) to simplify your code and build the static site for you, which is highly recommendable and much faster than hardcoding. -#### Further Reading +--- - Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) - Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site @@ -158,7 +158,6 @@ Now that you hopefully understand why you need all of this, it's simple: > Note: **do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). -## Further Reading - -- Read through GitLab Pages from A to Z _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ -- Read through GitLab Pages from A to Z _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ +||| +|:--|--:| +||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)| diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md index 0bc6a601a09..28fb7f08bdf 100644 --- a/doc/pages/getting_started_part_three.md +++ b/doc/pages/getting_started_part_three.md @@ -273,7 +273,6 @@ What you can do with GitLab CI is pretty much up to your creativity. Once you ge - On this blog post, we go through the process of [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) to deploy this website you're looking at, docs.gitlab.com. - On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/). -## Further Reading - -- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ -- Read through _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ +||| +|:--|--:| +|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)| diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md index b1d58b5b024..19e393f2623 100644 --- a/doc/pages/getting_started_part_two.md +++ b/doc/pages/getting_started_part_two.md @@ -106,7 +106,6 @@ On the contrary, if you deploy your website after forking one of our [default ex baseurl: "" ``` -## Further Reading - -- Read through _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ -- Read through _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ +||| +|:--|--:| +|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_three.md)| From 9f56b57b55307fc3fa25737b89806564259354f8 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Tue, 21 Feb 2017 16:47:25 -0300 Subject: [PATCH 108/129] Present GitLab version for each V3 to V4 API change on v3_to_v4.md --- .../28458-present-gitlab-version-for-v4-changes-on-docs.yml | 4 ++++ doc/api/v3_to_v4.md | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml diff --git a/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml b/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml new file mode 100644 index 00000000000..dbbe8a19204 --- /dev/null +++ b/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml @@ -0,0 +1,4 @@ +--- +title: Present GitLab version for each V3 to V4 API change on v3_to_v4.md +merge_request: +author: diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 59d7f0634b2..d99a52e4dc2 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -4,7 +4,7 @@ Our V4 API version is currently available as *Beta*! It means that V3 will still be supported and remain unchanged for now, but be aware that the following changes are in V4: -### Changes +### 8.17 - Removed `/projects/:search` (use: `/projects?search=x`) - `iid` filter has been removed from `projects/:id/issues` @@ -12,6 +12,9 @@ changes are in V4: - Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`) - Project snippets do not return deprecated field `expires_at` - Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) + +### 9.0 + - Status 409 returned for POST `project/:id/members` when a member already exists - Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` - Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) From eeb567d1b1b1988e9d3e07f1a6d7fa23ce7d06e3 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Wed, 22 Feb 2017 10:24:38 +0100 Subject: [PATCH 109/129] Add Merge Request link to the v3 to v4 documentation Similar to changelog, mention the Merge Request id & link for each change. --- doc/api/v3_to_v4.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index d99a52e4dc2..1fea3d3407f 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -6,18 +6,18 @@ changes are in V4: ### 8.17 -- Removed `/projects/:search` (use: `/projects?search=x`) -- `iid` filter has been removed from `projects/:id/issues` -- `projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids` -- Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`) -- Project snippets do not return deprecated field `expires_at` -- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) +- Removed `/projects/:search` (use: `/projects?search=x`) [!8877](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8877) +- `iid` filter has been removed from `projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967) +- `projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793) +- Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`) [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793) +- Project snippets do not return deprecated field `expires_at` [!8723](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8723) +- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) [!8716](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8716) ### 9.0 -- Status 409 returned for POST `project/:id/members` when a member already exists -- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` -- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) +- Status 409 returned for POST `project/:id/members` when a member already exists [!9093](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9093) +- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` [!9328](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9328) +- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) [!8853](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8853) - `/licences` - `/licences/:key` - `/gitignores` @@ -26,17 +26,17 @@ changes are in V4: - `/gitignores/:key` - `/gitlab_ci_ymls/:key` - `/dockerfiles/:key` -- Moved `/projects/fork/:id` to `/projects/:id/fork` -- Moved `DELETE /todos` to `POST /todos/mark_as_done` and `DELETE /todos/:todo_id` to `POST /todos/:todo_id/mark_as_done` -- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters -- Return pagination headers for all endpoints that return an array -- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead -- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` -- Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. -- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) -- Renamed param `branch_name` to `branch` on the following endpoints +- Moved `/projects/fork/:id` to `/projects/:id/fork` [!8940](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8940) +- Moved `DELETE /todos` to `POST /todos/mark_as_done` and `DELETE /todos/:todo_id` to `POST /todos/:todo_id/mark_as_done` [!9410](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9410) +- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters [!8962](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8962) +- Return pagination headers for all endpoints that return an array [!8606](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8606) +- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead [!9366](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9366) +- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` [!9371](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9371) +- Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. [!9325](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9325) +- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) [!8849](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8849) +- Renamed param `branch_name` to `branch` on the following endpoints [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936) - POST `:id/repository/branches` - POST `:id/repository/commits` - POST/PUT/DELETE `:id/repository/files` -- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response - +- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936) +- Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736) From e5e7fb55ed325bedd8aab4fb2c07d514bc920f95 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 12:59:01 -0300 Subject: [PATCH 110/129] remove link to unfinished video --- doc/pages/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/pages/index.md b/doc/pages/index.md index fe328748668..242fdf3147f 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -13,7 +13,6 @@ - [Part 1: Static sites, domains, DNS records, and SSL/TLS certificates](getting_started_part_one.md) - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) - Video tutorial: [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) - - Video tutorial: [How to publish a website with GitLab Pages on GitLab.com: from scratch](#LINK) - [Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md) - Secure GitLab Pages custom domain with SSL/TLS certificates - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) From 23e43ec5b18b35e6fe92adcbf3ca3d9a51a210b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 22 Feb 2017 16:00:49 +0000 Subject: [PATCH 111/129] Improve `Gitlab::EeCompatCheck` by using the `git apply --3way` flag This should solve 99% of the false-positive of the `ee_compat_check` job. --- lib/gitlab/ee_compat_check.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index c8e36d8ff4a..e0fdf3f3d64 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -119,7 +119,7 @@ module Gitlab step("Reseting to latest master", %w[git reset --hard origin/master]) step("Checking if #{patch_path} applies cleanly to EE/master") - output, status = Gitlab::Popen.popen(%W[git apply --check #{patch_path}]) + output, status = Gitlab::Popen.popen(%W[git apply --check --3way #{patch_path}]) unless status.zero? failed_files = output.lines.reduce([]) do |memo, line| From 3dcc2ca593ecc989265f3ed9487220836e91619e Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:06:44 -0300 Subject: [PATCH 112/129] fix spelling, add intermediate cert link --- doc/pages/getting_started_part_one.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md index 8ae4779108c..c3bee1f164c 100644 --- a/doc/pages/getting_started_part_one.md +++ b/doc/pages/getting_started_part_one.md @@ -135,7 +135,7 @@ Regardless the CA you choose, the steps to add your certificate to your Pages pr #### What do you need 1. A PEM certificate -1. An intermediary certificate +1. An intermediate certificate 1. A public key ![Pages project - adding certificates](img/add_certificate_to_pages.png) @@ -145,7 +145,7 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New #### What's what? - A PEM certificate is the certificate generated by the CA, which needs to be added to the field **Certificate (PEM)**. -- An [intermediary certificate][] \(aka "root certificate"\) is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. +- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) \(aka "root certificate"\) is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. - A public key is an encrypted key which validates your PEM against your domain. #### Now what? @@ -153,7 +153,7 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New Now that you hopefully understand why you need all of this, it's simple: - Your PEM certificate needs to be added to the first field -- If your certificate is missing its intermediary, copy and paste the root certificate (usually available from your CA website) and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/), just jumping a line between them. +- If your certificate is missing its intermediate, copy and paste the root certificate (usually available from your CA website) and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/), just jumping a line between them. - Copy your public key and paste it in the last field > Note: **do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). From 20009ca9a29cc1011c7235aae652592d77318d98 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:07:14 -0300 Subject: [PATCH 113/129] typo --- doc/pages/getting_started_part_three.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md index 28fb7f08bdf..10ae3abb9e5 100644 --- a/doc/pages/getting_started_part_three.md +++ b/doc/pages/getting_started_part_three.md @@ -187,7 +187,7 @@ test: The `test` job is running on the stage `test`, Jekyll will build the site in a directory called `test`, and this job will affect all the branches except `master`. -The best benefit of applying _stages_ to different _jobs_ is that every job in the same stage builds in parallel. So, if your web app needs more than one test before being deployed, you can run all your test at the same time, it's not necessary to wait on test to finish to run the other. Of course, this is just a brief introduction of GitLab CI and GitLab Runner, which are tools much more powerful than that. This is what you need to be able to create and tweak your builds for your GitLab Pages site. +The best benefit of applying _stages_ to different _jobs_ is that every job in the same stage builds in parallel. So, if your web app needs more than one test before being deployed, you can run all your test at the same time, it's not necessary to wait one test to finish to run the other. Of course, this is just a brief introduction of GitLab CI and GitLab Runner, which are tools much more powerful than that. This is what you need to be able to create and tweak your builds for your GitLab Pages site. #### Before Script From f9079cb5ae43fa2cae0346c8dfd77b1e817fb78f Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:16:54 -0300 Subject: [PATCH 114/129] wrap text - part 1 - [ci skip] --- doc/pages/getting_started_part_one.md | 179 ++++++++++++++++++++------ 1 file changed, 141 insertions(+), 38 deletions(-) diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md index c3bee1f164c..57fc5d21b96 100644 --- a/doc/pages/getting_started_part_one.md +++ b/doc/pages/getting_started_part_one.md @@ -10,11 +10,17 @@ ---- -This is a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. +This is a comprehensive guide, made for those who want to +publish a website with GitLab Pages but aren't familiar with +the entire process involved. -To **enable** GitLab Pages for GitLab CE (Community Edition) and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). +To **enable** GitLab Pages for GitLab CE (Community Edition) +and GitLab EE (Enterprise Edition), please read the +[admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), +and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). -> For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. +> For this guide, we assume you already have GitLab Pages +server up and running for your GitLab instance. ## What you need to know before getting started @@ -22,9 +28,13 @@ Before we begin, let's understand a few concepts first. ### Static Sites -GitLab Pages only supports static websites, meaning, your output files must be HTML, CSS, and JavaScript only. +GitLab Pages only supports static websites, meaning, +your output files must be HTML, CSS, and JavaScript only. -To create your static site, you can either hardcode in HTML, CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/) to simplify your code and build the static site for you, which is highly recommendable and much faster than hardcoding. +To create your static site, you can either hardcode in HTML, +CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/) +to simplify your code and build the static site for you, +which is highly recommendable and much faster than hardcoding. --- @@ -35,37 +45,72 @@ To create your static site, you can either hardcode in HTML, CSS, and JS, or use ### GitLab Pages Domain -If you set up a GitLab Pages project on GitLab.com, it will automatically be accessible under a [subdomain of `.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). The `` is defined by your username on GitLab.com, or the group name you created this project under. +If you set up a GitLab Pages project on GitLab.com, +it will automatically be accessible under a +[subdomain of `.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). +The `` is defined by your username on GitLab.com, +or the group name you created this project under. -> Note: If you use your own GitLab instance to deploy your site with GitLab Pages, check with your sysadmin what's your Pages wildcard domain. This guide is valid for any GitLab instance, you just need to replace Pages wildcard domain on GitLab.com (`*.gitlab.io`) with your own. +> Note: If you use your own GitLab instance to deploy your +site with GitLab Pages, check with your sysadmin what's your +Pages wildcard domain. This guide is valid for any GitLab instance, +you just need to replace Pages wildcard domain on GitLab.com +(`*.gitlab.io`) with your own. #### Practical examples **Project Websites:** -- You created a project called `blog` under your username `john`, therefore your project URL is `https://gitlab.com/john/blog/`. Once you enable GitLab Pages for this project, and build your site, it will be available under `https://john.gitlab.io/blog/`. -- You created a group for all your websites called `websites`, and a project within this group is called `blog`. Your project URL is `https://gitlab.com/websites/blog/`. Once you enable GitLab Pages for this project, the site will live under `https://websites.gitlab.io/blog/`. +- You created a project called `blog` under your username `john`, +therefore your project URL is `https://gitlab.com/john/blog/`. +Once you enable GitLab Pages for this project, and build your site, +it will be available under `https://john.gitlab.io/blog/`. +- You created a group for all your websites called `websites`, +and a project within this group is called `blog`. Your project +URL is `https://gitlab.com/websites/blog/`. Once you enable +GitLab Pages for this project, the site will live under +`https://websites.gitlab.io/blog/`. **User and Group Websites:** -- Under your username, `john`, you created a project called `john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`. Once you enable GitLab Pages for your project, your website will be published under `https://john.gitlab.io`. -- Under your group `websites`, you created a project called `websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project, your website will be published under `https://websites.gitlab.io`. +- Under your username, `john`, you created a project called +`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`. +Once you enable GitLab Pages for your project, your website +will be published under `https://john.gitlab.io`. +- Under your group `websites`, you created a project called +`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project, +your website will be published under `https://websites.gitlab.io`. **General example:** -- On GitLab.com, a project site will always be available under `https://namespace.gitlab.io/project-name` -- On GitLab.com, a user or group website will be available under `https://namespace.gitlab.io/` -- On your GitLab instance, replace `gitlab.io` above with your Pages server domain. Ask your sysadmin for this information. +- On GitLab.com, a project site will always be available under +`https://namespace.gitlab.io/project-name` +- On GitLab.com, a user or group website will be available under +`https://namespace.gitlab.io/` +- On your GitLab instance, replace `gitlab.io` above with your +Pages server domain. Ask your sysadmin for this information. ### DNS Records -A Domain Name System (DNS) web service routes visitors to websites by translating domain names (such as `www.example.com`) into the numeric IP addresses (such as `192.0.2.1`) that computers use to connect to each other. +A Domain Name System (DNS) web service routes visitors to websites +by translating domain names (such as `www.example.com`) into the +numeric IP addresses (such as `192.0.2.1`) that computers use to +connect to each other. -A DNS record is created to point a (sub)domain to a certain location, which can be an IP address or another domain. In case you want to use GitLab Pages with your own (sub)domain, you need to access your domain's registrar control panel to add a DNS record pointing it back to your GitLab Pages site. +A DNS record is created to point a (sub)domain to a certain location, +which can be an IP address or another domain. In case you want to use +GitLab Pages with your own (sub)domain, you need to access your domain's +registrar control panel to add a DNS record pointing it back to your +GitLab Pages site. -Note that **how to** add DNS records depends on which server your domain is hosted on. Every control panel has its own place to do it. If you are not an admin of your domain, and don't have access to your registrar, you'll need to ask for the technical support of your hosting service to do it for you. +Note that **how to** add DNS records depends on which server your domain +is hosted on. Every control panel has its own place to do it. If you are +not an admin of your domain, and don't have access to your registrar, +you'll need to ask for the technical support of your hosting service +to do it for you. -To help you out, we've gathered some instructions on how to do that for the most popular hosting services: +To help you out, we've gathered some instructions on how to do that +for the most popular hosting services: - [Amazon](http://docs.aws.amazon.com/gettingstarted/latest/swh/getting-started-configure-route53.html) - [Bluehost](https://my.bluehost.com/cgi/help/559) @@ -78,11 +123,19 @@ To help you out, we've gathered some instructions on how to do that for the most - [Media Temple](https://mediatemple.net/community/products/dv/204403794/how-can-i-change-the-dns-records-for-my-domain) - [Microsoft](https://msdn.microsoft.com/en-us/library/bb727018.aspx) -If your hosting service is not listed above, you can just try to search the web for "how to add dns record on ". +If your hosting service is not listed above, you can just try to +search the web for "how to add dns record on ". #### DNS A record -In case you want to point a root domain (`example.com`) to your GitLab Pages site, deployed to `namespace.gitlab.io`, you need to log into your domain's admin control panel and add a DNS `A` record pointing your domain to Pages' server IP address. For projects on GitLab.com, this IP is `104.208.235.32`. For projects leaving in other GitLab instances (CE or EE), please contact your sysadmin asking for this information (which IP address is Pages server running on your instance). +In case you want to point a root domain (`example.com`) to your +GitLab Pages site, deployed to `namespace.gitlab.io`, you need to +log into your domain's admin control panel and add a DNS `A` record +pointing your domain to Pages' server IP address. For projects on +GitLab.com, this IP is `104.208.235.32`. For projects leaving in +other GitLab instances (CE or EE), please contact your sysadmin +asking for this information (which IP address is Pages server +running on your instance). **Practical Example:** @@ -90,9 +143,15 @@ In case you want to point a root domain (`example.com`) to your GitLab Pages sit #### DNS CNAME record -In case you want to point a subdomain (`hello-world.example.com`) to your GitLab Pages site initially deployed to `namespace.gitlab.io`, you need to log into your domain's admin control panel and add a DNS `CNAME` record pointing your subdomain to your website URL (`namespace.gitlab.io`) address. +In case you want to point a subdomain (`hello-world.example.com`) +to your GitLab Pages site initially deployed to `namespace.gitlab.io`, +you need to log into your domain's admin control panel and add a DNS +`CNAME` record pointing your subdomain to your website URL +(`namespace.gitlab.io`) address. -Notice that, despite it's a user or project website, the `CNAME` should point to your Pages domain (`namespace.gitlab.io`), without any `/project-name`. +Notice that, despite it's a user or project website, the `CNAME` +should point to your Pages domain (`namespace.gitlab.io`), +without any `/project-name`. **Practical Example:** @@ -107,30 +166,61 @@ Notice that, despite it's a user or project website, the `CNAME` should point to > **Notes**: > -> - **Do not** use a CNAME record if you want to point your `domain.com` to your GitLab Pages site. Use an `A` record instead. -> - **Do not** add any special chars after the default Pages domain. E.g., **do not** point your `subdomain.domain.com` to `namespace.gitlab.io.` or `namespace.gitlab.io/`. +> - **Do not** use a CNAME record if you want to point your +`domain.com` to your GitLab Pages site. Use an `A` record instead. +> - **Do not** add any special chars after the default Pages +domain. E.g., **do not** point your `subdomain.domain.com` to +`namespace.gitlab.io.` or `namespace.gitlab.io/`. ### SSL/TLS Certificates -Every GitLab Pages project on GitLab.com will be available under HTTPS for the default Pages domain (`*.gitlab.io`). Once you set up your Pages project with your custom (sub)domain, if you want it secured by HTTPS, you will have to issue a certificate for that (sub)domain and install it on your project. +Every GitLab Pages project on GitLab.com will be available under +HTTPS for the default Pages domain (`*.gitlab.io`). Once you set +up your Pages project with your custom (sub)domain, if you want +it secured by HTTPS, you will have to issue a certificate for that +(sub)domain and install it on your project. -> Note: certificates are NOT required to add to your custom (sub)domain on your GitLab Pages project, though they are highly recommendable. +> Note: certificates are NOT required to add to your custom +(sub)domain on your GitLab Pages project, though they are +highly recommendable. -The importance of having any website securely served under HTTPS is explained on the introductory section of the blog post [Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview). +The importance of having any website securely served under HTTPS +is explained on the introductory section of the blog post +[Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview). -The reason why certificates are so important is that they encrypt the connection between the **client** (you, me, your visitors) and the **server** (where you site lives), through a keychain of authentications and validations. +The reason why certificates are so important is that they encrypt +the connection between the **client** (you, me, your visitors) +and the **server** (where you site lives), through a keychain of +authentications and validations. ### Issuing Certificates -GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by [Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) and self-signed certificates. Of course, [you'd rather issue a certificate than generate a self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate), for security reasons and for having browsers trusting your site's certificate. +GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by +[Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) +and self-signed certificates. Of course, +[you'd rather issue a certificate than generate a self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate), +for security reasons and for having browsers trusting your +site's certificate. -There are several different kinds of certificates, each one with certain security level. A static personal website will not require the same security level as an online banking web app, for instance. There are a couple Certificate Authorities that offer free certificates, aiming to make the internet more secure to everyone. The most popular is [Let's Encrypt](https://letsencrypt.org/), which issues certificates trusted by most of browsers, it's open source, and free to use. Please read through this tutorial to understand [how to secure your GitLab Pages website with Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/). +There are several different kinds of certificates, each one +with certain security level. A static personal website will +not require the same security level as an online banking web app, +for instance. There are a couple Certificate Authorities that +offer free certificates, aiming to make the internet more secure +to everyone. The most popular is [Let's Encrypt](https://letsencrypt.org/), +which issues certificates trusted by most of browsers, it's open +source, and free to use. Please read through this tutorial to +understand [how to secure your GitLab Pages website with Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/). -With the same popularity, there are [certificates issued by CloudFlare](https://www.cloudflare.com/ssl/), which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-free-cdn-and-you/). Their certs are valid up to 15 years. Read through the tutorial on [how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/). +With the same popularity, there are [certificates issued by CloudFlare](https://www.cloudflare.com/ssl/), +which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-free-cdn-and-you/). +Their certs are valid up to 15 years. Read through the tutorial on +[how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/). ### Adding certificates to your project -Regardless the CA you choose, the steps to add your certificate to your Pages project are the same. +Regardless the CA you choose, the steps to add your certificate to +your Pages project are the same. #### What do you need @@ -144,19 +234,32 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New #### What's what? -- A PEM certificate is the certificate generated by the CA, which needs to be added to the field **Certificate (PEM)**. -- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) \(aka "root certificate"\) is the part of the encryption keychain that identifies the CA. Usually it's combined with the PEM certificate, but there are some cases in which you need to add them manually. [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) are one of these cases. -- A public key is an encrypted key which validates your PEM against your domain. +- A PEM certificate is the certificate generated by the CA, +which needs to be added to the field **Certificate (PEM)**. +- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) (aka "root certificate") is +the part of the encryption keychain that identifies the CA. +Usually it's combined with the PEM certificate, but there are +some cases in which you need to add them manually. +[CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) +are one of these cases. +- A public key is an encrypted key which validates +your PEM against your domain. #### Now what? -Now that you hopefully understand why you need all of this, it's simple: +Now that you hopefully understand why you need all +of this, it's simple: - Your PEM certificate needs to be added to the first field -- If your certificate is missing its intermediate, copy and paste the root certificate (usually available from your CA website) and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/), just jumping a line between them. +- If your certificate is missing its intermediate, copy +and paste the root certificate (usually available from your CA website) +and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/), +just jumping a line between them. - Copy your public key and paste it in the last field -> Note: **do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). +> Note: **do not** open certificates or encryption keys in +regular text editors. Always use code editors (such as +Sublime Text, Atom, Dreamweaver, Brackets, etc). ||| |:--|--:| From ce5420ab8fde3b147d3e2e1068b6b6313883a224 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:22:14 -0300 Subject: [PATCH 115/129] fix link --- doc/pages/getting_started_part_two.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md index 19e393f2623..f211b0a3363 100644 --- a/doc/pages/getting_started_part_two.md +++ b/doc/pages/getting_started_part_two.md @@ -70,7 +70,7 @@ To turn a **project website** forked from the Pages group into a **user/group** #### Create a Project from Scratch -1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the [examples above](#practical-examples). +1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the [pratical examples](getting_started_part_one.md#practical-examples). 1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. 1. From the your **Project**'s page, click **Set up CI**: From 08e9d2b5c8fdf813e88a0594d6f96a4767661f68 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:27:02 -0300 Subject: [PATCH 116/129] wrapping text - part 2 [ci skip] --- doc/pages/getting_started_part_two.md | 79 +++++++++++++++++++++------ 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md index f211b0a3363..15f628ac1fb 100644 --- a/doc/pages/getting_started_part_two.md +++ b/doc/pages/getting_started_part_two.md @@ -12,7 +12,9 @@ ## Setting Up GitLab Pages -For a complete step-by-step tutorial, please read the blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain what do you need and why do you need them. +For a complete step-by-step tutorial, please read the +blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain +what do you need and why do you need them. @@ -20,17 +22,20 @@ For a complete step-by-step tutorial, please read the blog post [Hosting on GitL 1. A project 1. A configuration file (`.gitlab-ci.yml`) to deploy your site -1. A specific `job` called `pages` in the configuration file that will make GitLab aware that you are deploying a GitLab Pages website +1. A specific `job` called `pages` in the configuration file +that will make GitLab aware that you are deploying a GitLab Pages website #### Optional Features 1. A custom domain or subdomain 1. A DNS pointing your (sub)domain to your Pages site - 1. **Optional**: an SSL/TLS certificate so your custom domain is accessible under HTTPS. + 1. **Optional**: an SSL/TLS certificate so your custom + domain is accessible under HTTPS. ### Project -Your GitLab Pages project is a regular project created the same way you do for the other ones. To get started with GitLab Pages, you have two ways: +Your GitLab Pages project is a regular project created the +same way you do for the other ones. To get started with GitLab Pages, you have two ways: - Fork one of the templates from Page Examples, or - Create a new project from scratch @@ -39,9 +44,12 @@ Let's go over both options. #### Fork a Project to Get Started From -To make things easy for you, we've created this [group](https://gitlab.com/pages) of default projects containing the most popular SSGs templates. +To make things easy for you, we've created this +[group](https://gitlab.com/pages) of default projects +containing the most popular SSGs templates. -Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've created for the steps below. +Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've +created for the steps below. 1. Choose your SSG template 1. Fork a project from the [Pages group](https://gitlab.com/pages) @@ -62,45 +70,82 @@ To turn a **project website** forked from the Pages group into a **user/group** > >1. Why do I need to remove the fork relationship? > -> Unless you want to contribute to the original project, you won't need it connected to the upstream. A [fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork) is useful for submitting merge requests to the upstream. +> Unless you want to contribute to the original project, +you won't need it connected to the upstream. A +[fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork) +is useful for submitting merge requests to the upstream. > > 2. Why do I need to enable Shared Runners? > -> Shared Runners will run the script set by your GitLab CI configuration file. They're enabled by default to new projects, but not to forks. +> Shared Runners will run the script set by your GitLab CI +configuration file. They're enabled by default to new projects, +but not to forks. #### Create a Project from Scratch -1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the [pratical examples](getting_started_part_one.md#practical-examples). -1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. +1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, +click **New project**, and name it considering the +[pratical examples](getting_started_part_one.md#practical-examples). +1. Clone it to your local computer, add your website +files to your project, add, commit and push to GitLab. 1. From the your **Project**'s page, click **Set up CI**: ![setup GitLab CI](img/setup_ci.png) -1. Choose one of the templates from the dropbox menu. Pick up the template corresponding to the SSG you're using (or plain HTML). +1. Choose one of the templates from the dropbox menu. +Pick up the template corresponding to the SSG you're using (or plain HTML). ![gitlab-ci templates](img/choose_ci_template.png) -Once you have both site files and `.gitlab-ci.yml` in your project's root, GitLab CI will build your site and deploy it with Pages. Once the first build passes, you see your site is live by navigating to your **Project**'s **Settings** > **Pages**, where you'll find its default URL. +Once you have both site files and `.gitlab-ci.yml` in your project's +root, GitLab CI will build your site and deploy it with Pages. +Once the first build passes, you see your site is live by +navigating to your **Project**'s **Settings** > **Pages**, +where you'll find its default URL. > **Notes:** > -> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) to our examples. +> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, +if you don't find yours among the templates, you'll need +to configure your own `.gitlab-ci.yml`. Do do that, please +read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md). New SSGs are very welcome among +the [example projects](https://gitlab.com/pages). If you set +up a new one, please +[contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) +to our examples. > -> - The second step _"Clone it to your local computer"_, can be done differently, achieving the same results: instead of cloning the bare repository to you local computer and moving your site files into it, you can run `git init` in your local website directory, add the remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, then add, commit, and push. +> - The second step _"Clone it to your local computer"_, can be done +differently, achieving the same results: instead of cloning the bare +repository to you local computer and moving your site files into it, +you can run `git init` in your local website directory, add the +remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, +then add, commit, and push. ### URLs and Baseurls -Every Static Site Generator (SSG) default configuration expects to find your website under a (sub)domain (`example.com`), not in a subdirectory of that domain (`example.com/subdir`). Therefore, whenever you publish a project website (`namespace.gitlab.io/project-name`), you'll have to look for this configuration (base URL) on your SSG's documentation and set it up to reflect this pattern. +Every Static Site Generator (SSG) default configuration expects +to find your website under a (sub)domain (`example.com`), not +in a subdirectory of that domain (`example.com/subdir`). Therefore, +whenever you publish a project website (`namespace.gitlab.io/project-name`), +you'll have to look for this configuration (base URL) on your SSG's +documentation and set it up to reflect this pattern. -For example, for a Jekyll site, the `baseurl` is defined in the Jekyll configuration file, `_config.yml`. If your website URL is `https://john.gitlab.io/blog/`, you need to add this line to `_config.yml`: +For example, for a Jekyll site, the `baseurl` is defined in the Jekyll +configuration file, `_config.yml`. If your website URL is +`https://john.gitlab.io/blog/`, you need to add this line to `_config.yml`: ```yaml baseurl: "/blog" ``` -On the contrary, if you deploy your website after forking one of our [default examples](https://gitlab.com/pages), the baseurl will already be configured this way, as all examples there are project websites. If you decide to make yours a user or group website, you'll have to remove this configuration from your project. For the Jekyll example we've just mentioned, you'd have to change Jekyll's `_config.yml` to: +On the contrary, if you deploy your website after forking one of +our [default examples](https://gitlab.com/pages), the baseurl will +already be configured this way, as all examples there are project +websites. If you decide to make yours a user or group website, you'll +have to remove this configuration from your project. For the Jekyll +example we've just mentioned, you'd have to change Jekyll's `_config.yml` to: ```yaml baseurl: "" From 6ec9e318dc002303a02e07f972d01536bc1e884f Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:32:44 -0300 Subject: [PATCH 117/129] wrapping text - part 3 --- doc/pages/getting_started_part_three.md | 162 ++++++++++++++++++++---- 1 file changed, 135 insertions(+), 27 deletions(-) diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md index 10ae3abb9e5..8acf8a85d5a 100644 --- a/doc/pages/getting_started_part_three.md +++ b/doc/pages/getting_started_part_three.md @@ -12,15 +12,38 @@ ### Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages -[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves numerous purposes, to build, test, and deploy your app from GitLab through [Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) methods. You will need it to build your website with GitLab Pages, and deploy it to the Pages server. +[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves +numerous purposes, to build, test, and deploy your app +from GitLab through +[Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) +methods. You will need it to build your website with GitLab Pages, +and deploy it to the Pages server. -What this file actually does is telling the [GitLab Runner](https://docs.gitlab.com/runner/) to run scripts as you would do from the command line. The Runner acts as your terminal. GitLab CI tells the Runner which commands to run. Both are built-in in GitLab, and you don't need to set up anything for them to work. +What this file actually does is telling the +[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts +as you would do from the command line. The Runner acts as your +terminal. GitLab CI tells the Runner which commands to run. +Both are built-in in GitLab, and you don't need to set up +anything for them to work. -Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) and GitLab Runner is out of the scope of this guide, but we'll need to understand just a few things to be able to write our own `.gitlab-ci.yml` or tweak an existing one. It's an [Yaml](http://docs.ansible.com/ansible/YAMLSyntax.html) file, with its own syntax. You can always check your CI syntax with the [GitLab CI Lint Tool](https://gitlab.com/ci/lint). +Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) +and GitLab Runner is out of the scope of this guide, but we'll +need to understand just a few things to be able to write our own +`.gitlab-ci.yml` or tweak an existing one. It's an +[Yaml](http://docs.ansible.com/ansible/YAMLSyntax.html) file, +with its own syntax. You can always check your CI syntax with +the [GitLab CI Lint Tool](https://gitlab.com/ci/lint). **Practical Example:** -Let's consider you have a [Jekyll](https://jekyllrb.com/) site. To build it locally, you would open your terminal, and run `jekyll build`. Of course, before building it, you had to install Jekyll in your computer. For that, you had to open your terminal and run `gem install jekyll`. Right? GitLab CI + GitLab Runner do the same thing. But you need to write in the `.gitlab-ci.yml` the script you want to run so the GitLab Runner will do it for you. It looks more complicated then it is. What you need to tell the Runner: +Let's consider you have a [Jekyll](https://jekyllrb.com/) site. +To build it locally, you would open your terminal, and run `jekyll build`. +Of course, before building it, you had to install Jekyll in your computer. +For that, you had to open your terminal and run `gem install jekyll`. +Right? GitLab CI + GitLab Runner do the same thing. But you need to +write in the `.gitlab-ci.yml` the script you want to run so +GitLab Runner will do it for you. It looks more complicated then it +is. What you need to tell the Runner: ``` $ gem install jekyll @@ -39,7 +62,9 @@ script: #### Job -So far so good. Now, each `script`, in GitLab is organized by a `job`, which is a bunch of scripts and settings you want to apply to that specific task. +So far so good. Now, each `script`, in GitLab is organized by +a `job`, which is a bunch of scripts and settings you want to +apply to that specific task. ```yaml job: @@ -48,7 +73,9 @@ job: - jekyll build ``` -For GitLab Pages, this `job` has a specific name, called `pages`, which tells the Runner you want that task to deploy your website with GitLab Pages: +For GitLab Pages, this `job` has a specific name, called `pages`, +which tells the Runner you want that task to deploy your website +with GitLab Pages: ```yaml pages: @@ -59,7 +86,12 @@ pages: #### `public` Dir -We also need to tell Jekyll where do you want the website to build, and GitLab Pages will only consider files in a directory called `public`. To do that with Jekyll, we need to add a flag specifying the [destination (`-d`)](https://jekyllrb.com/docs/usage/) of the built website: `jekyll build -d public`. Of course, we need to tell this to our Runner: +We also need to tell Jekyll where do you want the website to build, +and GitLab Pages will only consider files in a directory called `public`. +To do that with Jekyll, we need to add a flag specifying the +[destination (`-d`)](https://jekyllrb.com/docs/usage/) of the +built website: `jekyll build -d public`. Of course, we need +to tell this to our Runner: ```yaml pages: @@ -70,7 +102,9 @@ pages: #### Artifacts -We also need to tell the Runner that this _job_ generates _artifacts_, which is the site built by Jekyll. Where are these artifacts stored? In the `public` directory: +We also need to tell the Runner that this _job_ generates +_artifacts_, which is the site built by Jekyll. +Where are these artifacts stored? In the `public` directory: ```yaml pages: @@ -82,7 +116,12 @@ pages: - public ``` -The script above would be enough to build your Jekyll site with GitLab Pages. But, from Jekyll 3.4.0 on, its default template originated by `jekyll new project` requires [Bundler](http://bundler.io/) to install Jekyll dependencies and the default theme. To adjust our script to meet these new requirements, we only need to install and build Jekyll with Bundler: +The script above would be enough to build your Jekyll +site with GitLab Pages. But, from Jekyll 3.4.0 on, its default +template originated by `jekyll new project` requires +[Bundler](http://bundler.io/) to install Jekyll dependencies +and the default theme. To adjust our script to meet these new +requirements, we only need to install and build Jekyll with Bundler: ```yaml pages: @@ -94,11 +133,18 @@ pages: - public ``` -That's it! A `.gitlab-ci.yml` with the content above would deploy your Jekyll 3.4.0 site with GitLab Pages. This is the minimum configuration for our example. On the steps below, we'll refine the script by adding extra options to our GitLab CI. +That's it! A `.gitlab-ci.yml` with the content above would deploy +your Jekyll 3.4.0 site with GitLab Pages. This is the minimum +configuration for our example. On the steps below, we'll refine +the script by adding extra options to our GitLab CI. #### Image -At this point, you probably ask yourself: "okay, but to install Jekyll I need Ruby. Where is Ruby on that script?". The answer is simple: the first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a [Docker](https://www.docker.com/) image specifying what do you need in your container to run that script: +At this point, you probably ask yourself: "okay, but to install Jekyll +I need Ruby. Where is Ruby on that script?". The answer is simple: the +first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a +[Docker](https://www.docker.com/) image specifying what do you need in +your container to run that script: ```yaml image: ruby:2.3 @@ -112,17 +158,32 @@ pages: - public ``` -In this case, you're telling the Runner to pull this image, which contains Ruby 2.3 as part of its file system. When you don't specify this image in your configuration, the Runner will use a default image, which is Ruby 2.1. +In this case, you're telling the Runner to pull this image, which +contains Ruby 2.3 as part of its file system. When you don't specify +this image in your configuration, the Runner will use a default +image, which is Ruby 2.1. -If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll need to specify which image you want to use, and this image should contain NodeJS as part of its file system. E.g., for a [Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`. +If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll +need to specify which image you want to use, and this image should +contain NodeJS as part of its file system. E.g., for a +[Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`. -> Note: we're not trying to explain what a Docker image is, we just need to introduce the concept with a minimum viable explanation. To know more about Docker images, please visit their website or take a look at a [summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here. +> Note: we're not trying to explain what a Docker image is, +we just need to introduce the concept with a minimum viable +explanation. To know more about Docker images, please visit +their website or take a look at a +[summarized explanation](http://paislee.io/how-to-automate-docker-deployments/) here. Let's go a little further. #### Branching -If you use GitLab as a version control platform, you will have your branching strategy to work on your project. Meaning, you will have other branches in your project, but you'll want only pushes to the default branch (usually `master`) to be deployed to your website. To do that, we need to add another line to our CI, telling the Runner to only perform that _job_ called `pages` on the `master` branch `only`: +If you use GitLab as a version control platform, you will have your +branching strategy to work on your project. Meaning, you will have +other branches in your project, but you'll want only pushes to the +default branch (usually `master`) to be deployed to your website. +To do that, we need to add another line to our CI, telling the Runner +to only perform that _job_ called `pages` on the `master` branch `only`: ```yaml image: ruby:2.3 @@ -140,7 +201,12 @@ pages: #### Stages -Another interesting concept to keep in mind are build stages. Your web app can pass through a lot of tests and other tasks until it's deployed to staging or production environments. There are three default stages on GitLab CI: build, test, and deploy. To specify which stage your _job_ is running, simply add another line to your CI: +Another interesting concept to keep in mind are build stages. +Your web app can pass through a lot of tests and other tasks +until it's deployed to staging or production environments. +There are three default stages on GitLab CI: build, test, +and deploy. To specify which stage your _job_ is running, +simply add another line to your CI: ```yaml image: ruby:2.3 @@ -157,7 +223,13 @@ pages: - master ``` -You might ask yourself: "why should I bother with stages at all?" Well, let's say you want to be able to test your script and check the built site before deploying your site to production. You want to run the test exactly as your script will do when you push to `master`. It's simple, let's add another task (_job_) to our CI, telling it to test every push to other branches, `except` the `master` branch: +You might ask yourself: "why should I bother with stages +at all?" Well, let's say you want to be able to test your +script and check the built site before deploying your site +to production. You want to run the test exactly as your +script will do when you push to `master`. It's simple, +let's add another task (_job_) to our CI, telling it to +test every push to other branches, `except` the `master` branch: ```yaml image: ruby:2.3 @@ -185,13 +257,29 @@ test: - master ``` -The `test` job is running on the stage `test`, Jekyll will build the site in a directory called `test`, and this job will affect all the branches except `master`. +The `test` job is running on the stage `test`, Jekyll +will build the site in a directory called `test`, and +this job will affect all the branches except `master`. -The best benefit of applying _stages_ to different _jobs_ is that every job in the same stage builds in parallel. So, if your web app needs more than one test before being deployed, you can run all your test at the same time, it's not necessary to wait one test to finish to run the other. Of course, this is just a brief introduction of GitLab CI and GitLab Runner, which are tools much more powerful than that. This is what you need to be able to create and tweak your builds for your GitLab Pages site. +The best benefit of applying _stages_ to different +_jobs_ is that every job in the same stage builds in +parallel. So, if your web app needs more than one test +before being deployed, you can run all your test at the +same time, it's not necessary to wait one test to finish +to run the other. Of course, this is just a brief +introduction of GitLab CI and GitLab Runner, which are +tools much more powerful than that. This is what you +need to be able to create and tweak your builds for +your GitLab Pages site. #### Before Script -To avoid running the same script multiple times across your _jobs_, you can add the parameter `before_script`, in which you specify which commands you want to run for every single _job_. In our example, notice that we run `bundle install` for both jobs, `pages` and `test`. We don't need to repeat it: +To avoid running the same script multiple times across +your _jobs_, you can add the parameter `before_script`, +in which you specify which commands you want to run for +every single _job_. In our example, notice that we run +`bundle install` for both jobs, `pages` and `test`. +We don't need to repeat it: ```yaml image: ruby:2.3 @@ -222,7 +310,11 @@ test: #### Caching Dependencies -If you want to cache the installation files for your projects dependencies, for building faster, you can use the parameter `cache`. For this example, we'll cache Jekyll dependencies in a `vendor` directory when we run `bundle install`: +If you want to cache the installation files for your +projects dependencies, for building faster, you can +use the parameter `cache`. For this example, we'll +cache Jekyll dependencies in a `vendor` directory +when we run `bundle install`: ```yaml image: ruby:2.3 @@ -255,22 +347,38 @@ test: - master ``` -For this specific case, we need to exclude `/vendor` from Jekyll `_config.yml` file, otherwise Jekyll will understand it as a regular directory to build together with the site: +For this specific case, we need to exclude `/vendor` +from Jekyll `_config.yml` file, otherwise Jekyll will +understand it as a regular directory to build +together with the site: ```yml exclude: - vendor ``` -There we go! Now our GitLab CI not only builds our website, but also **continuously test** pushes to feature-branches, **caches** dependencies installed with Bundler, and **continuously deploy** every push to the `master` branch. +There we go! Now our GitLab CI not only builds our website, +but also **continuously test** pushes to feature-branches, +**caches** dependencies installed with Bundler, and +**continuously deploy** every push to the `master` branch. ### Advanced GitLab CI for GitLab Pages -What you can do with GitLab CI is pretty much up to your creativity. Once you get used to it, you start creating awesome scripts that automate most of tasks you'd do manually in the past. Read through the [documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) to understand how to go even further on your scripts. +What you can do with GitLab CI is pretty much up to your +creativity. Once you get used to it, you start creating +awesome scripts that automate most of tasks you'd do +manually in the past. Read through the +[documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html) +to understand how to go even further on your scripts. -- On this blog post, understand the concept of [using GitLab CI `environments` to deploy your web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/). -- On this post, learn [how to run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) -- On this blog post, we go through the process of [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) to deploy this website you're looking at, docs.gitlab.com. +- On this blog post, understand the concept of +[using GitLab CI `environments` to deploy your +web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/). +- On this post, learn [how to run jobs sequentially, +in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/) +- On this blog post, we go through the process of +[pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) +to deploy this website you're looking at, docs.gitlab.com. - On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/). ||| From 1a802b82d6c5c8a0fca20ab3eeb526416ef076a3 Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:37:15 -0300 Subject: [PATCH 118/129] remove <> --- doc/pages/getting_started_part_one.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md index 57fc5d21b96..ed39301df39 100644 --- a/doc/pages/getting_started_part_one.md +++ b/doc/pages/getting_started_part_one.md @@ -47,8 +47,8 @@ which is highly recommendable and much faster than hardcoding. If you set up a GitLab Pages project on GitLab.com, it will automatically be accessible under a -[subdomain of `.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). -The `` is defined by your username on GitLab.com, +[subdomain of `namespace.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). +The `namespace` is defined by your username on GitLab.com, or the group name you created this project under. > Note: If you use your own GitLab instance to deploy your From 324260cef9ac8282bfb5b729b510f47e64b680fa Mon Sep 17 00:00:00 2001 From: Marcia Ramos Date: Wed, 22 Feb 2017 13:39:57 -0300 Subject: [PATCH 119/129] remove html comments --- doc/pages/getting_started_part_two.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md index 15f628ac1fb..94b9d11e5d5 100644 --- a/doc/pages/getting_started_part_two.md +++ b/doc/pages/getting_started_part_two.md @@ -16,8 +16,6 @@ For a complete step-by-step tutorial, please read the blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain what do you need and why do you need them. - - ### What You Need to Get Started 1. A project @@ -123,8 +121,6 @@ then add, commit, and push. ### URLs and Baseurls - - Every Static Site Generator (SSG) default configuration expects to find your website under a (sub)domain (`example.com`), not in a subdirectory of that domain (`example.com/subdir`). Therefore, From 969754c25ad678081dca3359fde4080022bccaec Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 18:44:31 +0100 Subject: [PATCH 120/129] Reorder main index items in Pages overview [ci skip] --- doc/pages/getting_started_part_one.md | 20 ++++++++-------- doc/pages/getting_started_part_three.md | 31 +++++++++++-------------- doc/pages/getting_started_part_two.md | 14 +++++------ doc/pages/index.md | 20 ++++++++++++---- 4 files changed, 46 insertions(+), 39 deletions(-) diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md index ed39301df39..c5b1aa4b654 100644 --- a/doc/pages/getting_started_part_one.md +++ b/doc/pages/getting_started_part_one.md @@ -1,9 +1,5 @@ # GitLab Pages from A to Z: Part 1 -> Type: user guide -> -> Level: beginner - - **Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates** - _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ - _[Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md)_ @@ -19,14 +15,15 @@ and GitLab EE (Enterprise Edition), please read the [admin documentation](https://docs.gitlab.com/ce/administration/pages/index.html), and/or watch this [video tutorial](https://youtu.be/dD8c7WNcc6s). -> For this guide, we assume you already have GitLab Pages +>**Note:** +For this guide, we assume you already have GitLab Pages server up and running for your GitLab instance. ## What you need to know before getting started Before we begin, let's understand a few concepts first. -### Static Sites +### Static sites GitLab Pages only supports static websites, meaning, your output files must be HTML, CSS, and JavaScript only. @@ -43,7 +40,7 @@ which is highly recommendable and much faster than hardcoding. - You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) - Fork an [example project](https://gitlab.com/pages) to build your website based upon -### GitLab Pages Domain +### GitLab Pages domain If you set up a GitLab Pages project on GitLab.com, it will automatically be accessible under a @@ -51,7 +48,8 @@ it will automatically be accessible under a The `namespace` is defined by your username on GitLab.com, or the group name you created this project under. -> Note: If you use your own GitLab instance to deploy your +>**Note:** +If you use your own GitLab instance to deploy your site with GitLab Pages, check with your sysadmin what's your Pages wildcard domain. This guide is valid for any GitLab instance, you just need to replace Pages wildcard domain on GitLab.com @@ -180,7 +178,8 @@ up your Pages project with your custom (sub)domain, if you want it secured by HTTPS, you will have to issue a certificate for that (sub)domain and install it on your project. -> Note: certificates are NOT required to add to your custom +>**Note:** +Certificates are NOT required to add to your custom (sub)domain on your GitLab Pages project, though they are highly recommendable. @@ -257,7 +256,8 @@ and paste it in the [same field as your PEM certificate](https://about.gitlab.co just jumping a line between them. - Copy your public key and paste it in the last field -> Note: **do not** open certificates or encryption keys in +>**Note:** +**Do not** open certificates or encryption keys in regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md index 8acf8a85d5a..ef47abef3a0 100644 --- a/doc/pages/getting_started_part_three.md +++ b/doc/pages/getting_started_part_three.md @@ -1,16 +1,12 @@ # GitLab Pages from A to Z: Part 3 -> Type: user guide -> -> Level: intermediate - - _[Part 1: Static Sites, Domains, DNS Records, and SSL/TLS Certificates](getting_started_part_one.md)_ - _[Part 2: Quick Start Guide - Setting Up GitLab Pages](getting_started_part_two.md)_ - **Part 3: Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages** --- -### Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages +## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages [GitLab CI](https://about.gitlab.com/gitlab-ci/) serves numerous purposes, to build, test, and deploy your app @@ -50,7 +46,7 @@ $ gem install jekyll $ jekyll build ``` -#### Script +### Script To transpose this script to Yaml, it would be like this: @@ -60,7 +56,7 @@ script: - jekyll build ``` -#### Job +### Job So far so good. Now, each `script`, in GitLab is organized by a `job`, which is a bunch of scripts and settings you want to @@ -84,7 +80,7 @@ pages: - jekyll build ``` -#### `public` Dir +### The `public` directory We also need to tell Jekyll where do you want the website to build, and GitLab Pages will only consider files in a directory called `public`. @@ -100,7 +96,7 @@ pages: - jekyll build -d public ``` -#### Artifacts +### Artifacts We also need to tell the Runner that this _job_ generates _artifacts_, which is the site built by Jekyll. @@ -138,7 +134,7 @@ your Jekyll 3.4.0 site with GitLab Pages. This is the minimum configuration for our example. On the steps below, we'll refine the script by adding extra options to our GitLab CI. -#### Image +### Image At this point, you probably ask yourself: "okay, but to install Jekyll I need Ruby. Where is Ruby on that script?". The answer is simple: the @@ -168,7 +164,8 @@ need to specify which image you want to use, and this image should contain NodeJS as part of its file system. E.g., for a [Hexo](https://gitlab.com/pages/hexo) site, you can use `image: node:4.2.2`. -> Note: we're not trying to explain what a Docker image is, +>**Note:** +We're not trying to explain what a Docker image is, we just need to introduce the concept with a minimum viable explanation. To know more about Docker images, please visit their website or take a look at a @@ -176,7 +173,7 @@ their website or take a look at a Let's go a little further. -#### Branching +### Branching If you use GitLab as a version control platform, you will have your branching strategy to work on your project. Meaning, you will have @@ -199,7 +196,7 @@ pages: - master ``` -#### Stages +### Stages Another interesting concept to keep in mind are build stages. Your web app can pass through a lot of tests and other tasks @@ -272,7 +269,7 @@ tools much more powerful than that. This is what you need to be able to create and tweak your builds for your GitLab Pages site. -#### Before Script +### Before Script To avoid running the same script multiple times across your _jobs_, you can add the parameter `before_script`, @@ -308,7 +305,7 @@ test: - master ``` -#### Caching Dependencies +### Caching Dependencies If you want to cache the installation files for your projects dependencies, for building faster, you can @@ -362,7 +359,7 @@ but also **continuously test** pushes to feature-branches, **caches** dependencies installed with Bundler, and **continuously deploy** every push to the `master` branch. -### Advanced GitLab CI for GitLab Pages +## Advanced GitLab CI for GitLab Pages What you can do with GitLab CI is pretty much up to your creativity. Once you get used to it, you start creating @@ -383,4 +380,4 @@ to deploy this website you're looking at, docs.gitlab.com. ||| |:--|--:| -|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)| +|[**← Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)|| diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md index 94b9d11e5d5..07dd24122c4 100644 --- a/doc/pages/getting_started_part_two.md +++ b/doc/pages/getting_started_part_two.md @@ -10,27 +10,27 @@ ---- -## Setting Up GitLab Pages +## Setting up GitLab Pages For a complete step-by-step tutorial, please read the blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain what do you need and why do you need them. -### What You Need to Get Started +## What you need to get started 1. A project 1. A configuration file (`.gitlab-ci.yml`) to deploy your site 1. A specific `job` called `pages` in the configuration file that will make GitLab aware that you are deploying a GitLab Pages website -#### Optional Features +Optional Features: 1. A custom domain or subdomain 1. A DNS pointing your (sub)domain to your Pages site 1. **Optional**: an SSL/TLS certificate so your custom domain is accessible under HTTPS. -### Project +## Project Your GitLab Pages project is a regular project created the same way you do for the other ones. To get started with GitLab Pages, you have two ways: @@ -40,7 +40,7 @@ same way you do for the other ones. To get started with GitLab Pages, you have t Let's go over both options. -#### Fork a Project to Get Started From +### Fork a project to get started from To make things easy for you, we've created this [group](https://gitlab.com/pages) of default projects @@ -79,11 +79,11 @@ is useful for submitting merge requests to the upstream. configuration file. They're enabled by default to new projects, but not to forks. -#### Create a Project from Scratch +### Create a project from scratch 1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**, click **New project**, and name it considering the -[pratical examples](getting_started_part_one.md#practical-examples). +[practical examples](getting_started_part_one.md#practical-examples). 1. Clone it to your local computer, add your website files to your project, add, commit and push to GitLab. 1. From the your **Project**'s page, click **Set up CI**: diff --git a/doc/pages/index.md b/doc/pages/index.md index 242fdf3147f..a6f928cc243 100644 --- a/doc/pages/index.md +++ b/doc/pages/index.md @@ -1,6 +1,13 @@ # All you need to know about GitLab Pages -## Product +With GitLab Pages you can create static websites for your GitLab projects, +groups, or user accounts. You can use any static website generator: Jekyll, +Middleman, Hexo, Hugo, Pelican, you name it! Connect as many customs domains +as you like and bring your own TLS certificate to secure them. + +Here's some info we have gathered to get you started. + +## General info - [Product webpage](https://pages.gitlab.io) - [We're bringing GitLab Pages to CE](https://about.gitlab.com/2016/12/24/were-bringing-gitlab-pages-to-community-edition/) @@ -8,12 +15,11 @@ ## Getting started -- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) a comprehensive step-by-step guide - GitLab Pages from A to Z - [Part 1: Static sites, domains, DNS records, and SSL/TLS certificates](getting_started_part_one.md) - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) - - Video tutorial: [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) - [Part 3: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_three.md) +- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) a comprehensive step-by-step guide - Secure GitLab Pages custom domain with SSL/TLS certificates - [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) - [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) @@ -24,6 +30,11 @@ - [SSGs part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) - [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) +## Video tutorials + +- [How to publish a website with GitLab Pages on GitLab.com: from a forked project](https://youtu.be/TWqh9MtT4Bg) +- [How to Enable GitLab Pages for GitLab CE and EE](https://youtu.be/dD8c7WNcc6s) + ## Advanced use - Blog Posts: @@ -32,8 +43,7 @@ - [Building a new GitLab docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) - [Publish code coverage reports with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) -## General documentation +## Specific documentation - [User docs](../user/project/pages/index.md) - [Admin docs](../administration/pages/index.md) - - Video tutorial - [How to Enable GitLab Pages for GitLab CE and EE](https://youtu.be/dD8c7WNcc6s) From 9f322f2b3866833af1bca7ecd3624428d968b017 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 21 Feb 2017 22:48:02 +0000 Subject: [PATCH 121/129] Added double newline after file upload markdown insert --- app/assets/javascripts/dropzone_input.js | 5 +++-- spec/features/issues_spec.rb | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 64a7a9eaf37..646f836aff0 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -126,13 +126,14 @@ require('./preview_markdown'); }; pasteText = function(text) { var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; + var formattedText = text + "\n\n"; caretStart = $(child)[0].selectionStart; caretEnd = $(child)[0].selectionEnd; textEnd = $(child).val().length; beforeSelection = $(child).val().substring(0, caretStart); afterSelection = $(child).val().substring(caretEnd, textEnd); - $(child).val(beforeSelection + text + afterSelection); - child.get(0).setSelectionRange(caretStart + text.length, caretEnd + text.length); + $(child).val(beforeSelection + formattedText + afterSelection); + child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); return form_textarea.trigger("input"); }; getFilename = function(e) { diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 094f645a077..ed3826bd46e 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -577,6 +577,15 @@ describe 'Issues', feature: true do expect(page.find_field("issue_description").value).to have_content 'banana_sample' end + + it 'adds double newline to end of attachment markdown' do + drop_in_dropzone test_image_file + + # Wait for the file to upload + sleep 1 + + expect(page.find_field("issue_description").value).to match /\n\n$/ + end end end From 165be6344de51e4db27456f8977da5105f9d33d5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 19:13:48 +0100 Subject: [PATCH 122/129] Add missing index.md to Pages docs [ci skip] --- doc/user/project/pages/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index 816600964de..276fbd26835 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -444,6 +444,6 @@ For a list of known issues, visit GitLab's [public issue tracker]. [public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Pages [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 [quick start guide]: ../../../ci/quick_start/README.md -[pages-index-guide]: ../../../pages/ +[pages-index-guide]: ../../../pages/index.md [pages-quick]: ../../../pages/getting_started_part_one.md [video-pages-fork]: https://youtu.be/TWqh9MtT4Bg From e2af9d0ab0fb2a5d631c0bbd4425d83a6d1203dc Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 20:14:10 +0100 Subject: [PATCH 123/129] Change Pages redirect [ci skip] --- doc/pages/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/pages/README.md b/doc/pages/README.md index c9715eed598..afe62a2ee71 100644 --- a/doc/pages/README.md +++ b/doc/pages/README.md @@ -1 +1 @@ -This document was moved to [user/project/pages](../user/project/pages/index.md). +This document was moved to [index.md](index.md). From 17aae3bd34a467e2aed4c7063bc8bc3619d87e0f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 22 Feb 2017 20:35:44 +0100 Subject: [PATCH 124/129] Remove Pages readme To prevent a redirect loop in docs.gitlab.com/ce/pages/ [ci skip] --- doc/pages/README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 doc/pages/README.md diff --git a/doc/pages/README.md b/doc/pages/README.md deleted file mode 100644 index afe62a2ee71..00000000000 --- a/doc/pages/README.md +++ /dev/null @@ -1 +0,0 @@ -This document was moved to [index.md](index.md). From a942240ef5c5bef7e0d1fcf5294209a3a6b69bdf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 22 Feb 2017 20:42:33 +0000 Subject: [PATCH 125/129] Ignore two Rails CVEs in bundler:audit job --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 433b3119fba..20f410d0b4c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -281,7 +281,7 @@ bundler:audit: - master@gitlab/gitlabhq - master@gitlab/gitlab-ee script: - - "bundle exec bundle-audit check --update --ignore OSVDB-115941" + - "bundle exec bundle-audit check --update --ignore OSVDB-115941 CVE-2016-6316 CVE-2016-6317" migration paths: stage: test From e6c58aff0f2915e2e17879141924efc7b386fe0d Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 22 Feb 2017 16:56:52 -0600 Subject: [PATCH 126/129] remove require.context from filtered_search_bundle --- .../filtered_search/filtered_search_bundle.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js index 392f1835966..faaba994f46 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js +++ b/app/assets/javascripts/filtered_search/filtered_search_bundle.js @@ -1,3 +1,9 @@ -function requireAll(context) { return context.keys().map(context); } - -requireAll(require.context('./', true, /^\.\/(?!filtered_search_bundle).*\.(js|es6)$/)); +require('./dropdown_hint'); +require('./dropdown_non_user'); +require('./dropdown_user'); +require('./dropdown_utils'); +require('./filtered_search_dropdown_manager'); +require('./filtered_search_dropdown'); +require('./filtered_search_manager'); +require('./filtered_search_token_keys'); +require('./filtered_search_tokenizer'); From 87b19b8cae067bd328c1e6a1cec4fa06a73e8b96 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 22 Feb 2017 17:01:41 -0600 Subject: [PATCH 127/129] remove require.context from graphs_bundle --- app/assets/javascripts/graphs/graphs_bundle.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index 4f7777aa5bc..086dcb34571 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,3 +1,4 @@ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!graphs_bundle).*\.(js|es6)$/)); +require('./stat_graph_contributors_graph'); +require('./stat_graph_contributors_util'); +require('./stat_graph_contributors'); +require('./stat_graph'); From f326e6fb8d806ba443c64965a564d64a2f313a19 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 22 Feb 2017 17:04:53 -0600 Subject: [PATCH 128/129] remove require.context from network_bundle --- app/assets/javascripts/network/network_bundle.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js index aae509caa79..e5947586583 100644 --- a/app/assets/javascripts/network/network_bundle.js +++ b/app/assets/javascripts/network/network_bundle.js @@ -2,9 +2,8 @@ /* global Network */ /* global ShortcutsNetwork */ -// require everything else in this directory -function requireAll(context) { return context.keys().map(context); } -requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/)); +require('./branch_graph'); +require('./network'); (function() { $(function() { From 5600b17de76c8bc93313adc1aa4d9859045de0a9 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 22 Feb 2017 17:42:11 -0500 Subject: [PATCH 129/129] Code style improvements --- app/views/groups/_head.html.haml | 19 ++++++++++++++++++ app/views/groups/_head_issues.html.haml | 19 ++++++++++++++++++ app/views/groups/activity.html.haml | 3 ++- .../groups/group_members/index.html.haml | 1 + app/views/groups/issues.html.haml | 1 + app/views/groups/labels/index.html.haml | 1 + app/views/groups/milestones/index.html.haml | 1 + app/views/groups/show.html.haml | 1 + app/views/layouts/nav/_group.html.haml | 20 ++----------------- .../26348-cleanup-navigation-order-groups.yml | 4 ++++ features/steps/group/milestones.rb | 4 +--- 11 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 app/views/groups/_head.html.haml create mode 100644 app/views/groups/_head_issues.html.haml create mode 100644 changelogs/unreleased/26348-cleanup-navigation-order-groups.yml diff --git a/app/views/groups/_head.html.haml b/app/views/groups/_head.html.haml new file mode 100644 index 00000000000..6b296ea8dea --- /dev/null +++ b/app/views/groups/_head.html.haml @@ -0,0 +1,19 @@ += content_for :sub_nav do + .scrolling-tabs-container.sub-nav-scroll + = render 'shared/nav_scroll' + .nav-links.sub-nav.scrolling-tabs + %ul{ class: container_class } + = nav_link(path: 'groups#show', html_options: { class: 'home' }) do + = link_to group_path(@group), title: 'Group Home' do + %span + Home + + = nav_link(path: 'groups#activity') do + = link_to activity_group_path(@group), title: 'Activity' do + %span + Activity + + = nav_link(path: 'group_members#index') do + = link_to group_group_members_path(@group), title: 'Members' do + %span + Members diff --git a/app/views/groups/_head_issues.html.haml b/app/views/groups/_head_issues.html.haml new file mode 100644 index 00000000000..d554bc23743 --- /dev/null +++ b/app/views/groups/_head_issues.html.haml @@ -0,0 +1,19 @@ += content_for :sub_nav do + .scrolling-tabs-container.sub-nav-scroll + = render 'shared/nav_scroll' + .nav-links.sub-nav.scrolling-tabs + %ul{ class: container_class } + = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do + = link_to issues_group_path(@group), title: 'List' do + %span + List + + = nav_link(path: 'labels#index') do + = link_to group_labels_path(@group), title: 'Labels' do + %span + Labels + + = nav_link(path: 'milestones#index') do + = link_to group_milestones_path(@group), title: 'Milestones' do + %span + Milestones diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml index aaad265b3ee..d7375b23524 100644 --- a/app/views/groups/activity.html.haml +++ b/app/views/groups/activity.html.haml @@ -2,7 +2,8 @@ - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") -- page_title "Activity" +- page_title "Activity" += render 'groups/head' %section.activities = render 'activities' diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 2e4e4511bb6..8cb56443191 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,4 +1,5 @@ - page_title "Members" += render 'groups/head' .project-members-page.prepend-top-default %h4 diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 83edb719692..939bddf3fe9 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,4 +1,5 @@ - page_title "Issues" += render "head_issues" = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues") diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 45325d6bc4b..2bc00fb16c8 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,4 +1,5 @@ - page_title 'Labels' += render "groups/head_issues" .top-area.adjust .nav-text diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index cd5388fe402..644895c56a1 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,4 +1,5 @@ - page_title "Milestones" += render "groups/head_issues" .top-area = render 'shared/milestones_filter' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index b040f404ac4..3d7b469660a 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -4,6 +4,7 @@ - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") += render 'groups/head' = render 'groups/home_panel' diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index f3539fd372d..e0742d70fac 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -5,23 +5,11 @@ .fade-right = icon('angle-right') %ul.nav-links.scrolling-tabs - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do + = nav_link(path: ['groups#show', 'groups#activity', 'group_members#index'], html_options: { class: 'home' }) do = link_to group_path(@group), title: 'Home' do %span Group - = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do - %span - Activity - = nav_link(controller: [:group, :labels]) do - = link_to group_labels_path(@group), title: 'Labels' do - %span - Labels - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones' do - %span - Milestones - = nav_link(path: 'groups#issues') do + = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do = link_to issues_group_path(@group), title: 'Issues' do %span Issues @@ -33,7 +21,3 @@ Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute %span.badge.count= number_with_delimiter(merge_requests.count) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - %span - Members diff --git a/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml b/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml new file mode 100644 index 00000000000..ce888baa32f --- /dev/null +++ b/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml @@ -0,0 +1,4 @@ +--- +title: Clean-up Groups navigation order +merge_request: 9309 +author: diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 70e23098dde..20204ad8654 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -5,9 +5,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps include SharedUser step 'I click on group milestones' do - page.within('.layout-nav') do - click_link 'Milestones' - end + visit group_milestones_path('owned') end step 'I should see group milestones index page has no milestones' do