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:
commit
51d92fb568
22 changed files with 258 additions and 97 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
27
app/controllers/projects/ci/lints_controller.rb
Normal file
27
app/controllers/projects/ci/lints_controller.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
27
app/views/projects/ci/lints/show.html.haml
Normal file
27
app/views/projects/ci/lints/show.html.haml
Normal 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)
|
|
@ -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 } }
|
||||
|
|
5
changelogs/unreleased/43603-ci-lint-support.yml
Normal file
5
changelogs/unreleased/43603-ci-lint-support.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move ci/lint under project's namespace
|
||||
merge_request: 17729
|
||||
author:
|
||||
type: added
|
|
@ -1,5 +1,5 @@
|
|||
namespace :ci do
|
||||
resource :lint, only: [:show, :create]
|
||||
resource :lint, only: :show
|
||||
|
||||
root to: redirect('')
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
123
spec/controllers/projects/ci/lints_controller_spec.rb
Normal file
123
spec/controllers/projects/ci/lints_controller_spec.rb
Normal 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
|
|
@ -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});")
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'ci/lints/show' do
|
||||
describe 'projects/ci/lints/show' do
|
||||
include Devise::Test::ControllerHelpers
|
||||
|
||||
describe 'XSS protection' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
|
||||
|
||||
describe 'XSS protection' do
|
||||
before do
|
||||
assign(:project, project)
|
||||
assign(:status, true)
|
||||
assign(:builds, config_processor.builds)
|
||||
assign(:stages, config_processor.stages)
|
||||
|
@ -48,6 +49,7 @@ describe 'ci/lints/show' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the content is valid' do
|
||||
let(:content) do
|
||||
{
|
||||
build_template: {
|
||||
|
@ -60,10 +62,8 @@ describe 'ci/lints/show' do
|
|||
}
|
||||
end
|
||||
|
||||
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
|
||||
|
||||
context 'when the content is valid' do
|
||||
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
|
Loading…
Reference in a new issue