Merge branch '43603-ci-lint-support' into 'master'

Resolve "/ci/lint should support include keyword in config file"

Closes #43603

See merge request gitlab-org/gitlab-ce!17729
This commit is contained in:
Kamil Trzciński 2018-03-28 08:01:33 +00:00
commit 51d92fb568
22 changed files with 258 additions and 97 deletions

View File

@ -1,21 +0,0 @@
.ci-body {
.incorrect-syntax {
font-size: 18px;
color: $lint-incorrect-color;
}
.correct-syntax {
font-size: 18px;
color: $lint-correct-color;
}
}
.ci-linter {
.ci-editor {
height: 400px;
}
.ci-template pre {
white-space: pre-wrap;
}
}

View File

@ -1121,3 +1121,25 @@ pre.light-well {
padding-top: $gl-padding;
padding-bottom: 37px;
}
.project-ci-body {
.incorrect-syntax {
font-size: 18px;
color: $lint-incorrect-color;
}
.correct-syntax {
font-size: 18px;
color: $lint-correct-color;
}
}
.project-ci-linter {
.ci-editor {
height: 400px;
}
.ci-template pre {
white-space: pre-wrap;
}
}

View File

@ -4,20 +4,5 @@ module Ci
def show
end
def create
@content = params[:content]
@error = Gitlab::Ci::YamlProcessor.validation_message(@content)
@status = @error.blank?
if @error.blank?
@config_processor = Gitlab::Ci::YamlProcessor.new(@content)
@stages = @config_processor.stages
@builds = @config_processor.builds
@jobs = @config_processor.jobs
end
render :show
end
end
end

View File

@ -0,0 +1,27 @@
class Projects::Ci::LintsController < Projects::ApplicationController
before_action :authorize_create_pipeline!
def show
end
def create
@content = params[:content]
@error = Gitlab::Ci::YamlProcessor.validation_message(@content, yaml_processor_options)
@status = @error.blank?
if @error.blank?
@config_processor = Gitlab::Ci::YamlProcessor.new(@content, yaml_processor_options)
@stages = @config_processor.stages
@builds = @config_processor.builds
@jobs = @config_processor.jobs
end
render :show
end
private
def yaml_processor_options
{ project: @project, sha: project.repository.commit.sha }
end
end

View File

@ -29,12 +29,12 @@ module Projects
@project_runners = @project.runners.ordered
@assignable_runners = current_user.ci_authorized_runners
.assignable_for(project).ordered.page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners = ::Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
def define_secret_variables
@variable = Ci::Variable.new(project: project)
@variable = ::Ci::Variable.new(project: project)
.present(current_user: current_user)
@variables = project.variables.order_key_asc
.map { |variable| variable.present(current_user: current_user) }
@ -42,7 +42,7 @@ module Projects
def define_triggers_variables
@triggers = @project.triggers
@trigger = Ci::Trigger.new
@trigger = ::Ci::Trigger.new
end
def define_badges_variables

View File

@ -1,27 +1,9 @@
- page_title "CI Lint"
- page_description "Validate your GitLab CI configuration file"
- content_for :library_javascripts do
= page_specific_javascript_tag('lib/ace.js')
%h2 Check your .gitlab-ci.yml
.ci-linter
.row
= form_tag ci_lint_path, method: :post do
.form-group
.col-sm-12
.file-holder
.js-file-title.file-title.clearfix
Content of .gitlab-ci.yml
#ci-editor.ci-editor= @content
= text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true)
.col-sm-12
.pull-left.prepend-top-10
= submit_tag('Validate', class: 'btn btn-success submit-yml')
.pull-right.prepend-top-10
= button_tag('Clear', type: 'button', class: 'btn btn-default clear-yml')
.row.prepend-top-20
.col-sm-12
.results.ci-template
= render partial: 'create' if defined?(@status)
.row.empty-state
.col-xs-12
.svg-content
= image_tag 'illustrations/feature_moved.svg'
.col-xs-12
.text-content.text-center
%h4= _("GitLab CI Linter has been moved")
%p
= _("To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button.")

