Commit b4028d45 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 05f1d5d9
...@@ -28,6 +28,11 @@ export default { ...@@ -28,6 +28,11 @@ export default {
required: false, required: false,
default: 7, default: 7,
}, },
showParticipantLabel: {
type: Boolean,
required: false,
default: true,
},
}, },
data() { data() {
return { return {
...@@ -80,6 +85,7 @@ export default { ...@@ -80,6 +85,7 @@ export default {
<template> <template>
<div> <div>
<div <div
v-if="showParticipantLabel"
v-tooltip v-tooltip
:title="participantLabel" :title="participantLabel"
class="sidebar-collapsed-icon" class="sidebar-collapsed-icon"
...@@ -92,7 +98,7 @@ export default { ...@@ -92,7 +98,7 @@ export default {
<gl-loading-icon v-if="loading" class="js-participants-collapsed-loading-icon" /> <gl-loading-icon v-if="loading" class="js-participants-collapsed-loading-icon" />
<span v-else class="js-participants-collapsed-count"> {{ participantCount }} </span> <span v-else class="js-participants-collapsed-count"> {{ participantCount }} </span>
</div> </div>
<div class="title hide-collapsed"> <div v-if="showParticipantLabel" class="title hide-collapsed">
<gl-loading-icon <gl-loading-icon
v-if="loading" v-if="loading"
:inline="true" :inline="true"
......
...@@ -28,7 +28,8 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -28,7 +28,8 @@ class Projects::TreeController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
lfs_blob_ids lfs_blob_ids if Feature.disabled?(:vue_file_list, @project)
@last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit @last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit
end end
end end
......
...@@ -21,8 +21,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -21,8 +21,7 @@ class ProjectsController < Projects::ApplicationController
before_action :assign_ref_vars, if: -> { action_name == 'show' && repo_exists? } before_action :assign_ref_vars, if: -> { action_name == 'show' && repo_exists? }
before_action :tree, before_action :tree,
if: -> { action_name == 'show' && repo_exists? && project_view_files? } if: -> { action_name == 'show' && repo_exists? && project_view_files? }
before_action :lfs_blob_ids, before_action :lfs_blob_ids, if: :show_blob_ids?, only: :show
if: -> { action_name == 'show' && repo_exists? && project_view_files? }
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export] before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
before_action :present_project, only: [:edit] before_action :present_project, only: [:edit]
before_action :authorize_download_code!, only: [:refs] before_action :authorize_download_code!, only: [:refs]
...@@ -296,6 +295,10 @@ class ProjectsController < Projects::ApplicationController ...@@ -296,6 +295,10 @@ class ProjectsController < Projects::ApplicationController
private private
def show_blob_ids?
repo_exists? && project_view_files? && Feature.disabled?(:vue_file_list, @project)
end
# Render project landing depending of which features are available # Render project landing depending of which features are available
# So if page is not available in the list it renders the next page # So if page is not available in the list it renders the next page
# #
......
...@@ -7,7 +7,7 @@ module HasStatus ...@@ -7,7 +7,7 @@ module HasStatus
BLOCKED_STATUS = %w[manual scheduled].freeze BLOCKED_STATUS = %w[manual scheduled].freeze
AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
ACTIVE_STATUSES = %w[preparing pending running].freeze ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
......
---
title: Extend Design view sidebar with issue link and a list of participants
merge_request: 22103
author:
type: added
---
title: New API endpoint GET /projects/:id/services
merge_request: 21330
author:
type: added
---
title: add background migration for sha256 fingerprints of ssh keys
merge_request: 21579
author: Roger Meier
type: added
---
title: Add migrations for version control snippets
merge_request: 22275
author:
type: added
---
title: Execute Gitaly LFS call once when Vue file enabled
merge_request: 22168
author:
type: performance
---
title: Make jobs with resource group cancellable
merge_request: 22356
author:
type: fixed
# frozen_string_literal: true
class AddRepositoryStorageToSnippets < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default( # rubocop:disable Migration/AddColumnWithDefault
:snippets,
:repository_storage,
:string,
default: 'default',
limit: 255,
allow_null: false
)
end
def down
remove_column(:snippets, :repository_storage)
end
end
# frozen_string_literal: true
class AddStorageVersionToSnippets < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default( # rubocop:disable Migration/AddColumnWithDefault
:snippets,
:storage_version,
:integer,
default: 2,
allow_null: false
)
end
def down
remove_column(:snippets, :storage_version)
end
end
# frozen_string_literal: true
class UpdateFingerprintSha256WithinKeys < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
class Key < ActiveRecord::Base
include EachBatch
self.table_name = 'keys'
self.inheritance_column = :_type_disabled
end
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(Key, 'MigrateFingerprintSha256WithinKeys', 5.minutes)
end
def down
# no-op
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_01_02_170221) do ActiveRecord::Schema.define(version: 2020_01_06_071113) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -3803,6 +3803,8 @@ ActiveRecord::Schema.define(version: 2020_01_02_170221) do ...@@ -3803,6 +3803,8 @@ ActiveRecord::Schema.define(version: 2020_01_02_170221) do
t.string "encrypted_secret_token", limit: 255 t.string "encrypted_secret_token", limit: 255
t.string "encrypted_secret_token_iv", limit: 255 t.string "encrypted_secret_token_iv", limit: 255
t.boolean "secret", default: false, null: false t.boolean "secret", default: false, null: false
t.string "repository_storage", limit: 255, default: "default", null: false
t.integer "storage_version", default: 2, null: false
t.index ["author_id"], name: "index_snippets_on_author_id" t.index ["author_id"], name: "index_snippets_on_author_id"
t.index ["content"], name: "index_snippets_on_content_trigram", opclass: :gin_trgm_ops, using: :gin t.index ["content"], name: "index_snippets_on_content_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["created_at"], name: "index_snippets_on_created_at" t.index ["created_at"], name: "index_snippets_on_created_at"
......
...@@ -2,6 +2,59 @@ ...@@ -2,6 +2,59 @@
>**Note:** This API requires an access token with Maintainer or Owner permissions >**Note:** This API requires an access token with Maintainer or Owner permissions
## List all active services
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/21330) in GitLab 12.7.
Get a list of all active project services.
```
GET /projects/:id/services
```
Example response:
```json
[
{
"id": 75,
"title": "Jenkins CI",
"created_at": "2019-11-20T11:20:25.297Z",
"updated_at": "2019-11-20T12:24:37.498Z",
"active": true,
"commit_events": true,
"push_events": true,
"issues_events": true,
"confidential_issues_events": true,
"merge_requests_events": true,
"tag_push_events": false,
"note_events": true,
"confidential_note_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"job_events": true
}
{
"id": 76,
"title": "Alerts endpoint",
"created_at": "2019-11-20T11:20:25.297Z",
"updated_at": "2019-11-20T12:24:37.498Z",
"active": true,
"commit_events": true,
"push_events": true,
"issues_events": true,
"confidential_issues_events": true,
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"confidential_note_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"job_events": true
}
]
```
## Asana ## Asana
Asana - Teamwork without email Asana - Teamwork without email
......
...@@ -1109,12 +1109,15 @@ module API ...@@ -1109,12 +1109,15 @@ module API
end end
end end
class ProjectService < Grape::Entity class ProjectServiceBasic < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active expose :id, :title, :created_at, :updated_at, :active
expose :commit_events, :push_events, :issues_events, :confidential_issues_events expose :commit_events, :push_events, :issues_events, :confidential_issues_events
expose :merge_requests_events, :tag_push_events, :note_events expose :merge_requests_events, :tag_push_events, :note_events
expose :confidential_note_events, :pipeline_events, :wiki_page_events expose :confidential_note_events, :pipeline_events, :wiki_page_events
expose :job_events expose :job_events
end
class ProjectService < ProjectServiceBasic
# Expose serialized properties # Expose serialized properties
expose :properties do |service, options| expose :properties do |service, options|
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
......
...@@ -66,6 +66,15 @@ module API ...@@ -66,6 +66,15 @@ module API
end end
end end
desc 'Get all active project services' do
success Entities::ProjectServiceBasic
end
get ":id/services" do
services = user_project.services.active
present services, with: Entities::ProjectServiceBasic
end
SERVICES.each do |service_slug, settings| SERVICES.each do |service_slug, settings|
desc "Set #{service_slug} service for project" desc "Set #{service_slug} service for project"
params do params do
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# This class is responsible to update all sha256 fingerprints within the keys table
class MigrateFingerprintSha256WithinKeys
# Temporary AR table for keys
class Key < ActiveRecord::Base
include EachBatch
self.table_name = 'keys'
self.inheritance_column = :_type_disabled
end
TEMP_TABLE = 'tmp_fingerprint_sha256_migration'
def perform(start_id, stop_id)
ActiveRecord::Base.transaction do
execute(<<~SQL)
CREATE TEMPORARY TABLE #{TEMP_TABLE}
(id bigint primary key, fingerprint_sha256 bytea not null)
ON COMMIT DROP
SQL
fingerprints = []
Key.where(id: start_id..stop_id, fingerprint_sha256: nil).find_each do |regular_key|
fingerprint = Base64.decode64(generate_ssh_public_key(regular_key.key))
fingerprints << {
id: regular_key.id,
fingerprint_sha256: ActiveRecord::Base.connection.escape_bytea(fingerprint)
}
end
Gitlab::Database.bulk_insert(TEMP_TABLE, fingerprints)
execute("ANALYZE #{TEMP_TABLE}")
execute(<<~SQL)
UPDATE keys
SET fingerprint_sha256 = t.fingerprint_sha256
FROM #{TEMP_TABLE} t
WHERE keys.id = t.id
SQL
end
end
private
def generate_ssh_public_key(regular_key)
Gitlab::SSHPublicKey.new(regular_key).fingerprint("SHA256").gsub("SHA256:", "")
end
def execute(query)
ActiveRecord::Base.connection.execute(query)
end
end
end
end
...@@ -172,6 +172,8 @@ excluded_attributes: ...@@ -172,6 +172,8 @@ excluded_attributes:
- :secret - :secret
- :encrypted_secret_token - :encrypted_secret_token
- :encrypted_secret_token_iv - :encrypted_secret_token_iv
- :repository_storage
- :storage_version
merge_request_diff: merge_request_diff:
- :external_diff - :external_diff
- :stored_externally - :stored_externally
......
...@@ -15,13 +15,9 @@ module QA ...@@ -15,13 +15,9 @@ module QA
issue.visit! issue.visit!
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
my_first_discussion = 'My first discussion'
show.select_all_activities_filter show.select_all_activities_filter
show.start_discussion(my_first_discussion) show.start_discussion('My first discussion')
page.assert_text(my_first_discussion)
show.reply_to_discussion(my_first_reply) show.reply_to_discussion(my_first_reply)
page.assert_text(my_first_reply)
end end
end end
......
...@@ -89,6 +89,34 @@ describe Projects::TreeController do ...@@ -89,6 +89,34 @@ describe Projects::TreeController do
end end
end end
describe "GET show" do
context 'lfs_blob_ids instance variable' do
let(:id) { 'master' }
context 'with vue tree view enabled' do
before do
get(:show, params: { namespace_id: project.namespace.to_param, project_id: project, id: id })
end
it 'is not set' do
expect(assigns[:lfs_blob_ids]).to be_nil
end
end
context 'with vue tree view disabled' do
before do
stub_feature_flags(vue_file_list: false)
get(:show, params: { namespace_id: project.namespace.to_param, project_id: project, id: id })
end
it 'is set' do
expect(assigns[:lfs_blob_ids]).not_to be_nil
end
end
end
end
describe 'GET show with whitespace in ref' do describe 'GET show with whitespace in ref' do
render_views render_views
......
...@@ -289,6 +289,36 @@ describe ProjectsController do ...@@ -289,6 +289,36 @@ describe ProjectsController do
.not_to exceed_query_limit(2).for_query(expected_query) .not_to exceed_query_limit(2).for_query(expected_query)
end end
end end
context 'lfs_blob_ids instance variable' do
let(:project) { create(:project, :public, :repository) }
before do
sign_in(user)
end
context 'with vue tree view enabled' do
before do
get :show, params: { namespace_id: project.namespace, id: project }
end
it 'is not set' do
expect(assigns[:lfs_blob_ids]).to be_nil
end
end
context 'with vue tree view disabled' do
before do
stub_feature_flags(vue_file_list: false)
get :show, params: { namespace_id: project.namespace, id: project }
end
it 'is set' do
expect(assigns[:lfs_blob_ids]).not_to be_nil
end
end
end
end end
describe 'GET edit' do describe 'GET edit' do
......
...@@ -306,6 +306,21 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -306,6 +306,21 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end end
end end
context 'when job is waiting for resource', :js do
let(:job) { create(:ci_build, :waiting_for_resource, pipeline: pipeline, resource_group: resource_group) }
let(:resource_group) { create(:ci_resource_group, project: project) }
before do
visit project_job_path(project, job)
wait_for_requests
end
it 'shows correct UI components' do
expect(page).to have_content("This job is waiting for resource: #{resource_group.key}")
expect(page).to have_link("Cancel this job")
end
end
context "Job from other project" do context "Job from other project" do
before do before do
visit project_job_path(project, job2) visit project_job_path(project, job2)
......
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"title": { "type": "string" },
"created_at": { "type": "date-time" },
"updated_at": { "type": "date-time" },
"active": { "type": "boolean" },
"commit_events": { "type": "boolean" },
"push_events": { "type": "boolean" },
"issues_events": { "type": "boolean" },
"confidential_issues_events": { "type": "boolean" },
"merge_requests_events": { "type": "boolean" },
"tag_push_events": { "type": "boolean" },
"note_events": { "type": "boolean" },
"confidential_note_events": { "type": "boolean" },
"pipeline_events": { "type": "boolean" },
"wiki_page_events": { "type": "boolean" },
"job_events": { "type": "boolean" }
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "service.json" }
}
...@@ -182,4 +182,21 @@ describe('Participants', function() { ...@@ -182,4 +182,21 @@ describe('Participants', function() {
expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar'); expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar');
}); });
}); });
describe('when not showing participants label', () => {
beforeEach(() => {
vm = mountComponent(Participants, {
participants: PARTICIPANT_LIST,
showParticipantLabel: false,
});
});
it('does not show sidebar collapsed icon', () => {
expect(vm.$el.querySelector('.sidebar-collapsed-icon')).not.toBeTruthy();
});
it('does not show participants label title', () => {
expect(vm.$el.querySelector('.title')).not.toBeTruthy();
});
});
}); });
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::MigrateFingerprintSha256WithinKeys, :migration, schema: 20200106071113 do
subject(:fingerprint_migrator) { described_class.new }
let(:key_table) { table(:keys) }
before do
generate_fingerprints!
end
it 'correctly creates a sha256 fingerprint for a key' do
key_1 = Key.find(1017)
key_2 = Key.find(1027)
expect(key_1.fingerprint_md5).to eq('ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1')
expect(key_1.fingerprint_sha256).to eq(nil)
expect(key_2.fingerprint_md5).to eq('39:e3:64:a6:24:ea:45:a2:8c:55:2a:e9:4d:4f:1f:b4')
expect(key_2.fingerprint_sha256).to eq(nil)
query_count = ActiveRecord::QueryRecorder.new do
fingerprint_migrator.perform(1, 10000)
end.count
expect(query_count).to eq(8)
key_1.reload
key_2.reload
expect(key_1.fingerprint_md5).to eq('ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1')
expect(key_1.fingerprint_sha256).to eq('nUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo/lCg')
expect(key_2.fingerprint_md5).to eq('39:e3:64:a6:24:ea:45:a2:8c:55:2a:e9:4d:4f:1f:b4')
expect(key_2.fingerprint_sha256).to eq('zMNbLekgdjtcgDv8VSC0z5lpdACMG3Q4PUoIz5+H2jM')
end
it 'migrates all keys' do
expect(Key.where(fingerprint_sha256: nil).count).to eq(Key.all.count)
fingerprint_migrator.perform(1, 10000)
expect(Key.where(fingerprint_sha256: nil).count).to eq(0)
end
def generate_fingerprints!
values = ""
(1000..2000).to_a.each do |record|
key = base_key_for(record)
fingerprint = fingerprint_for(key)
values += "(#{record}, #{record}, 'test-#{record}', '#{key}', '#{fingerprint}'),"
end
update_query = <<~SQL
INSERT INTO keys ( id, user_id, title, key, fingerprint )
VALUES
#{values.chomp(",")};
SQL
ActiveRecord::Base.connection.execute(update_query)
end
def base_key_for(record)
'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt0000k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0='
.gsub("0000", "%04d" % (record - 1)) # generate arbitrary keys with placeholder 0000 within the key above
end
def fingerprint_for(key)
Gitlab::SSHPublicKey.new(key).fingerprint("md5")
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200106071113_update_fingerprint_sha256_within_keys.rb')
describe UpdateFingerprintSha256WithinKeys, :sidekiq, :migration do
let(:key_table) { table(:keys) }
describe '#up' do
it 'the BackgroundMigrationWorker will be triggered and fingerprint_sha256 populated' do
key_table.create!(
id: 1,
user_id: 1,
title: 'test',
key: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=',
fingerprint: 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1',
fingerprint_sha256: nil
)
expect(Key.first.fingerprint_sha256).to eq(nil)
described_class.new.up
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
expect(BackgroundMigrationWorker.jobs.first["args"][0]).to eq("MigrateFingerprintSha256WithinKeys")
expect(BackgroundMigrationWorker.jobs.first["args"][1]).to eq([1, 1])
end
end
end
...@@ -1610,6 +1610,12 @@ describe Ci::Build do ...@@ -1610,6 +1610,12 @@ describe Ci::Build do
it { is_expected.to be_cancelable } it { is_expected.to be_cancelable }
end end
context 'when build is waiting for resource' do
let(:build) { create(:ci_build, :waiting_for_resource) }
it { is_expected.to be_cancelable }
end
end end
context 'when build is not cancelable' do context 'when build is not cancelable' do
......
...@@ -10,6 +10,37 @@ describe API::Services do ...@@ -10,6 +10,37 @@ describe API::Services do
create(:project, creator_id: user.id, namespace: user.namespace) create(:project, creator_id: user.id, namespace: user.namespace)
end end
describe "GET /projects/:id/services" do
it 'returns authentication error when unauthenticated' do
get api("/projects/#{project.id}/services")
expect(response).to have_gitlab_http_status(401)
end
it "returns error when authenticated but user is not a project owner" do
project.add_developer(user2)
get api("/projects/#{project.id}/services", user2)
expect(response).to have_gitlab_http_status(403)
end
context 'project with services' do
let!(:active_service) { create(:emails_on_push_service, project: project, active: true) }
let!(:service) { create(:custom_issue_tracker_service, project: project, active: false) }
it "returns a list of all active services" do
get api("/projects/#{project.id}/services", user)
aggregate_failures 'expect successful response with all active services' do
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
expect(response).to match_response_schema('public_api/v4/services')
end
end
end
end
Service.available_services_names.each do |service| Service.available_services_names.each do |service|
describe "PUT /projects/:id/services/#{service.dasherize}" do describe "PUT /projects/:id/services/#{service.dasherize}" do
include_context service include_context service
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment