diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb index 0d9a6f568a1c8e5a25c70881d85a5d6d88305aa4..fed6307514e85ebc48ffd3deab09359b518580ce 100644 --- a/app/controllers/projects/static_site_editor_controller.rb +++ b/app/controllers/projects/static_site_editor_controller.rb @@ -3,6 +3,7 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController include ExtractsPath include CreatesCommit + include BlobHelper layout 'fullscreen' @@ -24,28 +25,7 @@ def index end def show - service_response = ::StaticSiteEditor::ConfigService.new( - container: project, - current_user: current_user, - params: { - ref: @ref, - path: @path, - return_url: params[:return_url] - } - ).execute - - if service_response.success? - Gitlab::UsageDataCounters::StaticSiteEditorCounter.increment_views_count - - @data = serialize_necessary_payload_values_to_json(service_response.payload) - else - # TODO: For now, if the service returns any error, the user is redirected - # to the root project page with the error message displayed as an alert. - # See https://gitlab.com/gitlab-org/gitlab/-/issues/213285#note_414808004 - # for discussion of plans to handle this via a page owned by the Static Site Editor. - flash[:alert] = service_response.message - redirect_to project_path(project) - end + redirect_to ide_edit_path(project, @ref, @path) end private diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md index 0fb285c50d61bced07401806174673c30599ac86..88efd1885db460146b7e39e8350161013ebe089a 100644 --- a/doc/administration/gitaly/configure_gitaly.md +++ b/doc/administration/gitaly/configure_gitaly.md @@ -300,7 +300,7 @@ disable enforcement. For more information, see the documentation on configuring ```toml listen_addr = '0.0.0.0:8075' - runtime_dir = '/var/opt/gitlab/gitaly' + internal_socket_dir = '/var/opt/gitlab/gitaly' [logging] format = 'json' @@ -308,9 +308,6 @@ disable enforcement. For more information, see the documentation on configuring dir = '/var/log/gitaly' ``` - For GitLab 14.9 and earlier, set `internal_socket_dir = '/var/opt/gitlab/gitaly'` instead - of `runtime_dir`. - 1. Append the following to `/home/git/gitaly/config.toml` for each respective Gitaly server: On `gitaly1.internal`: diff --git a/jest.config.base.js b/jest.config.base.js index 0eab5caffb003673960d90703b93c53e829487bb..59c127fcb8a4d86f06f77680039d687392a687c1 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -109,7 +109,7 @@ module.exports = (path, options = {}) => { return { clearMocks: true, testMatch, - moduleFileExtensions: ['js', 'json', 'vue', 'gql', 'graphql'], + moduleFileExtensions: ['js', 'json', 'vue', 'gql', 'graphql', 'yaml'], moduleNameMapper, collectCoverageFrom, coverageDirectory: coverageDirectory(), @@ -127,6 +127,7 @@ module.exports = (path, options = {}) => { '^.+_worker\\.js$': './spec/frontend/__helpers__/web_worker_transformer.js', '^.+\\.js$': 'babel-jest', '^.+\\.vue$': 'vue-jest', + '^.+\\.yml$': './spec/frontend/__helpers__/yaml_transformer.js', '^.+\\.(md|zip|png)$': 'jest-raw-loader', }, transformIgnorePatterns: [ diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb index a498e329c3fdf804f4a02fc81cf894add9e4c226..1389939f28babda619a8fc7258c538892b077262 100644 --- a/lib/gitlab/setup_helper.rb +++ b/lib/gitlab/setup_helper.rb @@ -124,9 +124,9 @@ def configuration_toml(gitaly_dir, storage_paths, options, gitaly_ruby: true) config[:storage] = storages - runtime_dir = options[:runtime_dir] || File.join(gitaly_dir, 'run') - FileUtils.mkdir(runtime_dir) unless File.exist?(runtime_dir) - config[:runtime_dir] = runtime_dir + internal_socket_dir = options[:internal_socket_dir] || File.join(gitaly_dir, 'internal_sockets') + FileUtils.mkdir(internal_socket_dir) unless File.exist?(internal_socket_dir) + config[:internal_socket_dir] = internal_socket_dir config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } diff --git a/package.json b/package.json index d7908b33ec9a957eaba8c4bc34eecd3810febc5c..fc8d43d6a1f62531ebb073c477f315cfc34593cc 100644 --- a/package.json +++ b/package.json @@ -206,6 +206,8 @@ "@types/jest": "^26.0.24", "@vue/test-utils": "1.3.0", "acorn": "^6.3.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", "axios-mock-adapter": "^1.15.0", "babel-jest": "^26.5.2", "babel-plugin-dynamic-import-node": "^2.3.3", diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb index 476f87fff6bf154456671bd0c5ec204a01dfb904..d90e11a168192170c1be3d4d37595477a3bee5f7 100644 --- a/qa/qa/tools/test_resources_handler.rb +++ b/qa/qa/tools/test_resources_handler.rb @@ -81,6 +81,7 @@ def download(ci_project_name) return puts "\nNothing to download!" if files_list.empty? + FileUtils.mkdir_p('tmp/') files_list.each do |file_name| local_path = "tmp/#{file_name.split('/').last}" Runtime::Logger.info("Downloading #{file_name} to #{local_path}") diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb index 26161b5fb5cbe6db8a29a5491446c480341c8622..e1f25589eeb9f6643e5b4e822f7dc9000d627d81 100644 --- a/spec/controllers/projects/static_site_editor_controller_spec.rb +++ b/spec/controllers/projects/static_site_editor_controller_spec.rb @@ -76,12 +76,11 @@ get :show, params: default_params end - it 'increases the views counter' do - expect(Gitlab::UsageDataCounters::StaticSiteEditorCounter).to have_received(:increment_views_count) - end + it 'redirects to the Web IDE' do + get :show, params: default_params - it 'renders the edit page' do - expect(response).to render_template(:show) + expected_path_regex = %r[-/ide/project/#{project.full_path}/edit/master/-/README.md] + expect(response).to redirect_to(expected_path_regex) end it 'assigns ref and path variables' do @@ -96,62 +95,6 @@ expect(response).to have_gitlab_http_status(:not_found) end end - - context 'when invalid config file' do - let(:service_response) { ServiceResponse.error(message: 'invalid') } - - it 'redirects to project page and flashes error message' do - expect(response).to redirect_to(project_path(project)) - expect(controller).to set_flash[:alert].to('invalid') - end - end - - context 'with a service response payload containing multiple data types' do - let(:data) do - { - a_string: 'string', - an_array: [ - { - foo: 'bar' - } - ], - an_integer: 123, - a_hash: { - a_deeper_hash: { - foo: 'bar' - } - }, - a_boolean: true, - a_nil: nil - } - end - - let(:assigns_data) { assigns(:data) } - - it 'leaves data values which are strings as strings' do - expect(assigns_data[:a_string]).to eq('string') - end - - it 'leaves data values which are integers as integers' do - expect(assigns_data[:an_integer]).to eq(123) - end - - it 'serializes data values which are booleans to JSON' do - expect(assigns_data[:a_boolean]).to eq('true') - end - - it 'serializes data values which are arrays to JSON' do - expect(assigns_data[:an_array]).to eq('[{"foo":"bar"}]') - end - - it 'serializes data values which are hashes to JSON' do - expect(assigns_data[:a_hash]).to eq('{"a_deeper_hash":{"foo":"bar"}}') - end - - it 'serializes data values which are nil to an empty string' do - expect(assigns_data[:a_nil]).to eq('') - end - end end end end diff --git a/spec/features/static_site_editor_spec.rb b/spec/features/static_site_editor_spec.rb deleted file mode 100644 index 98313905a334ba003ce0c7d8430a138241f51a5d..0000000000000000000000000000000000000000 --- a/spec/features/static_site_editor_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Static Site Editor' do - include ContentSecurityPolicyHelpers - - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :public, :repository) } - - let(:sse_path) { project_show_sse_path(project, 'master/README.md') } - - before_all do - project.add_developer(user) - end - - before do - sign_in(user) - end - - context "when no config file is present" do - before do - visit sse_path - end - - it 'renders SSE page with all generated config values and default config file values' do - node = page.find('#static-site-editor') - - # assert generated config values are present - expect(node['data-base-url']).to eq("/#{project.full_path}/-/sse/master%2FREADME.md") - expect(node['data-branch']).to eq('master') - expect(node['data-commit-id']).to match(/\A[0-9a-f]{40}\z/) - expect(node['data-is-supported-content']).to eq('true') - expect(node['data-merge-requests-illustration-path']) - .to match(%r{/assets/illustrations/merge_requests-.*\.svg}) - expect(node['data-namespace']).to eq(project.namespace.full_path) - expect(node['data-project']).to eq(project.path) - expect(node['data-project-id']).to eq(project.id.to_s) - - # assert default config file values are present - expect(node['data-image-upload-path']).to eq('source/images') - expect(node['data-mounts']).to eq('[{"source":"source","target":""}]') - expect(node['data-static-site-generator']).to eq('middleman') - end - end - - context "when a config file is present" do - let(:config_file_yml) do - <<~YAML - image_upload_path: custom-image-upload-path - mounts: - - source: source1 - target: "" - - source: source2 - target: target2 - static_site_generator: middleman - YAML - end - - before do - allow_next_instance_of(Repository) do |repository| - allow(repository).to receive(:blob_data_at).and_return(config_file_yml) - end - - visit sse_path - end - - it 'renders Static Site Editor page values read from config file' do - node = page.find('#static-site-editor') - - # assert user-specified config file values are present - expected_mounts = '[{"source":"source1","target":""},{"source":"source2","target":"target2"}]' - expect(node['data-image-upload-path']).to eq('custom-image-upload-path') - expect(node['data-mounts']).to eq(expected_mounts) - expect(node['data-static-site-generator']).to eq('middleman') - end - end - - describe 'Static Site Editor Content Security Policy' do - subject { response_headers['Content-Security-Policy'] } - - context 'when no global CSP config exists' do - before do - setup_csp_for_controller(Projects::StaticSiteEditorController) - end - - it 'does not add CSP directives' do - visit sse_path - - is_expected.to be_blank - end - end - - context 'when a global CSP config exists' do - let_it_be(:cdn_url) { 'https://some-cdn.test' } - let_it_be(:youtube_url) { 'https://www.youtube.com' } - - before do - csp = ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src :self, cdn_url - end - - setup_existing_csp_for_controller(Projects::StaticSiteEditorController, csp) - end - - it 'appends youtube to the CSP frame-src policy' do - visit sse_path - - is_expected.to eql("frame-src 'self' #{cdn_url} #{youtube_url}") - end - end - end -end diff --git a/spec/frontend/__helpers__/matchers/index.js b/spec/frontend/__helpers__/matchers/index.js index 76571bafb06ab65b2c09ae6760c9a98f2803eb1d..9b83ced10e1bb91c982c773ce960b0d7ee0f214d 100644 --- a/spec/frontend/__helpers__/matchers/index.js +++ b/spec/frontend/__helpers__/matchers/index.js @@ -1,3 +1,4 @@ export * from './to_have_sprite_icon'; export * from './to_have_tracking_attributes'; export * from './to_match_interpolated_text'; +export * from './to_validate_json_schema'; diff --git a/spec/frontend/__helpers__/matchers/to_validate_json_schema.js b/spec/frontend/__helpers__/matchers/to_validate_json_schema.js new file mode 100644 index 0000000000000000000000000000000000000000..ff391f08c55192d8cff5238057e814c03bc7b453 --- /dev/null +++ b/spec/frontend/__helpers__/matchers/to_validate_json_schema.js @@ -0,0 +1,34 @@ +// NOTE: Make sure to initialize ajv when using this helper + +const getAjvErrorMessage = ({ errors }) => { + return (errors || []).map((error) => { + return `Error with item ${error.instancePath}: ${error.message}`; + }); +}; + +export function toValidateJsonSchema(testData, validator) { + if (!(validator instanceof Function && validator.schema)) { + return { + validator, + message: () => + 'Validator must be a validating function with property "schema", created with `ajv.compile`. See https://ajv.js.org/api.html#ajv-compile-schema-object-data-any-boolean-promise-any.', + pass: false, + }; + } + + const isValid = validator(testData); + + return { + actual: testData, + message: () => { + if (isValid) { + // We can match, but still fail because we're in a `expect...not.` context + return 'Expected the given data not to pass the schema validation, but found that it was considered valid.'; + } + + const errorMessages = getAjvErrorMessage(validator).join('\n'); + return `Expected the given data to pass the schema validation, but found that it was considered invalid. Errors:\n${errorMessages}`; + }, + pass: isValid, + }; +} diff --git a/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js b/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..fd42c710c65993b7f1316d0dd1fda097c6f7b217 --- /dev/null +++ b/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js @@ -0,0 +1,65 @@ +import Ajv from 'ajv'; +import AjvFormats from 'ajv-formats'; + +const JSON_SCHEMA = { + type: 'object', + properties: { + fruit: { + type: 'string', + minLength: 3, + }, + }, +}; + +const ajv = new Ajv({ + strictTypes: false, + strictTuples: false, + allowMatchingProperties: true, +}); + +AjvFormats(ajv); +const schema = ajv.compile(JSON_SCHEMA); + +describe('custom matcher toValidateJsonSchema', () => { + it('throws error if validator is not compiled correctly', () => { + expect(() => { + expect({}).toValidateJsonSchema({}); + }).toThrow( + 'Validator must be a validating function with property "schema", created with `ajv.compile`. See https://ajv.js.org/api.html#ajv-compile-schema-object-data-any-boolean-promise-any.', + ); + }); + + describe('positive assertions', () => { + it.each` + description | input + ${'valid input'} | ${{ fruit: 'apple' }} + `('schema validation passes for $description', ({ input }) => { + expect(input).toValidateJsonSchema(schema); + }); + + it('throws if not matching', () => { + expect(() => expect(null).toValidateJsonSchema(schema)).toThrowError( + `Expected the given data to pass the schema validation, but found that it was considered invalid. Errors: +Error with item : must be object`, + ); + }); + }); + + describe('negative assertions', () => { + it.each` + description | input + ${'no input'} | ${null} + ${'input with invalid type'} | ${'banana'} + ${'input with invalid length'} | ${{ fruit: 'aa' }} + ${'input with invalid type'} | ${{ fruit: 12345 }} + `('schema validation fails for $description', ({ input }) => { + expect(input).not.toValidateJsonSchema(schema); + }); + + it('throws if matching', () => { + expect(() => expect({ fruit: 'apple' }).not.toValidateJsonSchema(schema)).toThrowError( + 'Expected the given data not to pass the schema validation, but found that it was considered valid.', + ); + }); + }); +}); diff --git a/spec/frontend/__helpers__/yaml_transformer.js b/spec/frontend/__helpers__/yaml_transformer.js new file mode 100644 index 0000000000000000000000000000000000000000..a23f9b1f71549cc78b0798d6d83e591ba1319d6c --- /dev/null +++ b/spec/frontend/__helpers__/yaml_transformer.js @@ -0,0 +1,11 @@ +/* eslint-disable import/no-commonjs */ +const JsYaml = require('js-yaml'); + +// This will transform YAML files to JSON strings +module.exports = { + process: (sourceContent) => { + const jsonContent = JsYaml.load(sourceContent); + const json = JSON.stringify(jsonContent); + return `module.exports = ${json}`; + }, +}; diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..628c34a27c178ce502d993a0f0f8c71eaeea9781 --- /dev/null +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -0,0 +1,90 @@ +import Ajv from 'ajv'; +import AjvFormats from 'ajv-formats'; +import CiSchema from '~/editor/schema/ci.json'; + +// JSON POSITIVE TESTS +import AllowFailureJson from './json_tests/positive_tests/allow_failure.json'; +import EnvironmentJson from './json_tests/positive_tests/environment.json'; +import GitlabCiDependenciesJson from './json_tests/positive_tests/gitlab-ci-dependencies.json'; +import GitlabCiJson from './json_tests/positive_tests/gitlab-ci.json'; +import InheritJson from './json_tests/positive_tests/inherit.json'; +import MultipleCachesJson from './json_tests/positive_tests/multiple-caches.json'; +import RetryJson from './json_tests/positive_tests/retry.json'; +import TerraformReportJson from './json_tests/positive_tests/terraform_report.json'; +import VariablesMixStringAndUserInputJson from './json_tests/positive_tests/variables_mix_string_and_user_input.json'; +import VariablesJson from './json_tests/positive_tests/variables.json'; + +// JSON NEGATIVE TESTS +import DefaultNoAdditionalPropertiesJson from './json_tests/negative_tests/default_no_additional_properties.json'; +import InheritDefaultNoAdditionalPropertiesJson from './json_tests/negative_tests/inherit_default_no_additional_properties.json'; +import JobVariablesMustNotContainObjectsJson from './json_tests/negative_tests/job_variables_must_not_contain_objects.json'; +import ReleaseAssetsLinksEmptyJson from './json_tests/negative_tests/release_assets_links_empty.json'; +import ReleaseAssetsLinksInvalidLinkTypeJson from './json_tests/negative_tests/release_assets_links_invalid_link_type.json'; +import ReleaseAssetsLinksMissingJson from './json_tests/negative_tests/release_assets_links_missing.json'; +import RetryUnknownWhenJson from './json_tests/negative_tests/retry_unknown_when.json'; + +// YAML POSITIVE TEST +import CacheYaml from './yaml_tests/positive_tests/cache.yml'; +import FilterYaml from './yaml_tests/positive_tests/filter.yml'; +import IncludeYaml from './yaml_tests/positive_tests/include.yml'; +import RulesYaml from './yaml_tests/positive_tests/rules.yml'; + +// YAML NEGATIVE TEST +import CacheNegativeYaml from './yaml_tests/negative_tests/cache.yml'; +import IncludeNegativeYaml from './yaml_tests/negative_tests/include.yml'; + +const ajv = new Ajv({ + strictTypes: false, + strictTuples: false, + allowMatchingProperties: true, +}); + +AjvFormats(ajv); +const schema = ajv.compile(CiSchema); + +describe('positive tests', () => { + it.each( + Object.entries({ + // JSON + AllowFailureJson, + EnvironmentJson, + GitlabCiDependenciesJson, + GitlabCiJson, + InheritJson, + MultipleCachesJson, + RetryJson, + TerraformReportJson, + VariablesMixStringAndUserInputJson, + VariablesJson, + + // YAML + CacheYaml, + FilterYaml, + IncludeYaml, + RulesYaml, + }), + )('schema validates %s', (_, input) => { + expect(input).toValidateJsonSchema(schema); + }); +}); + +describe('negative tests', () => { + it.each( + Object.entries({ + // JSON + DefaultNoAdditionalPropertiesJson, + JobVariablesMustNotContainObjectsJson, + InheritDefaultNoAdditionalPropertiesJson, + ReleaseAssetsLinksEmptyJson, + ReleaseAssetsLinksInvalidLinkTypeJson, + ReleaseAssetsLinksMissingJson, + RetryUnknownWhenJson, + + // YAML + CacheNegativeYaml, + IncludeNegativeYaml, + }), + )('schema validates %s', (_, input) => { + expect(input).not.toValidateJsonSchema(schema); + }); +}); diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json new file mode 100644 index 0000000000000000000000000000000000000000..955c19ef1ab9e20736e18850b79d04d8f7dcd09f --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json @@ -0,0 +1,12 @@ +{ + "default": { + "secrets": { + "DATABASE_PASSWORD": { + "vault": "production/db/password" + } + }, + "environment": { + "name": "test" + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json new file mode 100644 index 0000000000000000000000000000000000000000..7411e4c24342f657b07abd0e51207f4a04255a4a --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json @@ -0,0 +1,8 @@ +{ + "karma": { + "inherit": { + "default": ["secrets"] + }, + "script": "karma" + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json new file mode 100644 index 0000000000000000000000000000000000000000..bfdbf26ee70c3cd7baff5d6a5b3a8694013dc934 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json @@ -0,0 +1,12 @@ +{ + "gitlab-ci-variables-object": { + "stage": "test", + "script": ["true"], + "variables": { + "DEPLOY_ENVIRONMENT": { + "value": "staging", + "description": "The deployment target. Change this variable to 'canary' or 'production' if needed." + } + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json new file mode 100644 index 0000000000000000000000000000000000000000..84a1aa1469845e0395c3637ab71c461a0b91eedb --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json @@ -0,0 +1,13 @@ +{ + "gitlab-ci-release-assets-links-empty": { + "script": "dostuff", + "stage": "deploy", + "release": { + "description": "Created using the release-cli $EXTRA_DESCRIPTION", + "tag_name": "$CI_COMMIT_TAG", + "assets": { + "links": [] + } + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json new file mode 100644 index 0000000000000000000000000000000000000000..048911aefa3c34f73921ee3523a2e3a9b9284628 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json @@ -0,0 +1,24 @@ +{ + "gitlab-ci-release-assets-links-invalid-link-type": { + "script": "dostuff", + "stage": "deploy", + "release": { + "description": "Created using the release-cli $EXTRA_DESCRIPTION", + "tag_name": "$CI_COMMIT_TAG", + "assets": { + "links": [ + { + "name": "asset1", + "url": "https://example.com/assets/1" + }, + { + "name": "asset2", + "url": "https://example.com/assets/2", + "filepath": "/pretty/url/1", + "link_type": "invalid" + } + ] + } + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json new file mode 100644 index 0000000000000000000000000000000000000000..6f0b5a3bff8155d08b57ff4e5214e8309c54f53a --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json @@ -0,0 +1,11 @@ +{ + "gitlab-ci-release-assets-links-missing": { + "script": "dostuff", + "stage": "deploy", + "release": { + "description": "Created using the release-cli $EXTRA_DESCRIPTION", + "tag_name": "$CI_COMMIT_TAG", + "assets": {} + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json new file mode 100644 index 0000000000000000000000000000000000000000..433504f52c64084a0f047432473c7b8ccf8b0e4e --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json @@ -0,0 +1,9 @@ +{ + "gitlab-ci-retry-object-unknown-when": { + "stage": "test", + "script": "rspec", + "retry": { + "when": "gitlab-ci-retry-object-unknown-when" + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/allow_failure.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/allow_failure.json new file mode 100644 index 0000000000000000000000000000000000000000..44d42116c1ae79a7c1f975f6da9d2ee5ebe3afdb --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/allow_failure.json @@ -0,0 +1,19 @@ +{ + "job1": { + "stage": "test", + "script": ["execute_script_that_will_fail"], + "allow_failure": true + }, + "job2": { + "script": ["exit 1"], + "allow_failure": { + "exit_codes": 137 + } + }, + "job3": { + "script": ["exit 137"], + "allow_failure": { + "exit_codes": [137, 255] + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/environment.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..0c6f7935063e5b64f8047b95a37941d2f3fa480d --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/environment.json @@ -0,0 +1,75 @@ +{ + "deploy to production 1": { + "stage": "deploy", + "script": "git push production HEAD: master", + "environment": "production" + }, + "deploy to production 2": { + "stage": "deploy", + "script": "git push production HEAD:master", + "environment": { + "name": "production" + } + }, + "deploy to production 3": { + "stage": "deploy", + "script": "git push production HEAD:master", + "environment": { + "name": "production", + "url": "https://prod.example.com" + } + }, + "review_app 1": { + "stage": "deploy", + "script": "make deploy-app", + "environment": { + "name": "review/$CI_COMMIT_REF_NAME", + "url": "https://$CI_ENVIRONMENT_SLUG.example.com", + "on_stop": "stop_review_app" + } + }, + "stop_review_app": { + "stage": "deploy", + "variables": { + "GIT_STRATEGY": "none" + }, + "script": "make delete-app", + "when": "manual", + "environment": { + "name": "review/$CI_COMMIT_REF_NAME", + "action": "stop" + } + }, + "review_app 2": { + "script": "deploy-review-app", + "environment": { + "name": "review/$CI_COMMIT_REF_NAME", + "auto_stop_in": "1 day" + } + }, + "deploy 1": { + "stage": "deploy", + "script": "make deploy-app", + "environment": { + "name": "production", + "kubernetes": { + "namespace": "production" + } + } + }, + "deploy 2": { + "script": "echo", + "environment": { + "name": "customer-portal", + "deployment_tier": "production" + } + }, + "deploy as review app": { + "stage": "deploy", + "script": "make deploy", + "environment": { + "name": "review/$CI_COMMIT_REF_NAME", + "url": "https://$CI_ENVIRONMENT_SLUG.example.com/" + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci-dependencies.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci-dependencies.json new file mode 100644 index 0000000000000000000000000000000000000000..5ffa7fa799ece925bb69458aa0dfad2cce3246d2 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci-dependencies.json @@ -0,0 +1,68 @@ +{ + ".build_config": { + "before_script": ["echo test"] + }, + ".build_script": "echo build script", + "default": { + "image": "ruby:2.5", + "services": ["docker:dind"], + "cache": { + "paths": ["vendor/"] + }, + "before_script": ["bundle install --path vendor/"], + "after_script": ["rm -rf tmp/"] + }, + "stages": ["install", "build", "test", "deploy"], + "image": "foo:latest", + "install task1": { + "image": "node:latest", + "stage": "install", + "script": "npm install", + "artifacts": { + "paths": ["node_modules/"] + } + }, + "build dev": { + "image": "node:latest", + "stage": "build", + "needs": [ + { + "job": "install task1" + } + ], + "script": "npm run build:dev" + }, + "build prod": { + "image": "node:latest", + "stage": "build", + "needs": ["install task1"], + "script": "npm run build:prod" + }, + "test": { + "image": "node:latest", + "stage": "build", + "needs": [ + "install task1", + { + "job": "build dev", + "artifacts": true + } + ], + "script": "npm run test" + }, + "deploy it": { + "image": "node:latest", + "stage": "deploy", + "needs": [ + { + "job": "build dev", + "artifacts": false + }, + { + "job": "build prod", + "artifacts": true + } + ], + "script": "npm run test" + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json new file mode 100644 index 0000000000000000000000000000000000000000..89420bbc35fd0f899bfd36303e25910619319e98 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/gitlab-ci.json @@ -0,0 +1,350 @@ +{ + ".build_config": { + "before_script": ["echo test"] + }, + ".build_script": "echo build script", + ".example_variables": { + "foo": "hello", + "bar": 42 + }, + ".example_services": [ + "docker:dind", + { + "name": "sql:latest", + "command": ["/usr/bin/super-sql", "run"] + } + ], + "default": { + "image": "ruby:2.5", + "services": ["docker:dind"], + "cache": { + "paths": ["vendor/"] + }, + "before_script": ["bundle install --path vendor/"], + "after_script": ["rm -rf tmp/"], + "tags": ["ruby", "postgres"], + "artifacts": { + "name": "%CI_COMMIT_REF_NAME%", + "expose_as": "artifact 1", + "paths": ["path/to/file.txt", "target/*.war"], + "when": "on_failure" + }, + "retry": 2, + "timeout": "3 hours 30 minutes", + "interruptible": true + }, + "stages": ["build", "test", "deploy", "random"], + "image": "foo:latest", + "services": ["sql:latest"], + "before_script": ["echo test", "echo test2"], + "after_script": [], + "cache": { + "key": "asd", + "paths": ["dist/", ".foo"], + "untracked": false, + "policy": "pull" + }, + "variables": { + "STAGE": "yep", + "PROD": "nope" + }, + "include": [ + "https://gitlab.com/awesome-project/raw/master/.before-script-template.yml", + "/templates/.after-script-template.yml", + { "template": "Auto-DevOps.gitlab-ci.yml" }, + { + "project": "my-group/my-project", + "ref": "master", + "file": "/templates/.gitlab-ci-template.yml" + }, + { + "project": "my-group/my-project", + "ref": "master", + "file": ["/templates/.gitlab-ci-template.yml", "/templates/another-template-to-include.yml"] + } + ], + "build": { + "image": { + "name": "node:latest" + }, + "services": [], + "stage": "build", + "script": "npm run build", + "before_script": ["npm install"], + "rules": [ + { + "if": "moo", + "changes": ["Moofile"], + "exists": ["main.cow"], + "when": "delayed", + "start_in": "3 hours" + } + ], + "retry": { + "max": 1, + "when": "stuck_or_timeout_failure" + }, + "cache": { + "key": "$CI_COMMIT_REF_NAME", + "paths": ["node_modules/"], + "policy": "pull-push" + }, + "artifacts": { + "paths": ["dist/"], + "expose_as": "link_name_in_merge_request", + "name": "bundles", + "when": "on_success", + "expire_in": "1 week", + "reports": { + "junit": "result.xml", + "cobertura": "cobertura-coverage.xml", + "codequality": "codequality.json", + "sast": "sast.json", + "dependency_scanning": "scan.json", + "container_scanning": "scan2.json", + "dast": "dast.json", + "license_management": "license.json", + "performance": "performance.json", + "metrics": "metrics.txt" + } + }, + "variables": { + "FOO_BAR": "..." + }, + "only": { + "kubernetes": "active", + "variables": ["$FOO_BAR == '...'"], + "changes": ["/path/to/file", "/another/file"] + }, + "except": ["master", "tags"], + "tags": ["docker"], + "allow_failure": true, + "when": "manual" + }, + "error-report": { + "when": "on_failure", + "script": "report error", + "stage": "test" + }, + "test": { + "image": { + "name": "node:latest", + "entrypoint": [""] + }, + "stage": "test", + "script": "npm test", + "parallel": 5, + "retry": { + "max": 2, + "when": [ + "runner_system_failure", + "stuck_or_timeout_failure", + "script_failure", + "unknown_failure", + "always" + ] + }, + "artifacts": { + "reports": { + "junit": ["result.xml"], + "cobertura": ["cobertura-coverage.xml"], + "codequality": ["codequality.json"], + "sast": ["sast.json"], + "dependency_scanning": ["scan.json"], + "container_scanning": ["scan2.json"], + "dast": ["dast.json"], + "license_management": ["license.json"], + "performance": ["performance.json"], + "metrics": ["metrics.txt"] + } + }, + "coverage": "/Cycles: \\d+\\.\\d+$/", + "dependencies": [] + }, + "docker": { + "script": "docker build -t foo:latest", + "when": "delayed", + "start_in": "10 min", + "timeout": "1h", + "retry": 1, + "only": { + "changes": ["Dockerfile", "docker/scripts/*"] + } + }, + "deploy": { + "services": [ + { + "name": "sql:latest", + "entrypoint": [""], + "command": ["/usr/bin/super-sql", "run"], + "alias": "super-sql" + }, + "sql:latest", + { + "name": "sql:latest", + "alias": "default-sql" + } + ], + "script": "dostuff", + "stage": "deploy", + "environment": { + "name": "prod", + "url": "http://example.com", + "on_stop": "stop-deploy" + }, + "only": ["master"], + "release": { + "name": "Release $CI_COMMIT_TAG", + "description": "Created using the release-cli $EXTRA_DESCRIPTION", + "tag_name": "$CI_COMMIT_TAG", + "ref": "$CI_COMMIT_TAG", + "milestones": ["m1", "m2", "m3"], + "released_at": "2020-07-15T08:00:00Z", + "assets": { + "links": [ + { + "name": "asset1", + "url": "https://example.com/assets/1" + }, + { + "name": "asset2", + "url": "https://example.com/assets/2", + "filepath": "/pretty/url/1", + "link_type": "other" + }, + { + "name": "asset3", + "url": "https://example.com/assets/3", + "link_type": "runbook" + }, + { + "name": "asset4", + "url": "https://example.com/assets/4", + "link_type": "package" + }, + { + "name": "asset5", + "url": "https://example.com/assets/5", + "link_type": "image" + } + ] + } + } + }, + ".performance-tmpl": { + "after_script": ["echo after"], + "before_script": ["echo before"], + "variables": { + "SCRIPT_NOT_REQUIRED": "true" + } + }, + "performance-a": { + "extends": ".performance-tmpl", + "script": "echo test" + }, + "performance-b": { + "extends": ".performance-tmpl" + }, + "workflow": { + "rules": [ + { + "if": "$CI_COMMIT_REF_NAME =~ /-wip$/", + "when": "never" + }, + { + "if": "$CI_COMMIT_TAG", + "when": "never" + }, + { + "when": "always" + } + ] + }, + "job": { + "script": "echo Hello, Rules!", + "rules": [ + { + "if": "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == \"master\"", + "when": "manual", + "allow_failure": true + } + ] + }, + "microservice_a": { + "trigger": { + "include": "path/to/microservice_a.yml" + } + }, + "microservice_b": { + "trigger": { + "include": [{ "local": "path/to/microservice_b.yml" }, { "template": "SAST.gitlab-cy.yml" }], + "strategy": "depend" + } + }, + "child-pipeline": { + "stage": "test", + "trigger": { + "include": [ + { + "artifact": "generated-config.yml", + "job": "generate-config" + } + ] + } + }, + "child-pipeline-simple": { + "stage": "test", + "trigger": { + "include": "other/file.yml" + } + }, + "complex": { + "stage": "deploy", + "trigger": { + "project": "my/deployment", + "branch": "stable" + } + }, + "parallel-integer": { + "stage": "test", + "script": ["echo ${CI_NODE_INDEX} ${CI_NODE_TOTAL}"], + "parallel": 5 + }, + "parallel-matrix-simple": { + "stage": "test", + "script": ["echo ${MY_VARIABLE}"], + "parallel": { + "matrix": [ + { + "MY_VARIABLE": 0 + }, + { + "MY_VARIABLE": "sample" + }, + { + "MY_VARIABLE": ["element0", 1, "element2"] + } + ] + } + }, + "parallel-matrix-gitlab-docs": { + "stage": "deploy", + "script": ["bin/deploy"], + "parallel": { + "matrix": [ + { + "PROVIDER": "aws", + "STACK": ["app1", "app2"] + }, + { + "PROVIDER": "ovh", + "STACK": ["monitoring", "backup", "app"] + }, + { + "PROVIDER": ["gcp", "vultr"], + "STACK": ["data", "processing"] + } + ] + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/inherit.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/inherit.json new file mode 100644 index 0000000000000000000000000000000000000000..3f72afa6ceb93dff881363d96de7a00134fd86e9 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/inherit.json @@ -0,0 +1,54 @@ +{ + "default": { + "image": "ruby:2.4", + "before_script": ["echo Hello World"] + }, + "variables": { + "DOMAIN": "example.com", + "WEBHOOK_URL": "https://my-webhook.example.com" + }, + "rubocop": { + "inherit": { + "default": false, + "variables": false + }, + "script": "bundle exec rubocop" + }, + "rspec": { + "inherit": { + "default": ["image"], + "variables": ["WEBHOOK_URL"] + }, + "script": "bundle exec rspec" + }, + "capybara": { + "inherit": { + "variables": false + }, + "script": "bundle exec capybara" + }, + "karma": { + "inherit": { + "default": true, + "variables": ["DOMAIN"] + }, + "script": "karma" + }, + "inherit literally all": { + "inherit": { + "default": [ + "after_script", + "artifacts", + "before_script", + "cache", + "image", + "interruptible", + "retry", + "services", + "tags", + "timeout" + ] + }, + "script": "true" + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/multiple-caches.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/multiple-caches.json new file mode 100644 index 0000000000000000000000000000000000000000..360938e5ce7433050e05b3e775407af9296ac6b5 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/multiple-caches.json @@ -0,0 +1,24 @@ +{ + "test-job": { + "stage": "build", + "cache": [ + { + "key": { + "files": ["Gemfile.lock"] + }, + "paths": ["vendor/ruby"] + }, + { + "key": { + "files": ["yarn.lock"] + }, + "paths": [".yarn-cache/"] + } + ], + "script": [ + "bundle install --path=vendor", + "yarn install --cache-folder .yarn-cache", + "echo Run tests..." + ] + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/retry.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/retry.json new file mode 100644 index 0000000000000000000000000000000000000000..1337e5e7bc84a2d1de908ded71996cdb996095b2 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/retry.json @@ -0,0 +1,60 @@ +{ + "gitlab-ci-retry-int": { + "stage": "test", + "script": "rspec", + "retry": 2 + }, + "gitlab-ci-retry-object-no-max": { + "stage": "test", + "script": "rspec", + "retry": { + "when": "runner_system_failure" + } + }, + "gitlab-ci-retry-object-single-when": { + "stage": "test", + "script": "rspec", + "retry": { + "max": 2, + "when": "runner_system_failure" + } + }, + "gitlab-ci-retry-object-multiple-when": { + "stage": "test", + "script": "rspec", + "retry": { + "max": 2, + "when": ["runner_system_failure", "stuck_or_timeout_failure"] + } + }, + "gitlab-ci-retry-object-multiple-when-dupes": { + "stage": "test", + "script": "rspec", + "retry": { + "max": 2, + "when": ["runner_system_failure", "runner_system_failure"] + } + }, + "gitlab-ci-retry-object-all-when": { + "stage": "test", + "script": "rspec", + "retry": { + "max": 2, + "when": [ + "always", + "unknown_failure", + "script_failure", + "api_failure", + "stuck_or_timeout_failure", + "runner_system_failure", + "runner_unsupported", + "stale_schedule", + "job_execution_timeout", + "archived_failure", + "unmet_prerequisites", + "scheduler_failure", + "data_integrity_failure" + ] + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/terraform_report.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/terraform_report.json new file mode 100644 index 0000000000000000000000000000000000000000..0e444a4ba620709822cdf0df7b0a153e39ec1eaa --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/terraform_report.json @@ -0,0 +1,50 @@ +{ + "image": { + "name": "registry.gitlab.com/gitlab-org/gitlab-build-images:terraform", + "entrypoint": [ + "/usr/bin/env", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ] + }, + "variables": { + "PLAN": "plan.tfplan", + "JSON_PLAN_FILE": "tfplan.json" + }, + "cache": { + "paths": [".terraform"] + }, + "before_script": [ + "alias convert_report=\"jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'\"", + "terraform --version", + "terraform init" + ], + "stages": ["validate", "build", "test", "deploy"], + "validate": { + "stage": "validate", + "script": ["terraform validate"] + }, + "plan": { + "stage": "build", + "script": [ + "terraform plan -out=$PLAN", + "terraform show --json $PLAN | convert_report > $JSON_PLAN_FILE" + ], + "artifacts": { + "name": "plan", + "paths": ["$PLAN"], + "reports": { + "terraform": "$JSON_PLAN_FILE" + } + } + }, + "apply": { + "stage": "deploy", + "environment": { + "name": "production" + }, + "script": ["terraform apply -input=false $PLAN"], + "dependencies": ["plan"], + "when": "manual", + "only": ["master"] + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables.json new file mode 100644 index 0000000000000000000000000000000000000000..ce59b3fbbec63ecc038777001aa74447faceeb05 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables.json @@ -0,0 +1,22 @@ +{ + "variables": { + "DEPLOY_ENVIRONMENT": { + "value": "staging", + "description": "The deployment target. Change this variable to 'canary' or 'production' if needed." + } + }, + "gitlab-ci-variables-string": { + "stage": "test", + "script": ["true"], + "variables": { + "TEST_VAR": "String variable" + } + }, + "gitlab-ci-variables-integer": { + "stage": "test", + "script": ["true"], + "variables": { + "canonical": 685230 + } + } +} diff --git a/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables_mix_string_and_user_input.json b/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables_mix_string_and_user_input.json new file mode 100644 index 0000000000000000000000000000000000000000..87a9ec05b575f8fbd582d2b59e7378b54941cce6 --- /dev/null +++ b/spec/frontend/editor/schema/ci/json_tests/positive_tests/variables_mix_string_and_user_input.json @@ -0,0 +1,10 @@ +{ + "variables": { + "SOME_STR": "--batch-mode --errors --fail-at-end --show-version", + "SOME_INT": 10, + "SOME_USER_INPUT_FLAG": { + "value": "flag value", + "description": "Some Flag!" + } + } +} diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml new file mode 100644 index 0000000000000000000000000000000000000000..ee533f54d3bcc1ce37d02f8bd8a0d29201f2f2a6 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml @@ -0,0 +1,15 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70779 +stages: + - prepare + +# invalid cache:when value +job1: + stage: prepare + cache: + when: 0 + +# invalid cache:when value +job2: + stage: prepare + cache: + when: 'never' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml new file mode 100644 index 0000000000000000000000000000000000000000..287150a765fb314e11a785e3cf71a0d9bba926a1 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/include.yml @@ -0,0 +1,17 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70779 +stages: + - prepare + +# missing file property +childPipeline: + stage: prepare + trigger: + include: + - project: 'my-group/my-pipeline-library' + +# missing project property +childPipeline2: + stage: prepare + trigger: + include: + - file: '.gitlab-ci.yml' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml new file mode 100644 index 0000000000000000000000000000000000000000..436c7d7269971a024a0163c7591d2b4e8c09186f --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/cache.yml @@ -0,0 +1,25 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70779 +stages: + - prepare + +# test for cache:when values +job1: + stage: prepare + script: + - echo 'running job' + cache: + when: 'on_success' + +job2: + stage: prepare + script: + - echo 'running job' + cache: + when: 'on_failure' + +job3: + stage: prepare + script: + - echo 'running job' + cache: + when: 'always' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/filter.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/filter.yml new file mode 100644 index 0000000000000000000000000000000000000000..2b29c24fa3ca823c8a9df5568f0b41e2985490aa --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/filter.yml @@ -0,0 +1,18 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79335 +deploy-template: + script: + - echo "hello world" + only: + - foo + except: + - bar + +# null value allowed +deploy-without-only: + extends: deploy-template + only: + +# null value allowed +deploy-without-except: + extends: deploy-template + except: diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/include.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/include.yml new file mode 100644 index 0000000000000000000000000000000000000000..3497be28058e442c59e306597d68a17f3c9e6782 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/include.yml @@ -0,0 +1,32 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70779 + +# test for include:rules +include: + - local: builds.yml + rules: + - if: '$INCLUDE_BUILDS == "true"' + when: always + +stages: + - prepare + +# test for trigger:include +childPipeline: + stage: prepare + script: + - echo 'creating pipeline...' + trigger: + include: + - project: 'my-group/my-pipeline-library' + file: '.gitlab-ci.yml' + +# accepts optional ref property +childPipeline2: + stage: prepare + script: + - echo 'creating pipeline...' + trigger: + include: + - project: 'my-group/my-pipeline-library' + file: '.gitlab-ci.yml' + ref: 'main' diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/rules.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/rules.yml new file mode 100644 index 0000000000000000000000000000000000000000..27a199cff1392177a2990cdbd691b94c4b21686e --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/rules.yml @@ -0,0 +1,13 @@ +# Covers https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74164 + +# test for workflow:rules:changes and workflow:rules:exists +workflow: + rules: + - if: '$CI_PIPELINE_SOURCE == "schedule"' + exists: + - Dockerfile + changes: + - Dockerfile + variables: + IS_A_FEATURE: 'true' + when: always diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb index 0ad83bdeeb24571e2c6a30b2dafb1ae4f9825350..a4ee618457da3b5ae30f237bcaca81ad3a3d1b2b 100644 --- a/spec/support/helpers/gitaly_setup.rb +++ b/spec/support/helpers/gitaly_setup.rb @@ -267,7 +267,7 @@ def setup_gitaly { 'default' => repos_path }, force: true, options: { - runtime_dir: File.join(gitaly_dir, "run2"), + internal_socket_dir: File.join(gitaly_dir, "internal_gitaly2"), gitaly_socket: "gitaly2.socket", config_filename: "gitaly2.config.toml" } diff --git a/yarn.lock b/yarn.lock index 9a1ed097f381626d2403683aa6446801c515d9c8..239cdab66924a7525be8ecfe7eb972983c14445f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2516,10 +2516,10 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.8.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" - integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.10.0, ajv@^8.8.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0"