View File

@ -0,0 +1,27 @@
- page_title "CI Lint"
- page_description "Validate your GitLab CI configuration file"
- content_for :library_javascripts do
= page_specific_javascript_tag('lib/ace.js')
%h2 Check your .gitlab-ci.yml
.project-ci-linter
.row
= form_tag project_ci_lint_path(@project), method: :post do
.form-group
.col-sm-12
.file-holder
.js-file-title.file-title.clearfix
Content of .gitlab-ci.yml
#ci-editor.ci-editor= @content
= text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true)
.col-sm-12
.pull-left.prepend-top-10
= submit_tag('Validate', class: 'btn btn-success submit-yml')
.pull-right.prepend-top-10
= button_tag('Clear', type: 'button', class: 'btn btn-default clear-yml')
.row.prepend-top-20
.col-sm-12
.results.project-ci-template
= render partial: 'create' if defined?(@status)

View File

@ -10,6 +10,6 @@
"no-pipelines-svg-path" => image_path('illustrations/pipelines_pending.svg'),
"can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s,
"new-pipeline-path" => can?(current_user, :create_pipeline, @project) && new_project_pipeline_path(@project),
"ci-lint-path" => can?(current_user, :create_pipeline, @project) && ci_lint_path,
"ci-lint-path" => can?(current_user, :create_pipeline, @project) && project_ci_lint_path(@project),
"reset-cache-path" => can?(current_user, :admin_pipeline, @project) && reset_cache_project_settings_ci_cd_path(@project) ,
"has-gitlab-ci" => (@project.has_ci? && @project.builds_enabled?).to_s } }

View File

@ -0,0 +1,5 @@
---
title: Move ci/lint under project's namespace
merge_request: 17729
author:
type: added

View File

@ -1,5 +1,5 @@
namespace :ci do
resource :lint, only: [:show, :create]
resource :lint, only: :show
root to: redirect('')
end

View File

@ -280,6 +280,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :keep
end
end
namespace :ci do
resource :lint, only: [:show, :create]
end
end
draw :legacy_builds

View File

@ -104,8 +104,8 @@ Jobs are used to create jobs, which are then picked by
What is important is that each job is run independently from each other.
If you want to check whether your `.gitlab-ci.yml` file is valid, there is a
Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
If you want to check whether the `.gitlab-ci.yml` of your project is valid, there is a
Lint tool under the page `/ci/lint` of your project namespace. You can also find
a "CI Lint" button to go to this page under **CI/CD ➔ Pipelines** and
**Pipelines ➔ Jobs** in your project.

View File

@ -1526,8 +1526,9 @@ capitalization, the commit will be created but the pipeline will be skipped.
## Validate the .gitlab-ci.yml
Each instance of GitLab CI has an embedded debug tool called Lint.
You can find the link under `/ci/lint` of your gitlab instance.
Each instance of GitLab CI has an embedded debug tool called Lint, which validates the
content of your `.gitlab-ci.yml` files. You can find the Lint under the page `ci/lint` of your
project namespace (e.g, `http://gitlab-example.com/gitlab-org/project-123/ci/lint`)
## Using reserved keywords

View File

@ -4,7 +4,8 @@ module Gitlab
# Base GitLab CI Configuration facade
#
class Config
def initialize(config)
# EE would override this and utilize opts argument
def initialize(config, opts = {})
@config = Loader.new(config).load!
@global = Entry::Global.new(@config)

View File

@ -7,8 +7,8 @@ module Gitlab
attr_reader :cache, :stages, :jobs
def initialize(config)
@ci_config = Gitlab::Ci::Config.new(config)
def initialize(config, opts = {})
@ci_config = Gitlab::Ci::Config.new(config, opts)
@config = @ci_config.to_hash
unless @ci_config.valid?
@ -73,11 +73,11 @@ module Gitlab
end
end
def self.validation_message(content)
def self.validation_message(content, opts = {})
return 'Please provide content of .gitlab-ci.yml' if content.blank?
begin
Gitlab::Ci::YamlProcessor.new(content)
Gitlab::Ci::YamlProcessor.new(content, opts)
nil
rescue ValidationError => e
e.message

