Commit 8c644ff8 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch 'mc/feature/add-ci-yaml-parmeter-to-project-lint' into 'master'

Add CI yaml parameter to API project CI lint

See merge request gitlab-org/gitlab!47026
parents 88f5d2e1 f72654c6
---
title: Add POST project CI lint API endpoint.
merge_request: 47026
author:
type: added
......@@ -99,6 +99,54 @@ Example response:
}
```
## Validate a CI YAML configuration with a namespace
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231352) in GitLab 13.6.
Checks if CI/CD YAML configuration is valid. This endpoint has namespace
specific context.
```plaintext
POST /projects/:id/ci/lint
```
| Attribute | Type | Required | Description |
| ---------- | ------- | -------- | -------- |
| `content` | string | yes | The CI/CD configuration content. |
| `dry_run` | boolean | no | Run [pipeline creation simulation](../ci/lint.md#pipeline-simulation), or only do static check. This is false by default. |
Example request:
```shell
curl --header "Content-Type: application/json" "https://gitlab.example.com/api/v4/projects/:id/ci/lint" --data '{"content": "{ \"image\": \"ruby:2.6\", \"services\": [\"postgres\"], \"before_script\": [\"bundle install\", \"bundle exec rake db:create\"], \"variables\": {\"DB_NAME\": \"postgres\"}, \"types\": [\"test\", \"deploy\", \"notify\"], \"rspec\": { \"script\": \"rake spec\", \"tags\": [\"ruby\", \"postgres\"], \"only\": [\"branches\"]}}"}'
```
Example responses:
- Valid configuration:
```json
{
"valid": true,
"merged_yaml": "---\n:test_job:\n :script: echo 1\n",
"errors": [],
"warnings": []
}
```
- Invalid configuration:
```json
{
"valid": false,
"merged_yaml": "---\n:test_job:\n :script: echo 1\n",
"errors": [
"jobs config should contain at least one visible job"
],
"warnings": []
}
```
## Validate a project's CI configuration
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231352) in GitLab 13.5.
......
......@@ -46,5 +46,25 @@ module API
present result, with: Entities::Ci::Lint::Result, current_user: current_user
end
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Validation of .gitlab-ci.yml content' do
detail 'This feature was introduced in GitLab 13.6.'
end
params do
requires :content, type: String, desc: 'Content of .gitlab-ci.yml'
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
end
post ':id/ci/lint' do
authorize! :download_code, user_project
result = Gitlab::Ci::Lint
.new(project: user_project, current_user: current_user)
.validate(params[:content], dry_run: params[:dry_run])
status 200
present result, with: Entities::Ci::Lint::Result, current_user: current_user
end
end
end
end
......@@ -275,4 +275,167 @@ RSpec.describe API::Lint do
end
end
end
describe 'POST /projects/:id/ci/lint' do
subject(:ci_lint) { post api("/projects/#{project.id}/ci/lint", api_user), params: { dry_run: dry_run, content: yaml_content } }
let(:project) { create(:project, :repository) }
let(:dry_run) { nil }
let_it_be(:api_user) { create(:user) }
let_it_be(:yaml_content) do
{ include: { local: 'another-gitlab-ci.yml' }, test: { stage: 'test', script: 'echo 1' } }.to_yaml
end
let_it_be(:included_content) do
{ another_test: { stage: 'test', script: 'echo 1' } }.to_yaml
end
RSpec.shared_examples 'valid project config' do
it 'passes validation' do
ci_lint
included_config = YAML.safe_load(included_content, [Symbol])
root_config = YAML.safe_load(yaml_content, [Symbol])
expected_yaml = included_config.merge(root_config).except(:include).to_yaml
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['merged_yaml']).to eq(expected_yaml)
expect(json_response['valid']).to eq(true)
expect(json_response['errors']).to eq([])
end
end
RSpec.shared_examples 'invalid project config' do
it 'responds with errors about invalid configuration' do
ci_lint
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['merged_yaml']).to eq(yaml_content)
expect(json_response['valid']).to eq(false)
expect(json_response['errors']).to eq(['jobs config should contain at least one visible job'])
end
end
context 'when unauthenticated' do
let_it_be(:api_user) { nil }
it 'returns authentication error' do
ci_lint
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when authenticated as non-member' do
context 'when project is private' do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
it 'returns authentication error' do
ci_lint
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when project is public' do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
context 'when running as dry run' do
let(:dry_run) { true }
it 'returns pipeline creation error' do
ci_lint
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['merged_yaml']).to eq(nil)
expect(json_response['valid']).to eq(false)
expect(json_response['errors']).to eq(['Insufficient permissions to create a new pipeline'])
end
end
context 'when running static validation' do
let(:dry_run) { false }
before do
project.repository.create_file(
project.creator,
'another-gitlab-ci.yml',
included_content,
message: 'Automatically created another-gitlab-ci.yml',
branch_name: 'master'
)
end
it_behaves_like 'valid project config'
end
end
end
context 'when authenticated as project guest' do
before do
project.add_guest(api_user)
end
it 'returns authentication error' do
ci_lint
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when authenticated as project developer' do
before do
project.add_developer(api_user)
end
context 'with valid .gitlab-ci.yml content' do
before do
project.repository.create_file(
project.creator,
'another-gitlab-ci.yml',
included_content,
message: 'Automatically created another-gitlab-ci.yml',
branch_name: 'master'
)
end
context 'when running as dry run' do
let(:dry_run) { true }
it_behaves_like 'valid project config'
end
context 'when running static validation' do
let(:dry_run) { false }
it_behaves_like 'valid project config'
end
end
context 'with invalid .gitlab-ci.yml content' do
let(:yaml_content) do
{ image: 'ruby:2.7', services: ['postgres'] }.to_yaml
end
context 'when running as dry run' do
let(:dry_run) { true }
it_behaves_like 'invalid project config'
end
context 'when running static validation' do
let(:dry_run) { false }
it_behaves_like 'invalid project config'
end
end
end
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