From 4c7e34071eceb05a9ce271354c21de7487e4ff84 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 22 Mar 2022 09:07:15 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- .../projects/default_project_templates.js | 6 +-- app/services/ci/register_job_service.rb | 3 +- app/services/web_hook_service.rb | 16 ++----- app/views/shared/hook_logs/_content.html.haml | 14 +++--- .../runners/_runner_type_alert.html.haml | 26 +++++------ doc/api/remote_mirrors.md | 20 ++++++++ doc/user/project/integrations/overview.md | 8 ++-- lib/api/remote_mirrors.rb | 23 +++++++++ lib/gitlab/project_template.rb | 2 +- locale/gitlab.pot | 18 +++---- package.json | 1 + .../create_merge_request_via_template_spec.rb | 2 +- .../revert/revert_commit_spec.rb | 2 +- .../file/create_file_via_web_spec.rb | 2 +- .../repository/file/edit_file_via_web_spec.rb | 2 +- ...roject_snippet_with_multiple_files_spec.rb | 2 +- spec/lib/gitlab/project_template_spec.rb | 2 +- spec/requests/api/remote_mirrors_spec.rb | 40 ++++++++++++++++ spec/services/ci/register_job_service_spec.rb | 14 ++++++ spec/services/web_hook_service_spec.rb | 15 ++++++ vendor/project_templates/middleman.tar.gz | Bin 0 -> 10917 bytes vendor/project_templates/sse_middleman.tar.gz | Bin 7368 -> 0 bytes yarn.lock | 44 ++++++++++++++++++ 24 files changed, 203 insertions(+), 61 deletions(-) create mode 100644 vendor/project_templates/middleman.tar.gz delete mode 100644 vendor/project_templates/sse_middleman.tar.gz diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 99cda256539..e015c905aa3 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -442277b91f5aa2822d52b810fd462a41c6cf5a0a +f21e9469e94600f50ecb01b98d46f54dd7b33b5c diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js index 0393d82ca36..6708b7bd9e2 100644 --- a/app/assets/javascripts/projects/default_project_templates.js +++ b/app/assets/javascripts/projects/default_project_templates.js @@ -57,9 +57,9 @@ export default { text: s__('ProjectTemplates|Pages/Hexo'), icon: '.template-option .icon-hexo', }, - sse_middleman: { - text: s__('ProjectTemplates|Static Site Editor/Middleman'), - icon: '.template-option .icon-sse_middleman', + middleman: { + text: s__('ProjectTemplates|Pages/Middleman'), + icon: '.template-option .icon-middleman', }, gitpod_spring_petclinic: { text: s__('ProjectTemplates|Gitpod/Spring Petclinic'), diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index 59c4c17a964..184db7516eb 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -271,7 +271,8 @@ module Ci runner_unsupported: -> (build, params) { !build.supported_runner?(params.dig(:info, :features)) }, archived_failure: -> (build, _) { build.archived? }, project_deleted: -> (build, _) { build.project.pending_delete? }, - builds_disabled: -> (build, _) { !build.project.builds_enabled? } + builds_disabled: -> (build, _) { !build.project.builds_enabled? }, + user_blocked: -> (build, _) { build.user&.blocked? } } end end diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index b1d8872aa5e..c0727e52cc3 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -36,7 +36,7 @@ class WebHookService def initialize(hook, data, hook_name, uniqueness_token = nil, force: false) @hook = hook - @data = data + @data = data.to_h @hook_name = hook_name.to_s @uniqueness_token = uniqueness_token @force = force @@ -70,9 +70,6 @@ class WebHookService end log_execution( - trigger: hook_name, - url: hook.url, - request_data: data, response: response, execution_duration: Gitlab::Metrics::System.monotonic_time - start_time ) @@ -86,9 +83,6 @@ class WebHookService Gitlab::Json::LimitedEncoder::LimitExceeded, URI::InvalidURIError => e execution_duration = Gitlab::Metrics::System.monotonic_time - start_time log_execution( - trigger: hook_name, - url: hook.url, - request_data: data, response: InternalErrorResponse.new, execution_duration: execution_duration, error_message: e.to_s @@ -139,14 +133,14 @@ class WebHookService make_request(post_url, basic_auth) end - def log_execution(trigger:, url:, request_data:, response:, execution_duration:, error_message: nil) + def log_execution(response:, execution_duration:, error_message: nil) category = response_category(response) log_data = { - trigger: trigger, - url: url, + trigger: hook_name, + url: hook.url, execution_duration: execution_duration, request_headers: build_headers, - request_data: request_data, + request_data: data, response_headers: format_response_headers(response), response_body: safe_response_body(response), response_status: response.code, diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml index 95590d6e515..59625eb33ea 100644 --- a/app/views/shared/hook_logs/_content.html.haml +++ b/app/views/shared/hook_logs/_content.html.haml @@ -10,14 +10,12 @@ %hr - if hook_log.internal_error_message.present? - .gl-alert-container - .gl-alert.gl-alert-danger - .gl-alert-container - = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title') - .gl-alert-content - %h4.gl-alert-title= _('Internal error occurred while delivering this webhook.') - .gl-alert-body - = _('Error: %{error}') % { error: hook_log.internal_error_message } + = render 'shared/global_alert', + title: _('Internal error occurred while delivering this webhook.'), + variant: :danger, + dismissible: false do + .gl-alert-body + = _('Error: %{error}') % { error: hook_log.internal_error_message } %h4= _('Response') = render partial: 'shared/hook_logs/status_label', locals: { hook_log: hook_log } diff --git a/app/views/shared/runners/_runner_type_alert.html.haml b/app/views/shared/runners/_runner_type_alert.html.haml index e0cc1e924d8..9fd55e215e2 100644 --- a/app/views/shared/runners/_runner_type_alert.html.haml +++ b/app/views/shared/runners/_runner_type_alert.html.haml @@ -1,20 +1,18 @@ -.gl-alert.gl-alert-info.gl-my-5 - = sprite_icon('information-o', css_class: 'gl-alert-icon') - - if runner.instance_type? - %h4.gl-alert-title - = s_('Runners|This runner is available to all groups and projects in your GitLab instance.') - .gl-alert-body - = s_('Runners|Shared runners are available to every project in a GitLab instance. If you want a runner to build only specific projects, restrict the project in the table below. After you restrict a runner to a project, you cannot change it back to a shared runner.') - = link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'shared-runners'), target: '_blank', rel: 'noopener noreferrer' - - elsif runner.group_type? - %h4.gl-alert-title - = s_('Runners|This runner is available to all projects and subgroups in a group.') +- alert_class = 'gl-mb-5' + +- if runner.group_type? + = render 'shared/global_alert', + alert_class: alert_class, + title: s_('Runners|This runner is available to all projects and subgroups in a group.'), + dismissible: false do .gl-alert-body = s_('Runners|Use Group runners when you want all projects in a group to have access to a set of runners.') = link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'group-runners'), target: '_blank', rel: 'noopener noreferrer' - - else - %h4.gl-alert-title - = s_('Runners|This runner is associated with specific projects.') +- else + = render 'shared/global_alert', + alert_class: alert_class, + title: s_('Runners|This runner is associated with specific projects.'), + dismissible: false do .gl-alert-body = s_('Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared runner.') = link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'specific-runners'), target: '_blank', rel: 'noopener noreferrer' diff --git a/doc/api/remote_mirrors.md b/doc/api/remote_mirrors.md index 8b584285033..dbe4970b5a9 100644 --- a/doc/api/remote_mirrors.md +++ b/doc/api/remote_mirrors.md @@ -136,3 +136,23 @@ Example response: "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" } ``` + +## Delete a remote mirror + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82778) in GitLab 14.10. + +Delete a remote mirror. + +```plaintext +DELETE /projects/:id/remote_mirrors/:mirror_id +``` + +| Attribute | Type | Required | Description | +| :---------- | :----- | :--------- |:------------------| +| `mirror_id` | Integer | yes | Remote mirror ID. | + +Example request: + +```shell +curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/42/remote_mirrors/101486" +``` diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md index c91d59cc783..081780e6277 100644 --- a/doc/user/project/integrations/overview.md +++ b/doc/user/project/integrations/overview.md @@ -22,9 +22,9 @@ want to configure. ## Integrations listing -Click on the service links to see further configuration instructions and details. +Click on the integration links to see further configuration instructions and details. -| Service | Description | Service hooks | +| Integration | Description | Integration hooks | | --------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------- | | [Asana](asana.md) | Add commit messages as comments to Asana tasks. | **{dotted-circle}** No | | Assembla | Manage projects. | **{dotted-circle}** No | @@ -69,7 +69,7 @@ Click on the service links to see further configuration instructions and details > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17874) in GitLab 12.4. -If a single push includes changes to more than three branches or tags, services +If a single push includes changes to more than three branches or tags, integrations supported by `push_hooks` and `tag_push_hooks` events aren't executed. The number of branches or tags supported can be changed via @@ -94,7 +94,7 @@ and some integrations. ## Troubleshooting integrations -Some integrations use service hooks for integration with external applications. To confirm which ones use service hooks, see the [integrations listing](#integrations-listing) above. Learn more about [troubleshooting service hooks](webhooks.md#troubleshoot-webhooks). +Some integrations use hooks for integration with external applications. To confirm which ones use integration hooks, see the [integrations listing](#integrations-listing) above. Learn more about [troubleshooting integration hooks](webhooks.md#troubleshoot-webhooks). ### Uninitialized repositories diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb index 83096772d32..cc9d1997d92 100644 --- a/lib/api/remote_mirrors.rb +++ b/lib/api/remote_mirrors.rb @@ -73,6 +73,29 @@ module API render_api_error!(result[:message], result[:http_status]) end end + + desc 'Delete a single remote mirror' do + detail 'This feature was introduced in GitLab 14.10' + end + params do + requires :mirror_id, type: String, desc: 'The ID of a remote mirror' + end + delete ':id/remote_mirrors/:mirror_id' do + mirror = user_project.remote_mirrors.find(params[:mirror_id]) + + destroy_conditionally!(mirror) do + mirror_params = declared_params(include_missing: false).merge(_destroy: 1) + mirror_params[:id] = mirror_params.delete(:mirror_id) + update_params = { remote_mirrors_attributes: mirror_params } + + # Note: We are using the update service to be consistent with how the controller handles deletion + result = ::Projects::UpdateService.new(user_project, current_user, update_params).execute + + if result[:status] != :success + render_api_error!(result[:message], 400) + end + end + end end end end diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb index 847f70693f3..629f49563c9 100644 --- a/lib/gitlab/project_template.rb +++ b/lib/gitlab/project_template.rb @@ -57,7 +57,7 @@ module Gitlab ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML'), 'https://gitlab.com/pages/plain-html'), ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'), ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'), - ProjectTemplate.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman', 'illustrations/logos/middleman.svg'), + ProjectTemplate.new('middleman', 'Pages/Middleman', _('Everything you need to create a GitLab Pages site using Middleman'), 'https://gitlab.com/gitlab-org/project-templates/middleman', 'illustrations/logos/middleman.svg'), ProjectTemplate.new('gitpod_spring_petclinic', 'Gitpod/Spring Petclinic', _('A Gitpod configured Webapplication in Spring and Java'), 'https://gitlab.com/gitlab-org/project-templates/gitpod-spring-petclinic', 'illustrations/logos/gitpod.svg'), ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'), ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'), diff --git a/locale/gitlab.pot b/locale/gitlab.pot index aea718dab5c..3efa9efe8d7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -14918,6 +14918,9 @@ msgstr "" msgid "Everything you need to create a GitLab Pages site using Jekyll" msgstr "" +msgid "Everything you need to create a GitLab Pages site using Middleman" +msgstr "" + msgid "Everything you need to create a GitLab Pages site using plain HTML" msgstr "" @@ -23915,9 +23918,6 @@ msgstr "" msgid "Mi" msgstr "" -msgid "Middleman project with Static Site Editor support" -msgstr "" - msgid "Migrated %{success_count}/%{total_count} files." msgstr "" @@ -29491,6 +29491,9 @@ msgstr "" msgid "ProjectTemplates|Pages/Jekyll" msgstr "" +msgid "ProjectTemplates|Pages/Middleman" +msgstr "" + msgid "ProjectTemplates|Pages/Plain HTML" msgstr "" @@ -29509,9 +29512,6 @@ msgstr "" msgid "ProjectTemplates|Spring" msgstr "" -msgid "ProjectTemplates|Static Site Editor/Middleman" -msgstr "" - msgid "ProjectTemplates|Tencent Serverless Framework/NextjsSSR" msgstr "" @@ -32195,9 +32195,6 @@ msgstr "" msgid "Runners|Select your preferred option here. In the next step, you can choose the capacity for your runner in the AWS CloudFormation console." msgstr "" -msgid "Runners|Shared runners are available to every project in a GitLab instance. If you want a runner to build only specific projects, restrict the project in the table below. After you restrict a runner to a project, you cannot change it back to a shared runner." -msgstr "" - msgid "Runners|Show runner installation and registration instructions" msgstr "" @@ -32240,9 +32237,6 @@ msgstr "" msgid "Runners|This runner is associated with specific projects." msgstr "" -msgid "Runners|This runner is available to all groups and projects in your GitLab instance." -msgstr "" - msgid "Runners|This runner is available to all projects and subgroups in a group." msgstr "" diff --git a/package.json b/package.json index 7b634e18600..604df6436c6 100644 --- a/package.json +++ b/package.json @@ -204,6 +204,7 @@ "@gitlab/stylelint-config": "4.0.0", "@graphql-eslint/eslint-plugin": "3.0.0", "@testing-library/dom": "^7.16.2", + "@types/jest": "^26.0.24", "@vue/test-utils": "1.3.0", "acorn": "^6.3.0", "axios-mock-adapter": "^1.15.0", diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb index c4aacd8fb06..3373f4f4233 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', :reliable do describe 'Merge request custom templates' do let(:template_name) { 'custom_merge_request_template'} let(:template_content) { 'This is a custom merge request template test' } diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb index a2b27e294e6..8885163b5e3 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', :reliable do describe 'Reverting a commit' do let(:file_name) { "secret_file.md" } diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb index f335cfdb367..095444d99f1 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', :reliable do context 'File management' do file_name = 'QA Test - File name' file_content = 'QA Test - File content' diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb index 25c095d9eda..95e7a2a12d0 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', :reliable do context 'File management' do let(:file) { Resource::File.fabricate_via_api! } diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb index 70891ec72c7..77b3c4df7e1 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', :reliable do describe 'Multiple file snippet' do let(:snippet) do Resource::ProjectSnippet.fabricate_via_browser_ui! do |snippet| diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb index 05417e721c7..0ef52b63bc6 100644 --- a/spec/lib/gitlab/project_template_spec.rb +++ b/spec/lib/gitlab/project_template_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::ProjectTemplate do expected = %w[ rails spring express iosswift dotnetcore android gomicro gatsby hugo jekyll plainhtml gitbook - hexo sse_middleman gitpod_spring_petclinic nfhugo + hexo middleman gitpod_spring_petclinic nfhugo nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx serverless_framework tencent_serverless_framework jsonnet cluster_management kotlin_native_linux diff --git a/spec/requests/api/remote_mirrors_spec.rb b/spec/requests/api/remote_mirrors_spec.rb index 436efb708fd..0df4c797630 100644 --- a/spec/requests/api/remote_mirrors_spec.rb +++ b/spec/requests/api/remote_mirrors_spec.rb @@ -99,4 +99,44 @@ RSpec.describe API::RemoteMirrors do expect(json_response['keep_divergent_refs']).to eq(true) end end + + describe 'DELETE /projects/:id/remote_mirrors/:mirror_id' do + let(:route) { ->(id) { "/projects/#{project.id}/remote_mirrors/#{id}" } } + let(:mirror) { project.remote_mirrors.first } + + it 'requires `admin_remote_mirror` permission' do + expect { delete api(route[mirror.id], developer) }.not_to change { project.remote_mirrors.count } + + expect(response).to have_gitlab_http_status(:unauthorized) + end + + context 'when the user is a maintainer' do + before do + project.add_maintainer(user) + end + + it 'returns 404 for non existing id' do + delete api(route[non_existing_record_id], user) + + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'returns bad request if the update service fails' do + expect_next_instance_of(Projects::UpdateService) do |service| + expect(service).to receive(:execute).and_return(status: :error, message: 'message') + end + + expect { delete api(route[mirror.id], user) }.not_to change { project.remote_mirrors.count } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq({ 'message' => 'message' }) + end + + it 'deletes a remote mirror' do + expect { delete api(route[mirror.id], user) }.to change { project.remote_mirrors.count }.from(1).to(0) + + expect(response).to have_gitlab_http_status(:no_content) + end + end + end end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 2127a4fa0fc..2e1f86605e8 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -103,6 +103,20 @@ module Ci pending_job.create_queuing_entry! end + context 'when build owner has been blocked' do + let(:user) { create(:user, :blocked) } + + before do + pending_job.update!(user: user) + end + + it 'does not pick the build and drops the build' do + expect(execute(shared_runner)).to be_falsey + + expect(pending_job.reload).to be_user_blocked + end + end + context 'for multiple builds' do let!(:project2) { create :project, shared_runners_enabled: true } let!(:pipeline2) { create :ci_pipeline, project: project2 } diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index c938ad9ee39..b99bc860523 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -107,6 +107,21 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state ).once end + context 'when the data is a Gitlab::DataBuilder::Pipeline' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:data) { ::Gitlab::DataBuilder::Pipeline.new(pipeline) } + + it 'can log the request payload' do + stub_full_request(project_hook.url, method: :post) + + # we call this with force to ensure that the logs are written inline, + # which tests that we can serialize the data to the DB correctly. + service = described_class.new(project_hook, data, :push_hooks, force: true) + + expect { service.execute }.to change(::WebHookLog, :count).by(1) + end + end + context 'when auth credentials are present' do let_it_be(:url) {'https://example.org'} let_it_be(:project_hook) { create(:project_hook, url: 'https://demo:demo@example.org/') } diff --git a/vendor/project_templates/middleman.tar.gz b/vendor/project_templates/middleman.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..41496d448b1ecb3ae584a5040939a8b8e2e64e63 GIT binary patch literal 10917 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