View File

@ -0,0 +1,123 @@
require 'spec_helper'
describe Projects::Ci::LintsController do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET #show' do
context 'with enough privileges' do
before do
project.add_developer(user)
get :show, namespace_id: project.namespace, project_id: project
end
it 'should be success' do
expect(response).to be_success
end
it 'should render show page' do
expect(response).to render_template :show
end
it 'should retrieve project' do
expect(assigns(:project)).to eq(project)
end
end
context 'without enough privileges' do
before do
project.add_guest(user)
get :show, namespace_id: project.namespace, project_id: project
end
it 'should respond with 404' do
expect(response).to have_gitlab_http_status(404)
end
end
end
describe 'POST #create' do
let(:remote_file_path) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
let(:remote_file_content) do
<<~HEREDOC
before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC
end
let(:content) do
<<~HEREDOC
include:
- #{remote_file_path}
rubocop:
script:
- bundle exec rubocop
HEREDOC
end
context 'with a valid gitlab-ci.yml' do
before do
WebMock.stub_request(:get, remote_file_path).to_return(body: remote_file_content)
project.add_developer(user)
post :create, namespace_id: project.namespace, project_id: project, content: content
end
it 'should be success' do
expect(response).to be_success
end
it 'render show page' do
expect(response).to render_template :show
end
it 'should retrieve project' do
expect(assigns(:project)).to eq(project)
end
end
context 'with an invalid gitlab-ci.yml' do
let(:content) do
<<~HEREDOC
rubocop:
scriptt:
- bundle exec rubocop
HEREDOC
end
before do
project.add_developer(user)
post :create, namespace_id: project.namespace, project_id: project, content: content
end
it 'should assign errors' do
expect(assigns[:error]).to eq('jobs:rubocop config contains unknown keys: scriptt')
end
end
context 'without enough privileges' do
before do
project.add_guest(user)
post :create, namespace_id: project.namespace, project_id: project, content: content
end
it 'should respond with 404' do
expect(response).to have_gitlab_http_status(404)
end
end
end
end

View File

@ -1,10 +1,14 @@
require 'spec_helper'
describe 'CI Lint', :js do
before do
sign_in(create(:user))
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
visit ci_lint_path
before do
project.add_developer(user)
sign_in(user)
visit project_ci_lint_path(project)
find('#ci-editor')
execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")

View File

@ -1,12 +1,13 @@
require 'spec_helper'
describe 'ci/lints/show' do
describe 'projects/ci/lints/show' do
include Devise::Test::ControllerHelpers
let(:project) { create(:project, :repository) }
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
describe 'XSS protection' do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
before do
assign(:project, project)
assign(:status, true)
assign(:builds, config_processor.builds)
assign(:stages, config_processor.stages)
@ -48,22 +49,21 @@ describe 'ci/lints/show' do
end
end
let(:content) do
{
build_template: {
script: './build.sh',
tags: ['dotnet'],
only: ['test@dude/repo'],
except: ['deploy'],
environment: 'testing'
}
}
end
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
context 'when the content is valid' do
let(:content) do
{
build_template: {
script: './build.sh',
tags: ['dotnet'],
only: ['test@dude/repo'],
except: ['deploy'],
environment: 'testing'
}
}
end
before do
assign(:project, project)
assign(:status, true)
assign(:builds, config_processor.builds)
assign(:stages, config_processor.stages)
@ -83,6 +83,7 @@ describe 'ci/lints/show' do
context 'when the content is invalid' do
before do
assign(:project, project)
assign(:status, false)
assign(:error, 'Undefined error')
end