Commit 0c8f4deb authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee into ce_upstream

parents 78215890 7b3aadca
Please view this file on the master branch, on stable branches it's out of date.
## 10.0.3 (2017-10-05)
- [FIXED] Rewrite Geo database rake tasks so they operate on the correct database. !3052
- [FIXED] Show group tab if member lock is enabled.
- [FIXED] File uploaders do not perform hard check, only soft check.
- [FIXED] Only show Turn on Service Desk button when user has permissions.
- [FIXED] Fix EE delta size check handling with annotated tags.
## 10.0.2 (2017-09-27)
- [FIXED] Send valid project path as name for Jira dev panel.
......@@ -53,6 +61,19 @@ Please view this file on the master branch, on stable branches it's out of date.
- Add group issue boards.
- Ports style changes fixed in a conflict in ce to ee upstream to master for new projects page.
## 9.5.8 (2017-10-04)
- [FIXED] Fix EE delta size check handling with annotated tags.
- [FIXED] Fix delta size check to handle commit or nil objects.
## 9.5.7 (2017-10-03)
- No changes.
## 9.5.6 (2017-09-29)
- [FIXED] Show group tab if member lock is enabled.
## 9.5.5 (2017-09-18)
- [FIXED] Fixes activation of project mirror when new project is created. !2756
......
......@@ -2,6 +2,23 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 10.0.3 (2017-10-05)
- [FIXED] find_user Users helper method no longer overrides find_user API helper method. !14418
- [FIXED] Fix CSRF validation issue when closing/opening merge requests from the UI. !14555
- [FIXED] Kubernetes integration: ensure v1.8.0 compatibility. !14635
- [FIXED] Fixes data parameter not being sent in ajax request for jobs log.
- [FIXED] Improves UX of autodevops popover to match gpg one.
- [FIXED] Fixed commenting on side-by-side commit diff.
- [FIXED] Make sure API responds with 401 when invalid authentication info is provided.
- [FIXED] Fix merge request counter updates after merge.
- [FIXED] Fix gitlab-rake gitlab:import:repos task failing.
- [FIXED] Fix pushes to an empty repository not invalidating has_visible_content? cache.
- [FIXED] Ensure all refs are restored on a restore from backup.
- [FIXED] Gitaly RepositoryExists remains opt-in for all method calls.
- [FIXED] Fix 500 error on merged merge requests when GitLab is restored from a backup.
- [FIXED] Adjust MRs being stuck on "process of being merged" for more than 2 hours.
## 10.0.2 (2017-09-27)
- [FIXED] Notes will not show an empty bubble when the author isn't a member. !14450
......@@ -195,6 +212,14 @@ entry.
- Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi)
- [BUGIFX] Improves subgroup creation permissions. !13418
## 9.5.8 (2017-10-04)
- [FIXED] Fixed fork button being disabled for users who can fork to a group.
## 9.5.7 (2017-10-03)
- Fix gitlab rake:import:repos task.
## 9.5.6 (2017-09-29)
- [FIXED] Fix MR ready to merge buttons/controls at mobile breakpoint. !14242
......
......@@ -72,6 +72,13 @@
:title="pipeline.yaml_errors">
yaml invalid
</span>
<span
v-if="pipeline.flags.failure_reason"
v-tooltip
class="js-pipeline-url-failure label label-danger"
:title="pipeline.failure_reason">
error
</span>
<a
v-if="pipeline.flags.auto_devops"
tabindex="0"
......
......@@ -27,7 +27,7 @@ export default {
<button
v-if="showDisabledButton"
type="button"
class="btn btn-success btn-sm"
class="js-disabled-merge-button btn btn-success btn-sm"
disabled="true">
Merge
</button>
......
......@@ -10,27 +10,37 @@ export default {
},
template: `
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<status-icon
status="failed"
showDisabledButton />
<div class="media-body space-children">
<span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span>
<span
v-if="mr.shouldBeRebased"
class="bold">
Fast-forward merge is not possible.
To merge this request, first rebase locally.
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="btn btn-default btn-xs js-resolve-conflicts-button">
Resolve conflicts
</a>
<a
v-if="mr.canMerge"
class="btn btn-default btn-xs js-merge-locally-button"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
<template v-else>
<span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span>
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="js-resolve-conflicts-button btn btn-default btn-xs">
Resolve conflicts
</a>
<a
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
</template>
</div>
</div>
`,
......
......@@ -288,14 +288,16 @@ export default {
:mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" />
<span v-if="mr.ffOnlyEnabled">
<span
v-if="mr.ffOnlyEnabled"
class="js-fast-forward-message">
Fast-forward merge without a merge commit
</span>
<button
v-else
@click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs"
class="js-modify-commit-message-button btn btn-default btn-xs"
type="button">
Modify commit message
</button>
......
......@@ -59,6 +59,8 @@ export default class MergeRequestStore {
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false;
this.mergePath = data.merge_path;
this.ffOnlyEnabled = data.ff_only_enabled;
this.shouldBeRebased = !!data.should_be_rebased;
this.statusPath = data.status_path;
this.emailPatchesPath = data.email_patches_path;
this.plainDiffPath = data.plain_diff_path;
......
......@@ -26,7 +26,7 @@ class Admin::PushRulesController < Admin::ApplicationController
def push_rule_params
params.require(:push_rule).permit(:deny_delete_tag, :delete_branch_regex,
:commit_message_regex, :branch_name_regex, :force_push_regex, :author_email_regex, :member_check,
:file_name_regex, :max_file_size, :prevent_secrets)
:file_name_regex, :max_file_size, :prevent_secrets, :reject_unsigned_commits)
end
def push_rule
......
......@@ -6,7 +6,7 @@ class Groups::BillingsController < Groups::ApplicationController
def index
@top_most_group = @group.root_ancestor if @group.has_parent?
current_plan = (@top_most_group || @group).actual_plan
current_plan = (@top_most_group || @group).actual_plan_name
@plans_data = FetchSubscriptionPlansService.new(plan: current_plan).execute
end
end
......@@ -2,6 +2,8 @@ class Profiles::BillingsController < Profiles::ApplicationController
before_action :verify_namespace_plan_check_enabled
def index
@plans_data = FetchSubscriptionPlansService.new(plan: current_user.namespace.actual_plan).execute
@plans_data = FetchSubscriptionPlansService
.new(plan: current_user.namespace.actual_plan_name)
.execute
end
end
......@@ -9,7 +9,7 @@ class Projects::BranchesController < Projects::ApplicationController
def index
@sort = params[:sort].presence || sort_value_recently_updated
@branches = BranchesFinder.new(@repository, params).execute
@branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
@branches = Kaminari.paginate_array(@branches).page(params[:page])
respond_to do |format|
......
......@@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_create_issue!, only: [:new, :create]
# Allow modify issue
before_action :authorize_update_issue!, only: [:edit, :update, :move]
before_action :authorize_update_issue!, only: [:update, :move]
# Allow create a new branch and empty WIP merge request from current issue
before_action :authorize_create_merge_request!, only: [:create_merge_request]
......@@ -65,10 +65,6 @@ class Projects::IssuesController < Projects::ApplicationController
respond_with(@issue)
end
def edit
respond_with(@issue)
end
def show
@noteable = @issue
@note = @project.notes.new(noteable: @issue)
......@@ -128,10 +124,6 @@ class Projects::IssuesController < Projects::ApplicationController
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
respond_to do |format|
format.html do
recaptcha_check_with_fallback { render :edit }
end
format.json do
render_issue_json
end
......
......@@ -25,7 +25,14 @@ class Projects::PushRulesController < Projects::ApplicationController
# Only allow a trusted parameter "white list" through.
def push_rule_params
params.require(:push_rule).permit(:deny_delete_tag, :delete_branch_regex,
:commit_message_regex, :branch_name_regex, :force_push_regex, :author_email_regex, :member_check, :file_name_regex, :max_file_size, :prevent_secrets)
allowed_fields = %i[deny_delete_tag delete_branch_regex commit_message_regex
branch_name_regex force_push_regex author_email_regex
member_check file_name_regex max_file_size prevent_secrets]
if can?(current_user, :change_reject_unsigned_commits, project)
allowed_fields << :reject_unsigned_commits
end
params.require(:push_rule).permit(allowed_fields)
end
end
......@@ -347,6 +347,7 @@ class ProjectsController < Projects::ApplicationController
:tag_list,
:visibility_level,
:template_name,
:merge_method,
project_feature_attributes: %i[
builds_access_level
......
module ProjectsHelper
include Gitlab::CurrentSettings
prepend ::EE::ProjectsHelper
def link_to_project(project)
link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
title = content_tag(:span, project.name, class: 'project-name')
......
module PushRulesHelper
def reject_unsigned_commits_description(push_rule)
message = [s_("ProjectSettings|Only signed commits can be pushed to this repository.")]
if push_rule.global?
message << s_("ProjectSettings|This setting will be applied to all projects unless overridden by an admin.")
else
if PushRule.global&.reject_unsigned_commits
message << if push_rule.reject_unsigned_commits
s_("ProjectSettings|This setting is applied on the server level and can be overridden by an admin.")
else
s_("ProjectSettings|This setting is applied on the server level but has been overridden for this project.")
end
message << s_("ProjectSettings|Contact an admin to change this setting.") unless current_user.admin?
end
end
message.join(' ')
end
end
......@@ -5,6 +5,7 @@ module Ci
include Importable
include AfterCommitQueue
include Presentable
include Gitlab::OptimisticLocking
prepend ::EE::Ci::Pipeline
......@@ -71,6 +72,11 @@ module Ci
auto_devops_source: 2
}
enum failure_reason: {
unknown_failure: 0,
config_error: 1
}.merge(EE_FAILURE_REASONS)
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
......@@ -122,6 +128,12 @@ module Ci
pipeline.auto_canceled_by = nil
end
before_transition any => :failed do |pipeline, transition|
transition.args.first.try do |reason|
pipeline.failure_reason = reason
end
end
after_transition [:created, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
......@@ -276,7 +288,7 @@ module Ci
end
def cancel_running
Gitlab::OptimisticLocking.retry_lock(cancelable_statuses) do |cancelable|
retry_optimistic_lock(cancelable_statuses) do |cancelable|
cancelable.find_each do |job|
yield(job) if block_given?
job.cancel
......@@ -325,6 +337,10 @@ module Ci
@stage_seeds ||= config_processor.stage_seeds(self)
end
def seeds_size
@seeds_size ||= stage_seeds.sum(&:size)
end
def has_kubernetes_active?
project.kubernetes_service&.active?
end
......@@ -416,7 +432,7 @@ module Ci
end
def update_status
Gitlab::OptimisticLocking.retry_lock(self) do
retry_optimistic_lock(self) do
case latest_builds_status
when 'pending' then enqueue
when 'running' then run
......
......@@ -81,6 +81,7 @@ module HasStatus
scope :canceled, -> { where(status: 'canceled') }
scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') }
scope :alive, -> { where(status: [:created, :pending, :running]) }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
......
......@@ -23,6 +23,7 @@ class GeoNode < ActiveRecord::Base
validates :encrypted_secret_access_key, presence: true
validates :geo_node_key, presence: true, if: :secondary?
validate :check_not_adding_primary_as_secondary, if: :secondary?
after_initialize :build_dependents
after_save :expire_cache!
......@@ -216,12 +217,12 @@ class GeoNode < ActiveRecord::Base
end
end
def validate(record)
# Prevent locking yourself out
if record.host == Gitlab.config.gitlab.host &&
record.port == Gitlab.config.gitlab.port &&
record.relative_url_root == Gitlab.config.gitlab.relative_url_root && !record.primary
record.errors[:base] << 'Current node must be the primary node or you will be locking yourself out'
# Prevent locking yourself out
def check_not_adding_primary_as_secondary
if host == Gitlab.config.gitlab.host &&
port == Gitlab.config.gitlab.port &&
relative_url_root == Gitlab.config.gitlab.relative_url_root
errors.add(:base, 'Current node must be the primary node or you will be locking yourself out')
end
end
......
......@@ -12,7 +12,6 @@ class License < ActiveRecord::Base
contribution_analytics
elastic_search
export_issues
fast_forward_merge
group_webhooks
issuable_default_templates
issue_board_focus_mode
......@@ -63,7 +62,6 @@ class License < ActiveRecord::Base
cross_project_pipelines
deploy_board
export_issues
fast_forward_merge
file_locks
group_webhooks
issuable_default_templates
......
......@@ -549,6 +549,14 @@ class MergeRequest < ActiveRecord::Base
true
end
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
end
def should_be_rebased?
project.ff_merge_must_be_possible? && !ff_merge_possible?
end
def can_cancel_merge_when_pipeline_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
......
......@@ -1588,6 +1588,34 @@ class Project < ActiveRecord::Base
Gitlab::GlRepository.gl_repository(self, is_wiki)
end
def merge_method
if self.merge_requests_ff_only_enabled
:ff
elsif self.merge_requests_rebase_enabled
:rebase_merge
else
:merge
end
end
def merge_method=(method)
case method.to_s
when "ff"
self.merge_requests_ff_only_enabled = true
self.merge_requests_rebase_enabled = true
when "rebase_merge"
self.merge_requests_ff_only_enabled = false
self.merge_requests_rebase_enabled = true
when "merge"
self.merge_requests_ff_only_enabled = false
self.merge_requests_rebase_enabled = false
end
end
def ff_merge_must_be_possible?
self.merge_requests_ff_only_enabled || self.merge_requests_rebase_enabled
end
private
def storage
......
......@@ -6,16 +6,27 @@ class PushRule < ActiveRecord::Base
FILES_BLACKLIST = YAML.load_file(Rails.root.join('lib/gitlab/checks/files_blacklist.yml'))
def self.global
find_by(is_sample: true)
end
def commit_validation?
commit_message_regex.present? ||
branch_name_regex.present? ||
author_email_regex.present? ||
reject_unsigned_commits ||
member_check ||
file_name_regex.present? ||
max_file_size > 0 ||
prevent_secrets
end
def commit_signature_allowed?(commit)
return true unless reject_unsigned_commits
commit.has_signature?
end
def commit_message_allowed?(message)
data_match?(message, commit_message_regex)
end
......@@ -36,6 +47,33 @@ class PushRule < ActiveRecord::Base
regex_list.find { |regex| data_match?(file_path, regex) }
end
def reject_unsigned_commits=(value)
enabled_globally = PushRule.global&.reject_unsigned_commits
is_disabled = !Gitlab::Utils.to_boolean(value)
# If setting is globally disabled and user disable it at project level,
# reset the attr so we can use the default global if required later.
if !enabled_globally && is_disabled
super(nil)
else
super(value)
end
end
def reject_unsigned_commits
value = super
# return if value is true/false or if current object is the global setting
return value if global? || !value.nil?
PushRule.global&.reject_unsigned_commits
end
alias_method :reject_unsigned_commits?, :reject_unsigned_commits
def global?
is_sample?
end
private
def data_match?(data, regex)
......
module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
prepend ::EE::Ci::PipelinePresenter
FAILURE_REASONS = {
config_error: 'CI/CD YAML configuration error!'
}.merge(EE_FAILURE_REASONS)
presents :pipeline
def failure_reason
return unless pipeline.failure_reason?
FAILURE_REASONS[pipeline.failure_reason.to_sym] ||
pipeline.failure_reason
end
def status_title
if auto_canceled?
"Pipeline is redundant and is auto-canceled by Pipeline ##{auto_canceled_by_id}"
......
......@@ -20,6 +20,7 @@ class PipelineEntity < Grape::Entity
expose :has_yaml_errors?, as: :yaml_errors
expose :can_retry?, as: :retryable
expose :can_cancel?, as: :cancelable
expose :failure_reason?, as: :failure_reason
end
expose :details do
......@@ -44,6 +45,11 @@ class PipelineEntity < Grape::Entity
end
expose :commit, using: CommitEntity
expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? }
expose :failure_reason, if: -> (pipeline, _) { pipeline.failure_reason? } do |pipeline|
pipeline.present.failure_reason
end
expose :retry_path, if: -> (*) { can_retry? } do |pipeline|
retry_project_pipeline_path(pipeline.project, pipeline)
......@@ -53,8 +59,6 @@ class PipelineEntity < Grape::Entity
cancel_project_pipeline_path(pipeline.project, pipeline)
end
expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? }
private
alias_method :pipeline, :object
......
......@@ -6,7 +6,9 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::Config,
Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::Create].freeze
EE::Gitlab::Ci::Pipeline::Chain::Limit::Size,
Gitlab::Ci::Pipeline::Chain::Create,
EE::Gitlab::Ci::Pipeline::Chain::Limit::Activity].freeze
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, mirror_update: false, &block)
@pipeline = Ci::Pipeline.new(
......
......@@ -12,7 +12,7 @@ module MergeRequests
def rebase
if merge_request.rebase_in_progress?
log_error('Rebase task canceled: Another rebase is already in progress')
log_error('Rebase task canceled: Another rebase is already in progress', save_message_on_model: true)
return false
end
......@@ -52,7 +52,7 @@ module MergeRequests
false
rescue => e
log_error('Failed to rebase branch:')
log_error(e)
log_error(e.message, save_message_on_model: true)
false
ensure
clean_dir
......
......@@ -24,7 +24,7 @@ module MergeRequests
log_error("`#{command.join(' ')}` failed:")
end
log_error(output)
log_error(output, save_message_on_model: true)
raise GitCommandError
end
......@@ -40,8 +40,10 @@ module MergeRequests
@target_project ||= merge_request.target_project
end
def log_error(message)
def log_error(message, save_message_on_model: false)
Gitlab::GitLogger.error("#{self.class.name} error (#{merge_request.to_reference(full: true)}): #{message}")
merge_request.update(merge_error: message) if save_message_on_model
end
def clean_dir
......
- page_title "Billing"
- if @top_most_group
- top_most_group_plan = subscription_plan_info(@plans_data, @top_most_group.actual_plan)
- top_most_group_plan = subscription_plan_info(@plans_data, @top_most_group.actual_plan_name)
= render 'shared/billings/billing_plan_header', namespace: @group, plan: top_most_group_plan, parent_group: @top_most_group
- else
= render 'shared/billings/billing_plans', plans_data: @plans_data, namespace: @group
- form = local_assigns.fetch(:form)
- project = local_assigns.fetch(:project)
.radio
= label_tag :project_merge_method_ff do
= form.radio_button :merge_method, :ff, class: "js-merge-method-radio"
%strong Fast-forward merge
%br
%span.descr
No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.
%br
%span.descr
When fast-forward merge is not possible, the user must first rebase locally.
- form = local_assigns.fetch(:form)
.radio
= label_tag :project_merge_method_rebase_merge do
= form.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio"
%strong Merge commit with semi-linear history
%br
%span.descr
A merge commit is created for every merge, but merging is only allowed if fast-forward merge is possible.
This way you could make sure that if this merge request would build, after merging to target branch it would also build.
%br
%span.descr
When fast-forward merge is not possible, the user must first rebase locally.
- form = local_assigns.fetch(:form)
= render 'projects/ee/merge_request_settings', form: form, project: @project
.form-group
= label_tag :merge_method_merge, class: 'label-light' do
Merge method
.radio
= label_tag :project_merge_method_merge do
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio"
%strong Merge commit
%br
%span.descr
A merge commit is created for every merge, and merging is allowed as long as there are no conflicts.
= render 'merge_request_rebase_settings', form: form
= render 'merge_request_fast_forward_settings', project: @project, form: form
= render 'projects/merge_request_merge_settings', form: form
......@@ -95,7 +95,7 @@
= render 'shared/promotions/promote_mr_features'
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
= render 'merge_request_settings', form: f
= render 'projects/ee/merge_request_settings', form: f
= f.submit 'Save changes', class: "btn btn-save"
= render 'projects/ee/service_desk_settings'
......
- page_title "Edit", "#{@issue.title} (#{@issue.to_reference})", "Issues"
%h3.page-title
Edit Issue ##{@issue.iid}
%hr
= render "form"
.form-group
= f.check_box :reject_unsigned_commits, class: "pull-left", disabled: !can_change_reject_unsigned_commits?(f.object)
.prepend-left-20
= f.label :reject_unsigned_commits, class: "label-light append-bottom-0" do
Reject unsigned commits
%p.light.append-bottom-0
= reject_unsigned_commits_description(f.object)
.form-group
= f.check_box :deny_delete_tag, class: "pull-left"
.prepend-left-20
......
......@@ -34,7 +34,7 @@
- case purchase_link.action
- when 'downgrade'
= plan_purchase_link(href, s_("Billinglans|Downgrade"))
= plan_purchase_link(href, s_("BillingPlans|Downgrade"))
- when 'current_plan'
= plan_purchase_link(href, s_("BillingPlans|Current plan"))
- when 'upgrade'
......
- current_plan = subscription_plan_info(plans_data, namespace.actual_plan)
- current_plan = subscription_plan_info(plans_data, namespace.actual_plan_name)
- if current_plan
= render 'shared/billings/billing_plan_header', namespace: namespace, plan: current_plan
......
......@@ -16,7 +16,7 @@ class StuckImportJobsWorker
private
def mark_projects_without_jid_as_failed!
started_projects_without_jid.each do |project|
enqueued_projects_without_jid.each do |project|
project.mark_import_as_failed(error_message)
end.count
end
......@@ -24,7 +24,7 @@ class StuckImportJobsWorker
def mark_projects_with_jid_as_failed!
completed_jids_count = 0
started_projects_with_jid.find_in_batches(batch_size: 500) do |group|
enqueued_projects_with_jid.find_in_batches(batch_size: 500) do |group|
jids = group.map(&:import_jid)
# Find the jobs that aren't currently running or that exceeded the threshold.
......@@ -43,16 +43,16 @@ class StuckImportJobsWorker
completed_jids_count
end
def started_projects
Project.with_import_status(:started)
def enqueued_projects
Project.with_import_status(:scheduled, :started)
end
def started_projects_with_jid
started_projects.where.not(import_jid: nil)
def enqueued_projects_with_jid
enqueued_projects.where.not(import_jid: nil)
end
def started_projects_without_jid
started_projects.where(import_jid: nil)
def enqueued_projects_without_jid
enqueued_projects.where(import_jid: nil)
end
def error_message
......
---
title: Fix EE delta size check handling with annotated tags
title: Show errors when rebase onto target branch fails in the UI
merge_request:
author:
type: fixed
---
title: Fix removing the username from the git repository URL for pull mirroring
merge_request: 3060
author:
type: fixed
---
title: Add new push rule to reject unsigned commits
merge_request: 2913
author:
---
title: Show group tab if member lock is enabled
title: Add partial index on push_rules.is_sample
merge_request:
author:
type: fixed
type: other
---
title: Fix a Geo node validation, preventing admins from locking themselves out
merge_request: 3040
author:
type: fixed
---
title: File uploaders do not perform hard check, only soft check
merge_request:
author:
type: fixed
---
title: Add suport for CI/CD pipeline policy management
merge_request: 2986
author:
type: added
---
title: Find stuck scheduled import jobs and also mark them as failed.
merge_request: 3055
author:
type: fixed
---
title: Only show Turn on Service Desk button when user has permissions.
merge_request:
author:
type: fixed
---
title: Fix the default branches sorting to actually be 'Last updated'
merge_request: 14295
author:
type: fixed
---
title: Remove the ability to visit the issue edit form directly
merge_request: 14523
author:
type: removed
---
title: find_user Users helper method no longer overrides find_user API helper method.
merge_request: 14418
author:
type: fixed
---
title: Adjust MRs being stuck on "process of being merged" for more than 2 hours
merge_request:
author:
type: fixed
---
title: Fixes data parameter not being sent in ajax request for jobs log
merge_request:
author:
type: fixed
---
title: Fix CSRF validation issue when closing/opening merge requests from the UI
merge_request: 14555
author:
type: fixed
---
title: Fixed commenting on side-by-side commit diff
merge_request:
author:
type: fixed
---
title: Make sure API responds with 401 when invalid authentication info is provided
merge_request:
author:
type: fixed
---
title: Fix merge request counter updates after merge
merge_request:
author:
type: fixed
---
title: Gitaly RepositoryExists remains opt-in for all method calls
merge_request:
author:
type: fixed
......@@ -45,6 +45,7 @@ module Gitlab
#{config.root}/ee/app/models/concerns
#{config.root}/ee/app/policies
#{config.root}/ee/app/serializers
#{config.root}/ee/app/presenters
#{config.root}/ee/app/services
#{config.root}/ee/app/workers
])
......
require './spec/support/sidekiq'
Plan.create!(name: EE::Namespace::FREE_PLAN,
title: EE::Namespace::FREE_PLAN.titleize)
EE::Namespace::EE_PLANS.each_key do |plan|
Plan.create!(name: plan, title: plan.titleize)
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddRejectUnsignedCommitsToPushRules < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :push_rules, :reject_unsigned_commits, :boolean
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexForPushrulesIsSample < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index", "remove_concurrent_index" or
# "add_column_with_default" you must disable the use of transactions
# as these methods can not run in an existing transaction.
# When using "add_concurrent_index" or "remove_concurrent_index" methods make sure
# that either of them is the _only_ method called in the migration,
# any other changes should go in a separate migration.
# This ensures that upon failure _only_ the index creation or removing fails
# and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
disable_ddl_transaction!
# Careful, on MySQL the where clause is ignored. We make the index
# on the same column as the WHERE clause (is_sample) even though
# this is generally a silly thing to do because that way on MySQL
# the resulting index on is_sample will still fix the same
# queries. It'll just waste space indexing all rows where is_sample
# is false as well. In this case there's only a single row where
# is_sample is true so having it be the index key is harmless.
def up
return if index_exists? :push_rules, :is_sample
add_concurrent_index(:push_rules, :is_sample, where: "is_sample")
end
def down
return unless index_exists? :push_rules, :is_sample
remove_concurrent_index(:push_rules, :is_sample, where: "is_sample")
end
end
class AddPipelineQuotasToPlan < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :plans, :active_pipelines_limit, :integer
add_column :plans, :pipeline_size_limit, :integer
end
end
class AddFailureReasonToPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_pipelines, :failure_reason, :integer
end
end
class CreateMissingFreePlan < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
class Plan < ActiveRecord::Base
self.table_name = 'plans'
end
def up
Plan.create!(name: 'free', title: 'Free')
end
def down
Plan.find_by(name: 'free')&.destroy!
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170928100231) do
ActiveRecord::Schema.define(version: 20171002105019) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -403,6 +403,7 @@ ActiveRecord::Schema.define(version: 20170928100231) do
t.integer "source"
t.integer "config_source"
t.boolean "protected"
t.integer "failure_reason"
end
add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
......@@ -1367,6 +1368,8 @@ ActiveRecord::Schema.define(version: 20170928100231) do
t.datetime_with_timezone "updated_at", null: false
t.string "name"
t.string "title"
t.integer "active_pipelines_limit"
t.integer "pipeline_size_limit"
end
add_index "plans", ["name"], name: "index_plans_on_name", using: :btree
......@@ -1616,8 +1619,10 @@ ActiveRecord::Schema.define(version: 20170928100231) do
t.integer "max_file_size", default: 0, null: false
t.boolean "prevent_secrets", default: false, null: false
t.string "branch_name_regex"
t.boolean "reject_unsigned_commits"
end
add_index "push_rules", ["is_sample"], name: "index_push_rules_on_is_sample", where: "is_sample", using: :btree
add_index "push_rules", ["project_id"], name: "index_push_rules_on_project_id", using: :btree
create_table "redirect_routes", force: :cascade do |t|
......
## Enable or disable GitLab CI
## Enable or disable GitLab CI/CD
_To effectively use GitLab CI, you need a valid [`.gitlab-ci.yml`](yaml/README.md)
To effectively use GitLab CI/CD, you need a valid [`.gitlab-ci.yml`](yaml/README.md)
file present at the root directory of your project and a
[runner](runners/README.md) properly set up. You can read our
[quick start guide](quick_start/README.md) to get you started._
[quick start guide](quick_start/README.md) to get you started.
If you are using an external CI server like Jenkins or Drone CI, it is advised
to disable GitLab CI in order to not have any conflicts with the commits status
If you are using an external CI/CD server like Jenkins or Drone CI, it is advised
to disable GitLab CI/CD in order to not have any conflicts with the commits status
API.
---
GitLab CI is exposed via the `/pipelines` and `/builds` pages of a project.
Disabling GitLab CI in a project does not delete any previous jobs.
In fact, the `/pipelines` and `/builds` pages can still be accessed, although
GitLab CI/CD is exposed via the `/pipelines` and `/jobs` pages of a project.
Disabling GitLab CI/CD in a project does not delete any previous jobs.
In fact, the `/pipelines` and `/jobs` pages can still be accessed, although
it's hidden from the left sidebar menu.
GitLab CI is enabled by default on new installations and can be disabled either
GitLab CI/CD is enabled by default on new installations and can be disabled either
individually under each project's settings, or site-wide by modifying the
settings in `gitlab.yml` and `gitlab.rb` for source and Omnibus installations
respectively.
### Per-project user setting
The setting to enable or disable GitLab CI can be found with the name **Pipelines**
under the **Sharing & Permissions** area of a project's settings along with
**Merge Requests**. Choose one of **Disabled**, **Only team members** and
**Everyone with access** and hit **Save changes** for the settings to take effect.
The setting to enable or disable GitLab CI/CD can be found under your project's
**Settings > General > Permissions**. Choose one of "Disabled", "Only team members"
or "Everyone with access" and hit **Save changes** for the settings to take effect.
![Sharing & Permissions settings](img/permissions_settings.png)
![Sharing & Permissions settings](../user/project/settings/img/sharing_and_permissions_settings.png)
---
### Site-wide administrator setting
### Site-wide admin setting
You can disable GitLab CI site-wide, by modifying the settings in `gitlab.yml`
You can disable GitLab CI/CD site-wide, by modifying the settings in `gitlab.yml`
and `gitlab.rb` for source and Omnibus installations respectively.
Two things to note:
1. Disabling GitLab CI, will affect only newly-created projects. Projects that
1. Disabling GitLab CI/CD, will affect only newly-created projects. Projects that
had it enabled prior to this modification, will work as before.
1. Even if you disable GitLab CI, users will still be able to enable it in the
1. Even if you disable GitLab CI/CD, users will still be able to enable it in the
project's settings.
---
For installations from source, open `gitlab.yml` with your editor and set
`builds` to `false`:
......
......@@ -26,7 +26,7 @@ so every environment can have one or more deployments. GitLab keeps track of
your deployments, so you always know what is currently being deployed on your
servers. If you have a deployment service such as [Kubernetes][kubernetes-service]
enabled for your project, you can use it to assist with your deployments, and
can even access a web terminal for your environment from within GitLab!
can even access a [web terminal](#web-terminals) for your environment from within GitLab!
To better understand how environments and deployments work, let's consider an
example. We assume that you have already created a project in GitLab and set up
......@@ -119,7 +119,7 @@ where you can find information of the last deployment status of an environment.
Here's how the Environments page looks so far.
![Staging environment view](img/environments_available_staging.png)
![Environment view](img/environments_available.png)
There's a bunch of information there, specifically you can see:
......@@ -229,7 +229,7 @@ You can find it in the pipeline, job, environment, and deployment views.
| Pipelines | Single pipeline | Environments | Deployments | jobs |
| --------- | ----------------| ------------ | ----------- | -------|
| ![Pipelines manual action](img/environments_manual_action_pipelines.png) | ![Pipelines manual action](img/environments_manual_action_single_pipeline.png) | ![Environments manual action](img/environments_manual_action_environments.png) | ![Deployments manual action](img/environments_manual_action_deployments.png) | ![Builds manual action](img/environments_manual_action_builds.png) |
| ![Pipelines manual action](img/environments_manual_action_pipelines.png) | ![Pipelines manual action](img/environments_manual_action_single_pipeline.png) | ![Environments manual action](img/environments_manual_action_environments.png) | ![Deployments manual action](img/environments_manual_action_deployments.png) | ![Builds manual action](img/environments_manual_action_jobs.png) |
Clicking on the play button in either of these places will trigger the
`deploy_prod` job, and the deployment will be recorded under a new
......@@ -402,7 +402,7 @@ places within GitLab.
| In a merge request widget as a link | In the Environments view as a button | In the Deployments view as a button |
| -------------------- | ------------ | ----------- |
| ![Environment URL in merge request](img/environments_mr_review_app.png) | ![Environment URL in environments](img/environments_link_url.png) | ![Environment URL in deployments](img/environments_link_url_deployments.png) |
| ![Environment URL in merge request](img/environments_mr_review_app.png) | ![Environment URL in environments](img/environments_available.png) | ![Environment URL in deployments](img/deployments_view.png) |
If a merge request is eventually merged to the default branch (in our case
`master`) and that branch also deploys to an environment (in our case `staging`
......@@ -574,7 +574,7 @@ Once configured, GitLab will attempt to retrieve [supported performance metrics]
environment which has had a successful deployment. If monitoring data was
successfully retrieved, a Monitoring button will appear for each environment.
![Environment Detail with Metrics](img/prometheus_environment_detail_with_metrics.png)
![Environment Detail with Metrics](img/deployments_view.png)
Clicking on the Monitoring button will display a new page, showing up to the last
8 hours of performance data. It may take a minute or two for data to appear
......@@ -593,10 +593,11 @@ Web terminals were added in GitLab 8.15 and are only available to project
masters and owners.
If you deploy to your environments with the help of a deployment service (e.g.,
the [Kubernetes service][kubernetes-service], GitLab can open
the [Kubernetes service][kubernetes-service]), GitLab can open
a terminal session to your environment! This is a very powerful feature that
allows you to debug issues without leaving the comfort of your web browser. To
enable it, just follow the instructions given in the service documentation.
enable it, just follow the instructions given in the service integration
documentation.
Once enabled, your environments will gain a "terminal" button:
......
doc/ci/img/deployments_view.png

19.5 KB | W: | H:

doc/ci/img/deployments_view.png

59.7 KB | W: | H:

doc/ci/img/deployments_view.png
doc/ci/img/deployments_view.png
doc/ci/img/deployments_view.png
doc/ci/img/deployments_view.png
  • 2-up
  • Swipe
  • Onion skin
doc/ci/img/environments_link_url_mr.png

17.5 KB | W: | H:

doc/ci/img/environments_link_url_mr.png

33.6 KB | W: | H:

doc/ci/img/environments_link_url_mr.png
doc/ci/img/environments_link_url_mr.png
doc/ci/img/environments_link_url_mr.png
doc/ci/img/environments_link_url_mr.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -150,14 +150,15 @@ script:
## Secret variables
>**Notes:**
- This feature requires GitLab Runner 0.4.0 or higher.
- Group-level secret variables added in GitLab 9.4.
- Be aware that secret variables are not masked, and their values can be shown
in the job logs if explicitly asked to do so. If your project is public or
internal, you can set the pipelines private from your project's Pipelines
settings. Follow the discussion in issue [#13784][ce-13784] for masking the
secret variables.
NOTE: **Note:**
Group-level secret variables were added in GitLab 9.4.
CAUTION: **Important:**
Be aware that secret variables are not masked, and their values can be shown
in the job logs if explicitly asked to do so. If your project is public or
internal, you can set the pipelines private from your [project's Pipelines
settings](../../user/project/pipelines/settings.md#visibility-of-pipelines).
Follow the discussion in issue [#13784][ce-13784] for masking the secret variables.
GitLab CI allows you to define per-project or per-group secret variables
that are set in the pipeline environment. The secret variables are stored out of
......@@ -172,6 +173,8 @@ Likewise, group-level secret variables can be added by going to your group's
**Settings > CI/CD**, then finding the section called **Secret variables**.
Any variables of [subgroups] will be inherited recursively.
![Secret variables](img/secret_variables.png)
Once you set them, they will be available for all subsequent pipelines. You can also
[protect your variables](#protected-secret-variables).
......@@ -221,7 +224,7 @@ are set in the build environment. These variables are only defined for
the project services that you are using to learn which variables they define.
An example project service that defines deployment variables is
[Kubernetes Service](../../user/project/integrations/kubernetes.md).
[Kubernetes Service](../../user/project/integrations/kubernetes.md#deployment-variables).
## Debug tracing
......@@ -458,8 +461,8 @@ export CI_REGISTRY_USER="gitlab-ci-token"
export CI_REGISTRY_PASSWORD="longalfanumstring"
```
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784
[ee-2112]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI secret variables"
[eep]: https://about.gitlab.com/gitlab-ee/ "Available only in GitLab Enterprise Edition Premium"
[envs]: ../environments.md
[protected branches]: ../../user/project/protected_branches.md
......
......@@ -312,41 +312,7 @@ not create them manually.
### Upgrading Geo
#### Upgrading to GitLab 10.1
[Hashed storage](../administration/repository_storage_types.md) was introduced
in GitLab 10.0, and a [migration path](../administration/raketasks/storage.md)
for existing repositories was added in GitLab 10.1.
After upgrading to GitLab 10.1, we recommend that you
[enable hashed storage for all new projects](#step-5-enabling-hashed-storage-from-gitlab-100),
then [migrate existing projects to hashed storage](../administration/raketasks/storage.md).
This will significantly reduce the amount of synchronization required between
nodes in the event of project or group renames.
#### Upgrading to GitLab 10.0
Since GitLab 10.0, we require all **Geo** systems to [use SSH key lookups via
the database](ssh.md) to avoid having to maintain consistency of the
`authorized_keys` file for SSH access. Failing to do this will prevent users
from being able to clone via SSH.
#### Upgrading from GitLab 9.3 or older
If you started running Geo on GitLab 9.3 or older, we recommend that you
resync your secondary PostgreSQL databases to use replication slots. If you
started using Geo with GitLab 9.4 or 10.x, no further action should be
required because replication slots are used by default. However, if you
started with GitLab 9.3 and upgraded later, you should still follow the
instructions below.
When in doubt, it does not hurt to do a resync. The easiest way to do this in
Omnibus is the following:
1. Install GitLab on the primary server
1. Run `gitlab-ctl reconfigure` and `gitlab-ctl restart postgresql`. This will enable replication slots on the primary database.
1. Install GitLab on the secondary server.
1. Re-run the [database replication process](database.md#step-3-initiate-the-replication-process).
See the [updating the Geo nodes document](updating_the_geo_nodes.md).
## Troubleshooting
......
......@@ -14,6 +14,42 @@ all you need to do is update GitLab itself:
the tracking database is enabled.
1. [Test](#check-status-after-updating) primary and secondary nodes, and check version in each.
## Upgrading to GitLab 10.1
[Hashed storage](../administration/repository_storage_types.md) was introduced
in GitLab 10.0, and a [migration path](../administration/raketasks/storage.md)
for existing repositories was added in GitLab 10.1.
After upgrading to GitLab 10.1, we recommend that you
[enable hashed storage for all new projects](#step-5-enabling-hashed-storage-from-gitlab-100),
then [migrate existing projects to hashed storage](../administration/raketasks/storage.md).
This will significantly reduce the amount of synchronization required between
nodes in the event of project or group renames.
## Upgrading to GitLab 10.0
Since GitLab 10.0, we require all **Geo** systems to [use SSH key lookups via
the database](ssh.md) to avoid having to maintain consistency of the
`authorized_keys` file for SSH access. Failing to do this will prevent users
from being able to clone via SSH.
## Upgrading from GitLab 9.3 or older
If you started running Geo on GitLab 9.3 or older, we recommend that you
resync your secondary PostgreSQL databases to use replication slots. If you
started using Geo with GitLab 9.4 or 10.x, no further action should be
required because replication slots are used by default. However, if you
started with GitLab 9.3 and upgraded later, you should still follow the
instructions below.
When in doubt, it does not hurt to do a resync. The easiest way to do this in
Omnibus is the following:
1. Install GitLab on the primary server
1. Run `gitlab-ctl reconfigure` and `gitlab-ctl restart postgresql`. This will enable replication slots on the primary database.
1. Install GitLab on the secondary server.
1. Re-run the [database replication process](database.md#step-3-initiate-the-replication-process).
## Special update notes for 9.0.x
> **IMPORTANT**:
......
......@@ -63,6 +63,7 @@ The following options are available.
| --------- | :------------: | ----------- |
| Removal of tags with `git push` | 7.10 | Forbid users to remove git tags with `git push`. Tags will still be able to be deleted through the web UI. |
| Check whether author is a GitLab user | 7.10 | Restrict commits by author (email) to existing GitLab users. |
| Check whether commit is signed through GPG | 10.1 | Reject commit when it is not signed through GPG. Read [signing commits with GPG][signing-commits]. |
| Prevent committing secrets to Git | 8.12 | GitLab will reject any files that are likely to contain secrets. Read [what files are forbidden](#prevent-pushing-secrets-to-the-repository). |
| Restrict by commit message | 7.10 | Only commit messages that match this Ruby regular expression are allowed to be pushed. Leave empty to allow any commit message. |
| Restrict by branch name | 9.3 | Only branch names that match this Ruby regular expression are allowed to be pushed. Leave empty to allow any branch name. |
......@@ -145,6 +146,7 @@ bash_history
```
[protected-branches]: ../user/project/protected_branches.md
[signing-commits]: ../user/project/repository/gpg_signed_commits/index.md
[ee-385]: https://gitlab.com/gitlab-org/gitlab-ee/issues/385
[list]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/lib/gitlab/checks/files_blacklist.yml
[hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
......
......@@ -17,25 +17,25 @@ have its own space to store its Docker images.
You can read more about Docker Registry at https://docs.docker.com/registry/introduction/.
---
## Enable the Container Registry for your project
NOTE: **Note:**
If you cannot find the Container Registry entry under your project's settings,
that means that it is not enabled in your GitLab instance. Ask your administrator
to enable it.
1. First, ask your system administrator to enable GitLab Container Registry
following the [administration documentation](../../administration/container_registry.md).
If you are using GitLab.com, this is enabled by default so you can start using
the Registry immediately.
1. Go to your project's settings and enable the **Container Registry** feature
on your project. For new projects this might be enabled by default. For
existing projects (prior GitLab 8.8), you will have to explicitly enable it.
![Enable Container Registry](img/container_registry_enable.png)
1. Go to your [project's General settings](settings/index.md#sharing-and-permissions)
and enable the **Container Registry** feature on your project. For new
projects this might be enabled by default. For existing projects
(prior GitLab 8.8), you will have to explicitly enable it.
1. Hit **Save changes** for the changes to take effect. You should now be able
to see the **Registry** link in the project menu.
to see the **Registry** link in the sidebar.
![Container Registry tab](img/container_registry_tab.png)
![Container Registry](img/container_registry.png)
## Build and push images
......
......@@ -17,6 +17,15 @@ With Deploy Boards you can gain more insight into deploys with benefits such as:
- Finer state detail (Waiting, Deploying, Finished, Unknown)
- See [Canary Deployments](canary_deployments.md)
Here's an example of a Deploy Board of the production environment.
![Deploy Boards landing page](img/deploy_boards_landing_page.png)
The squares represent pods in your Kubernetes cluster that are associated with
the given environment. Hovering above each square you can see the state of a
deploy rolling out. The percentage is the percent of the pods that are updated
to the latest release.
Since Deploy Boards are tightly coupled with Kubernetes, there is some required
knowledge. In particular you should be familiar with:
......@@ -80,17 +89,6 @@ GitLab will only display a Deploy Board for top-level environments. Foldered
environments like `review/*` (usually used for [Review Apps]) won't have a
Deploy Board attached to them.
## How it works
Here's an example of a Deploy Board of the production environment.
![Deploy Boards landing page](img/deploy_boards_landing_page.png)
The squares represent pods in your Kubernetes cluster that are associated with
the given environment. Hovering above each square you can see the state of a
deploy rolling out. The percentage is the percent of the pods that are updated
to the latest release.
## Canary Deployments
A popular CI strategy, where a small portion of the fleet is updated to the new
......@@ -118,4 +116,4 @@ version of your application.
[autodeploy]: ../../ci/autodeploy/index.md "GitLab Autodeploy"
[kube-image]: https://gitlab.com/gitlab-examples/kubernetes-deploy/container_registry "Kubernetes deploy Container Registry"
[manual action]: ../../ci/yaml/README.md#manual-actions
[runners]: ../../ci/runners/README.md
\ No newline at end of file
[runners]: ../../ci/runners/README.md
doc/user/project/img/issue_board.png

50.2 KB | W: | H:

doc/user/project/img/issue_board.png

80.7 KB | W: | H:

doc/user/project/img/issue_board.png
doc/user/project/img/issue_board.png
doc/user/project/img/issue_board.png
doc/user/project/img/issue_board.png
  • 2-up
  • Swipe
  • Onion skin
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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