Commit 51d92fb5 authored by Kamil Trzciński's avatar Kamil Trzciński

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
parents 949d1b37 bab17600
.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;
}
}
...@@ -1121,3 +1121,25 @@ pre.light-well { ...@@ -1121,3 +1121,25 @@ pre.light-well {
padding-top: $gl-padding; padding-top: $gl-padding;
padding-bottom: 37px; 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;
}
}
...@@ -4,20 +4,5 @@ module Ci ...@@ -4,20 +4,5 @@ module Ci
def show def show
end 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
end end
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
...@@ -29,12 +29,12 @@ module Projects ...@@ -29,12 +29,12 @@ module Projects
@project_runners = @project.runners.ordered @project_runners = @project.runners.ordered
@assignable_runners = current_user.ci_authorized_runners @assignable_runners = current_user.ci_authorized_runners
.assignable_for(project).ordered.page(params[:page]).per(20) .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) @shared_runners_count = @shared_runners.count(:all)
end end
def define_secret_variables def define_secret_variables
@variable = Ci::Variable.new(project: project) @variable = ::Ci::Variable.new(project: project)
.present(current_user: current_user) .present(current_user: current_user)
@variables = project.variables.order_key_asc @variables = project.variables.order_key_asc
.map { |variable| variable.present(current_user: current_user) } .map { |variable| variable.present(current_user: current_user) }
...@@ -42,7 +42,7 @@ module Projects ...@@ -42,7 +42,7 @@ module Projects
def define_triggers_variables def define_triggers_variables
@triggers = @project.triggers @triggers = @project.triggers
@trigger = Ci::Trigger.new @trigger = ::Ci::Trigger.new
end end
def define_badges_variables def define_badges_variables
......
- page_title "CI Lint" .row.empty-state
- page_description "Validate your GitLab CI configuration file" .col-xs-12
- content_for :library_javascripts do .svg-content
= page_specific_javascript_tag('lib/ace.js') = image_tag 'illustrations/feature_moved.svg'
.col-xs-12
%h2 Check your .gitlab-ci.yml .text-content.text-center
%h4= _("GitLab CI Linter has been moved")
.ci-linter %p
.row = _("To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button.")
= 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)
- 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)
...@@ -10,6 +10,6 @@ ...@@ -10,6 +10,6 @@
"no-pipelines-svg-path" => image_path('illustrations/pipelines_pending.svg'), "no-pipelines-svg-path" => image_path('illustrations/pipelines_pending.svg'),
"can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s, "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), "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) , "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 } } "has-gitlab-ci" => (@project.has_ci? && @project.builds_enabled?).to_s } }
---
title: Move ci/lint under project's namespace
merge_request: 17729
author:
type: added
namespace :ci do namespace :ci do
resource :lint, only: [:show, :create] resource :lint, only: :show
root to: redirect('') root to: redirect('')
end end
...@@ -280,6 +280,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -280,6 +280,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :keep post :keep
end end
end end
namespace :ci do
resource :lint, only: [:show, :create]
end
end end
draw :legacy_builds draw :legacy_builds
......
...@@ -104,8 +104,8 @@ Jobs are used to create jobs, which are then picked by ...@@ -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. 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 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 GitLab instance. You can also find 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 a "CI Lint" button to go to this page under **CI/CD ➔ Pipelines** and
**Pipelines ➔ Jobs** in your project. **Pipelines ➔ Jobs** in your project.
......
...@@ -1526,8 +1526,9 @@ capitalization, the commit will be created but the pipeline will be skipped. ...@@ -1526,8 +1526,9 @@ capitalization, the commit will be created but the pipeline will be skipped.
## Validate the .gitlab-ci.yml ## Validate the .gitlab-ci.yml
Each instance of GitLab CI has an embedded debug tool called Lint. Each instance of GitLab CI has an embedded debug tool called Lint, which validates the
You can find the link under `/ci/lint` of your gitlab instance. 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 ## Using reserved keywords
......
...@@ -4,7 +4,8 @@ module Gitlab ...@@ -4,7 +4,8 @@ module Gitlab
# Base GitLab CI Configuration facade # Base GitLab CI Configuration facade
# #
class Config class Config
def initialize(config) # EE would override this and utilize opts argument
def initialize(config, opts = {})
@config = Loader.new(config).load! @config = Loader.new(config).load!
@global = Entry::Global.new(@config) @global = Entry::Global.new(@config)
......
...@@ -7,8 +7,8 @@ module Gitlab ...@@ -7,8 +7,8 @@ module Gitlab
attr_reader :cache, :stages, :jobs attr_reader :cache, :stages, :jobs
def initialize(config) def initialize(config, opts = {})
@ci_config = Gitlab::Ci::Config.new(config) @ci_config = Gitlab::Ci::Config.new(config, opts)
@config = @ci_config.to_hash @config = @ci_config.to_hash
unless @ci_config.valid? unless @ci_config.valid?
...@@ -73,11 +73,11 @@ module Gitlab ...@@ -73,11 +73,11 @@ module Gitlab
end end
end end
def self.validation_message(content) def self.validation_message(content, opts = {})
return 'Please provide content of .gitlab-ci.yml' if content.blank? return 'Please provide content of .gitlab-ci.yml' if content.blank?
begin begin
Gitlab::Ci::YamlProcessor.new(content) Gitlab::Ci::YamlProcessor.new(content, opts)
nil nil
rescue ValidationError => e rescue ValidationError => e
e.message e.message
......
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
require 'spec_helper' require 'spec_helper'
describe 'CI Lint', :js do describe 'CI Lint', :js do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do before do
sign_in(create(:user)) project.add_developer(user)
sign_in(user)
visit ci_lint_path visit project_ci_lint_path(project)
find('#ci-editor') find('#ci-editor')
execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});") execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")
......
require 'spec_helper' require 'spec_helper'
describe 'ci/lints/show' do describe 'projects/ci/lints/show' do
include Devise::Test::ControllerHelpers include Devise::Test::ControllerHelpers
let(:project) { create(:project, :repository) }
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
describe 'XSS protection' do describe 'XSS protection' do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
before do before do
assign(:project, project)
assign(:status, true) assign(:status, true)
assign(:builds, config_processor.builds) assign(:builds, config_processor.builds)
assign(:stages, config_processor.stages) assign(:stages, config_processor.stages)
...@@ -48,22 +49,21 @@ describe 'ci/lints/show' do ...@@ -48,22 +49,21 @@ describe 'ci/lints/show' do
end end
end end
let(:content) do context 'when the content is valid' do
{ let(:content) do
build_template: { {
script: './build.sh', build_template: {
tags: ['dotnet'], script: './build.sh',
only: ['test@dude/repo'], tags: ['dotnet'],
except: ['deploy'], only: ['test@dude/repo'],
environment: 'testing' except: ['deploy'],
environment: 'testing'
}
} }
} end
end
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
context 'when the content is valid' do
before do before do
assign(:project, project)
assign(:status, true) assign(:status, true)
assign(:builds, config_processor.builds) assign(:builds, config_processor.builds)
assign(:stages, config_processor.stages) assign(:stages, config_processor.stages)
...@@ -83,6 +83,7 @@ describe 'ci/lints/show' do ...@@ -83,6 +83,7 @@ describe 'ci/lints/show' do
context 'when the content is invalid' do context 'when the content is invalid' do
before do before do
assign(:project, project)
assign(:status, false) assign(:status, false)
assign(:error, 'Undefined error') assign(:error, 'Undefined error')
end end
......
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