Merge branch '15571-api-for-ci-lint' into 'master'
API for CI Lint ## What does this MR do? Add API for CI Lint. Can check if `.gitlab-ci.yml` content is valid. ## What are the relevant issue numbers? Closes #15571 ## Does this MR meet the acceptance criteria? - [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added - [x] API support added - [x] Documentation - Tests - [x] Added for this feature/bug - [x] All builds are passing cc @ubudzisz @grzesiek @yorickpeterse See merge request !5953
This commit is contained in:
commit
a83c5ff48f
9 changed files with 172 additions and 9 deletions
|
@ -92,6 +92,7 @@ v 8.12.0 (unreleased)
|
||||||
- Refactor the triggers page and documentation !6217
|
- Refactor the triggers page and documentation !6217
|
||||||
- Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
|
- Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
|
||||||
- Use default clone protocol on "check out, review, and merge locally" help page URL
|
- Use default clone protocol on "check out, review, and merge locally" help page URL
|
||||||
|
- API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
|
||||||
|
|
||||||
v 8.11.5 (unreleased)
|
v 8.11.5 (unreleased)
|
||||||
- Optimize branch lookups and force a repository reload for Repository#find_branch
|
- Optimize branch lookups and force a repository reload for Repository#find_branch
|
||||||
|
|
|
@ -7,19 +7,14 @@ module Ci
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@content = params[:content]
|
@content = params[:content]
|
||||||
|
@error = Ci::GitlabCiYamlProcessor.validation_message(@content)
|
||||||
|
@status = @error.blank?
|
||||||
|
|
||||||
if @content.blank?
|
if @error.blank?
|
||||||
@status = false
|
|
||||||
@error = "Please provide content of .gitlab-ci.yml"
|
|
||||||
else
|
|
||||||
@config_processor = Ci::GitlabCiYamlProcessor.new(@content)
|
@config_processor = Ci::GitlabCiYamlProcessor.new(@content)
|
||||||
@stages = @config_processor.stages
|
@stages = @config_processor.stages
|
||||||
@builds = @config_processor.builds
|
@builds = @config_processor.builds
|
||||||
@status = true
|
|
||||||
end
|
end
|
||||||
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
|
|
||||||
@error = e.message
|
|
||||||
@status = false
|
|
||||||
rescue
|
rescue
|
||||||
@error = 'Undefined error'
|
@error = 'Undefined error'
|
||||||
@status = false
|
@status = false
|
||||||
|
|
|
@ -41,8 +41,9 @@ following locations:
|
||||||
- [Sidekiq metrics](sidekiq_metrics.md)
|
- [Sidekiq metrics](sidekiq_metrics.md)
|
||||||
- [System Hooks](system_hooks.md)
|
- [System Hooks](system_hooks.md)
|
||||||
- [Tags](tags.md)
|
- [Tags](tags.md)
|
||||||
- [Users](users.md)
|
|
||||||
- [Todos](todos.md)
|
- [Todos](todos.md)
|
||||||
|
- [Users](users.md)
|
||||||
|
- [Validate CI configuration](ci/lint.md)
|
||||||
|
|
||||||
### Internal CI API
|
### Internal CI API
|
||||||
|
|
||||||
|
|
49
doc/api/ci/lint.md
Normal file
49
doc/api/ci/lint.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Validate the .gitlab-ci.yml
|
||||||
|
|
||||||
|
> [Introduced][ce-5953] in GitLab 8.12.
|
||||||
|
|
||||||
|
Checks if your .gitlab-ci.yml file is valid.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST ci/lint
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| ---------- | ------- | -------- | -------- |
|
||||||
|
| `content` | string | yes | the .gitlab-ci.yaml content|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --header "Content-Type: application/json" https://gitlab.example.com/api/v3/ci/lint --data '{"content": "{ \"image\": \"ruby:2.1\", \"services\": [\"postgres\"], \"before_script\": [\"gem install bundler\", \"bundle install\", \"bundle exec rake db:create\"], \"variables\": {\"DB_NAME\": \"postgres\"}, \"types\": [\"test\", \"deploy\", \"notify\"], \"rspec\": { \"script\": \"rake spec\", \"tags\": [\"ruby\", \"postgres\"], \"only\": [\"branches\"]}}"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Be sure to copy paste the exact contents of `.gitlab-ci.yml` as YAML is very picky about indentation and spaces.
|
||||||
|
|
||||||
|
Example responses:
|
||||||
|
|
||||||
|
* Valid content:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "valid",
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Invalid content:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "invalid",
|
||||||
|
"errors": [
|
||||||
|
"variables config should be a hash of key value pairs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Without the content attribute:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "content is missing"
|
||||||
|
}
|
||||||
|
```
|
|
@ -45,6 +45,7 @@ module API
|
||||||
mount ::API::Keys
|
mount ::API::Keys
|
||||||
mount ::API::Labels
|
mount ::API::Labels
|
||||||
mount ::API::LicenseTemplates
|
mount ::API::LicenseTemplates
|
||||||
|
mount ::API::Lint
|
||||||
mount ::API::Members
|
mount ::API::Members
|
||||||
mount ::API::MergeRequests
|
mount ::API::MergeRequests
|
||||||
mount ::API::Milestones
|
mount ::API::Milestones
|
||||||
|
|
21
lib/api/lint.rb
Normal file
21
lib/api/lint.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
module API
|
||||||
|
class Lint < Grape::API
|
||||||
|
namespace :ci do
|
||||||
|
desc 'Validation of .gitlab-ci.yml content'
|
||||||
|
params do
|
||||||
|
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
|
||||||
|
end
|
||||||
|
post '/lint' do
|
||||||
|
error = Ci::GitlabCiYamlProcessor.validation_message(params[:content])
|
||||||
|
|
||||||
|
status 200
|
||||||
|
|
||||||
|
if error.blank?
|
||||||
|
{ status: 'valid', errors: [] }
|
||||||
|
else
|
||||||
|
{ status: 'invalid', errors: [error] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -78,6 +78,17 @@ module Ci
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.validation_message(content)
|
||||||
|
return 'Please provide content of .gitlab-ci.yml' if content.blank?
|
||||||
|
|
||||||
|
begin
|
||||||
|
Ci::GitlabCiYamlProcessor.new(content)
|
||||||
|
nil
|
||||||
|
rescue ValidationError, Psych::SyntaxError => e
|
||||||
|
e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def initial_parsing
|
def initial_parsing
|
||||||
|
|
|
@ -1250,5 +1250,40 @@ EOT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#validation_message" do
|
||||||
|
context "when the YAML could not be parsed" do
|
||||||
|
it "returns an error about invalid configutaion" do
|
||||||
|
content = YAML.dump("invalid: yaml: test")
|
||||||
|
|
||||||
|
expect(GitlabCiYamlProcessor.validation_message(content))
|
||||||
|
.to eq "Invalid configuration format"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the tags parameter is invalid" do
|
||||||
|
it "returns an error about invalid tags" do
|
||||||
|
content = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
|
||||||
|
|
||||||
|
expect(GitlabCiYamlProcessor.validation_message(content))
|
||||||
|
.to eq "jobs:rspec tags should be an array of strings"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when YAML content is empty" do
|
||||||
|
it "returns an error about missing content" do
|
||||||
|
expect(GitlabCiYamlProcessor.validation_message(''))
|
||||||
|
.to eq "Please provide content of .gitlab-ci.yml"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the YAML is valid" do
|
||||||
|
it "does not return any errors" do
|
||||||
|
content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
|
||||||
|
|
||||||
|
expect(GitlabCiYamlProcessor.validation_message(content)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
49
spec/requests/api/lint_spec.rb
Normal file
49
spec/requests/api/lint_spec.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe API::Lint, api: true do
|
||||||
|
include ApiHelpers
|
||||||
|
|
||||||
|
describe 'POST /ci/lint' do
|
||||||
|
context 'with valid .gitlab-ci.yaml content' do
|
||||||
|
let(:yaml_content) do
|
||||||
|
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'passes validation' do
|
||||||
|
post api('/ci/lint'), { content: yaml_content }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response).to be_an Hash
|
||||||
|
expect(json_response['status']).to eq('valid')
|
||||||
|
expect(json_response['errors']).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an invalid .gitlab_ci.yml' do
|
||||||
|
it 'responds with errors about invalid syntax' do
|
||||||
|
post api('/ci/lint'), { content: 'invalid content' }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response['status']).to eq('invalid')
|
||||||
|
expect(json_response['errors']).to eq(['Invalid configuration format'])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "responds with errors about invalid configuration" do
|
||||||
|
post api('/ci/lint'), { content: '{ image: "ruby:2.1", services: ["postgres"] }' }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response['status']).to eq('invalid')
|
||||||
|
expect(json_response['errors']).to eq(['jobs config should contain at least one visible job'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without the content parameter' do
|
||||||
|
it 'responds with validation error about missing content' do
|
||||||
|
post api('/ci/lint')
|
||||||
|
|
||||||
|
expect(response).to have_http_status(400)
|
||||||
|
expect(json_response['error']).to eq('content is missing')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue