diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 2ae82b49609..06ba16f9724 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -60,9 +60,32 @@ module Emails @project = project @alert = alert.present + add_project_headers + add_alert_headers + subject_text = "Alert: #{@alert.email_title}" mail(to: user.notification_email_for(@project.group), subject: subject(subject_text)) end + + private + + def add_alert_headers + return unless @alert + + headers['X-GitLab-Alert-ID'] = @alert.id + headers['X-GitLab-Alert-IID'] = @alert.iid + headers['X-GitLab-NotificationReason'] = "alert_#{@alert.state}" + + add_incident_headers + end + + def add_incident_headers + incident = @alert.issue + return unless incident + + headers['X-GitLab-Incident-ID'] = incident.id + headers['X-GitLab-Incident-IID'] = incident.iid + end end end diff --git a/app/models/integrations/irker.rb b/app/models/integrations/irker.rb index 7048dd641ea..cea4aa2038d 100644 --- a/app/models/integrations/irker.rb +++ b/app/models/integrations/irker.rb @@ -4,6 +4,8 @@ require 'uri' module Integrations class Irker < Integration + include ActionView::Helpers::UrlHelper + prop_accessor :server_host, :server_port, :default_irc_uri prop_accessor :recipients, :channels boolean_accessor :colorize_messages @@ -12,11 +14,11 @@ module Integrations before_validation :get_channels def title - 'Irker (IRC gateway)' + s_('IrkerService|irker (IRC gateway)') end def description - 'Send IRC messages.' + s_('IrkerService|Send update messages to an irker server.') end def self.to_param @@ -42,33 +44,25 @@ module Integrations end def fields + recipients_docs_link = link_to s_('IrkerService|How to enter channels or users?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/irker', anchor: 'enter-irker-recipients'), target: '_blank', rel: 'noopener noreferrer' [ - { type: 'text', name: 'server_host', placeholder: 'localhost', - help: 'Irker daemon hostname (defaults to localhost)' }, - { type: 'text', name: 'server_port', placeholder: 6659, - help: 'Irker daemon port (defaults to 6659)' }, - { type: 'text', name: 'default_irc_uri', title: 'Default IRC URI', - help: 'A default IRC URI to prepend before each recipient (optional)', + { type: 'text', name: 'server_host', placeholder: 'localhost', title: s_('IrkerService|Server host (optional)'), + help: s_('IrkerService|irker daemon hostname (defaults to localhost).') }, + { type: 'text', name: 'server_port', placeholder: 6659, title: s_('IrkerService|Server port (optional)'), + help: s_('IrkerService|irker daemon port (defaults to 6659).') }, + { type: 'text', name: 'default_irc_uri', title: s_('IrkerService|Default IRC URI (optional)'), + help: s_('IrkerService|URI to add before each recipient.'), placeholder: 'irc://irc.network.net:6697/' }, - { type: 'textarea', name: 'recipients', - placeholder: 'Recipients/channels separated by whitespaces', required: true, - help: 'Recipients have to be specified with a full URI: '\ - 'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\ - 'you want the channel to be a nickname instead, append ",isnick" to ' \ - 'the channel name; if the channel is protected by a secret password, ' \ - ' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \ - ' want to use a password, you have to omit the "#" on the channel). If you ' \ - ' specify a default IRC URI to prepend before each recipient, you can just ' \ - ' give a channel name.' }, - { type: 'checkbox', name: 'colorize_messages' } + { type: 'textarea', name: 'recipients', title: s_('IrkerService|Recipients'), + placeholder: 'irc[s]://irc.network.net[:port]/#channel', required: true, + help: s_('IrkerService|Channels and users separated by whitespaces. %{recipients_docs_link}').html_safe % { recipients_docs_link: recipients_docs_link.html_safe } }, + { type: 'checkbox', name: 'colorize_messages', title: _('Colorize messages') } ] end def help - ' NOTE: Irker does NOT have built-in authentication, which makes it' \ - ' vulnerable to spamming IRC channels if it is hosted outside of a ' \ - ' firewall. Please make sure you run the daemon within a secured network ' \ - ' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.' + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/irker', anchor: 'set-up-an-irker-daemon'), target: '_blank', rel: 'noopener noreferrer' + s_('IrkerService|Send update messages to an irker server. Before you can use this, you need to set up the irker daemon. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end private diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index cf5906a4cbf..ab044b80133 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -28,8 +28,6 @@ class GroupMember < Member attr_accessor :last_owner, :last_blocked_owner - self.enumerate_columns_in_select_statements = true - def self.access_level_roles Gitlab::Access.options_with_owner end diff --git a/app/models/user.rb b/app/models/user.rb index 2824d192375..bfb0c29a023 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1576,10 +1576,11 @@ class User < ApplicationRecord .order('routes.path') end - def namespaces - namespace_ids = groups.pluck(:id) - namespace_ids.push(namespace.id) - Namespace.where(id: namespace_ids) + def namespaces(owned_only: false) + user_groups = owned_only ? owned_groups : groups + personal_namespace = Namespace.where(id: namespace.id) + + Namespace.from_union([user_groups, personal_namespace]) end def oauth_authorized_tokens diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index b6e063f5584..4dcd29382ce 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -533,7 +533,7 @@ class ProjectPolicy < BasePolicy enable :read_project_for_iids end - rule { ~project_allowed_for_job_token }.prevent_all + rule { ~public_project & ~internal_access & ~project_allowed_for_job_token }.prevent_all rule { can?(:public_access) }.policy do enable :read_package diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 463984a13a2..22a5bada311 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -26,7 +26,7 @@ = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do = search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control gl-form-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false } .control.d-none.d-md-block - = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-default btn-icon' do + = link_to project_commits_path(@project, @id, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-default btn-icon' do = sprite_icon('rss', css_class: 'qa-rss-icon') = render_if_exists 'projects/commits/mirror_status' diff --git a/config/initializers/active_record_build_select.rb b/config/initializers/active_record_build_select.rb index 48f1b1ee407..ab5a872cac6 100644 --- a/config/initializers/active_record_build_select.rb +++ b/config/initializers/active_record_build_select.rb @@ -9,10 +9,6 @@ # statement cache. If a different migration is then run and one of these columns is # removed in the meantime, the query is invalid. -ActiveRecord::Base.class_eval do - class_attribute :enumerate_columns_in_select_statements -end - module ActiveRecord module QueryMethods private @@ -20,8 +16,6 @@ module ActiveRecord def build_select(arel) if select_values.any? arel.project(*arel_columns(select_values.uniq)) - elsif klass.enumerate_columns_in_select_statements - arel.project(*klass.column_names.map { |field| table[field] }) else arel.project(@klass.arel_table[Arel.star]) end diff --git a/db/post_migrate/20210727175201_remove_unused_columns_from_elastic_reindexing_tasks.rb b/db/post_migrate/20210727175201_remove_unused_columns_from_elastic_reindexing_tasks.rb new file mode 100644 index 00000000000..d4ce1e7024f --- /dev/null +++ b/db/post_migrate/20210727175201_remove_unused_columns_from_elastic_reindexing_tasks.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class RemoveUnusedColumnsFromElasticReindexingTasks < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + def up + remove_column :elastic_reindexing_tasks, :documents_count, :integer + remove_column :elastic_reindexing_tasks, :index_name_from, :text + remove_column :elastic_reindexing_tasks, :index_name_to, :text + remove_column :elastic_reindexing_tasks, :elastic_task, :text + remove_column :elastic_reindexing_tasks, :documents_count_target, :integer + end + + def down + add_column :elastic_reindexing_tasks, :documents_count, :integer + add_column :elastic_reindexing_tasks, :index_name_from, :text + add_column :elastic_reindexing_tasks, :index_name_to, :text + add_column :elastic_reindexing_tasks, :elastic_task, :text + add_column :elastic_reindexing_tasks, :documents_count_target, :integer + + add_text_limit :elastic_reindexing_tasks, :index_name_from, 255 + add_text_limit :elastic_reindexing_tasks, :index_name_to, 255 + add_text_limit :elastic_reindexing_tasks, :elastic_task, 255 + end +end diff --git a/db/schema_migrations/20210727175201 b/db/schema_migrations/20210727175201 new file mode 100644 index 00000000000..78dae022e6b --- /dev/null +++ b/db/schema_migrations/20210727175201 @@ -0,0 +1 @@ +c7ae79084b802723a24064cb700b6cdc9a23011d3fed45457799c1ae7aa19ce6 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 7517098fc02..d6620c1b78e 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12661,21 +12661,13 @@ CREATE TABLE elastic_reindexing_tasks ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, - documents_count integer, state smallint DEFAULT 0 NOT NULL, in_progress boolean DEFAULT true NOT NULL, - index_name_from text, - index_name_to text, - elastic_task text, error_message text, - documents_count_target integer, delete_original_index_at timestamp with time zone, max_slices_running smallint DEFAULT 60 NOT NULL, slice_multiplier smallint DEFAULT 2 NOT NULL, - CONSTRAINT check_04151aca42 CHECK ((char_length(index_name_from) <= 255)), - CONSTRAINT check_7f64acda8e CHECK ((char_length(error_message) <= 255)), - CONSTRAINT check_85ebff7124 CHECK ((char_length(index_name_to) <= 255)), - CONSTRAINT check_942e5aae53 CHECK ((char_length(elastic_task) <= 255)) + CONSTRAINT check_7f64acda8e CHECK ((char_length(error_message) <= 255)) ); CREATE SEQUENCE elastic_reindexing_tasks_id_seq diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index 8b9921f9e0e..b855a3b9bbd 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -270,7 +270,7 @@ innersourcing interdependencies interdependency interruptible -Irker +irker issuables Istio Jaeger diff --git a/doc/administration/logs.md b/doc/administration/logs.md index b1605604df5..634a7099b25 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -342,7 +342,7 @@ Depending on your installation method, this file is located at: - Installations from source: `/home/git/gitlab/log/integrations_json.log` It contains information about [integration](../user/project/integrations/overview.md) -activities, such as Jira, Asana, and Irker services. It uses JSON format, +activities, such as Jira, Asana, and irker services. It uses JSON format, like this example: ```json diff --git a/doc/api/index.md b/doc/api/index.md index 1d3a85c8ec7..4a47ec84ad1 100644 --- a/doc/api/index.md +++ b/doc/api/index.md @@ -259,7 +259,7 @@ Refer to this feature's version history for more details. You can limit the access scope of a project's CI/CD job token to increase the job token's security. A job token might give extra permissions that aren't necessary -to access specific resources. Limiting the job token access scope reduces the risk of a leaked +to access specific private resources. Limiting the job token access scope reduces the risk of a leaked token being used to access private data that the user associated to the job can access. Control the job token access scope with an allowlist of other projects authorized @@ -273,7 +273,9 @@ setting at all times, and configure the allowlist for cross-project access if ne For example, when the setting is enabled, jobs in a pipeline in project `A` have a `CI_JOB_TOKEN` scope limited to project `A`. If the job needs to use the token -to make an API request to project `B`, then `B` must be added to the allowlist for `A`. +to make an API request to a private project `B`, then `B` must be added to the allowlist for `A`. +If project `B` is public or internal, it doesn't need to be added to the allowlist. +The job token scope is only for controlling access to private projects. To enable and configure the job token scope limit: diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md index e6ad855f62f..03aefaf4380 100644 --- a/doc/api/namespaces.md +++ b/doc/api/namespaces.md @@ -21,8 +21,15 @@ administrator, a list of all namespaces in the GitLab instance is shown. ```plaintext GET /namespaces +GET /namespaces?search=foobar +GET /namespaces?owned_only=true ``` +| Attribute | Type | Required | Description | +| ------------ | ------- | -------- | ----------- | +| `search` | string | no | Returns a list of namespaces the user is authorized to view based on the search criteria | +| `owned_only` | boolean | no | In GitLab 14.2 and later, returns a list of owned namespaces only | + Example request: ```shell @@ -116,48 +123,6 @@ once a day. NOTE: Only group owners are presented with `members_count_with_descendants` and `plan`. -## Search for namespace - -Get all namespaces that match a string in their name or path. - -```plaintext -GET /namespaces?search=foobar -``` - -| Attribute | Type | Required | Description | -| --------- | ------ | -------- | ----------- | -| `search` | string | no | Returns a list of namespaces the user is authorized to see based on the search criteria | - -Example request: - -```shell -curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/namespaces?search=twitter" -``` - -Example response: - -```json -[ - { - "id": 4, - "name": "twitter", - "path": "twitter", - "kind": "group", - "full_path": "twitter", - "parent_id": null, - "avatar_url": null, - "web_url": "https://gitlab.example.com/groups/twitter", - "members_count_with_descendants": 2, - "billable_members_count": 2, - "max_seats_used": 0, - "seats_in_use": 0, - "plan": "default", - "trial_ends_on": null, - "trial": false - } -] -``` - ## Get namespace by ID Get a namespace by ID. diff --git a/doc/api/services.md b/doc/api/services.md index da5e02076fc..0a699aee4e6 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -695,16 +695,15 @@ Get Hangouts Chat service settings for a project. GET /projects/:id/services/hangouts-chat ``` -## Irker (IRC gateway) +## irker (IRC gateway) -Send IRC messages, on update, to a list of recipients through an Irker gateway. +Send IRC messages, on update, to a list of recipients through an irker gateway. -### Create/Edit Irker (IRC gateway) service +For more information, see the [irker integration documentation](../user/project/integrations/irker.md). -Set Irker (IRC gateway) service for a project. +### Create/Edit irker (IRC gateway) service -NOTE: -Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read [Security analysis of `irker`](http://www.catb.org/~esr/irker/security.html). +Set irker (IRC gateway) service for a project. ```plaintext PUT /projects/:id/services/irker @@ -721,17 +720,17 @@ Parameters: | `colorize_messages` | boolean | false | Colorize messages | | `push_events` | boolean | false | Enable notifications for push events | -### Delete Irker (IRC gateway) service +### Delete irker (IRC gateway) service -Delete Irker (IRC gateway) service for a project. +Delete irker (IRC gateway) service for a project. ```plaintext DELETE /projects/:id/services/irker ``` -### Get Irker (IRC gateway) service settings +### Get irker (IRC gateway) service settings -Get Irker (IRC gateway) service settings for a project. +Get irker (IRC gateway) service settings for a project. ```plaintext GET /projects/:id/services/irker diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 1d69bc8d930..7ab6d8f5f3d 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -3353,6 +3353,50 @@ Coverage output from [child pipelines](../pipelines/parent_child_pipelines.md) i or displayed. Check [the related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/280818) for more details. +## `dast_configuration` **(ULTIMATE)** + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5981) in GitLab 14.1. + +Use the `dast_configuration` keyword to specify a site profile and scanner profile to be used in a +CI/CD configuration. Both profiles must first have been created in the project. The job's stage must +be `dast`. + +**Keyword type**: Job keyword. You can use only as part of a job. + +**Possible inputs**: One each of `site_profile` and `scanner_profile`. + +- Use `site_profile` to specify the site profile to be used in the job. +- Use `scanner_profile` to specify the scanner profile to be used in the job. + +**Example of `dast_configuration`**: + +```yaml +stages: + - build + - dast + +include: + - template: DAST.gitlab-ci.yml + +dast: + dast_configuration: + site_profile: "Example Co" + scanner_profile: "Quick Passive Test" +``` + +In this example, the `dast` job extends the `dast` configuration added with the `include:` keyword +to select a specific site profile and scanner profile. + +**Additional details**: + +- Settings contained in either a site profile or scanner profile take precedence over those + contained in the DAST template. + +**Related topics**: + +- [Site profile](../../user/application_security/dast/index.md#site-profile). +- [Scanner profile](../../user/application_security/dast/index.md#scanner-profile). + ### `retry` > [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3515) in GitLab 11.5, you can control which failures to retry on. @@ -4551,50 +4595,6 @@ You can use [CI/CD variables](../variables/index.md) to configure how the runner You can also use variables to configure how many times a runner [attempts certain stages of job execution](../runners/configure_runners.md#job-stages-attempts). -## `dast_configuration` **(ULTIMATE)** - -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5981) in GitLab 14.1. - -Use the `dast_configuration` keyword to specify a site profile and scanner profile to be used in a -CI/CD configuration. Both profiles must first have been created in the project. The job's stage must -be `dast`. - -**Keyword type**: Job keyword. You can use only as part of a job. - -**Possible inputs**: One each of `site_profile` and `scanner_profile`. - -- Use `site_profile` to specify the site profile to be used in the job. -- Use `scanner_profile` to specify the scanner profile to be used in the job. - -**Example of `dast_configuration`**: - -```yaml -stages: - - build - - dast - -include: - - template: DAST.gitlab-ci.yml - -dast: - dast_configuration: - site_profile: "Example Co" - scanner_profile: "Quick Passive Test" -``` - -In this example, the `dast` job extends the `dast` configuration added with the `include:` keyword -to select a specific site profile and scanner profile. - -**Additional details**: - -- Settings contained in either a site profile or scanner profile take precedence over those - contained in the DAST template. - -**Related topics**: - -- [Site profile](../../user/application_security/dast/index.md#site-profile). -- [Scanner profile](../../user/application_security/dast/index.md#scanner-profile). - ## YAML-specific features In your `.gitlab-ci.yml` file, you can use YAML-specific features like anchors (`&`), aliases (`*`), diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md index cb5258981be..886fb492670 100644 --- a/doc/user/packages/generic_packages/index.md +++ b/doc/user/packages/generic_packages/index.md @@ -131,6 +131,18 @@ download: - 'wget --header="JOB-TOKEN: $CI_JOB_TOKEN" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt' ``` +When using a Windows runner with PowerShell, you must use `Invoke-WebRequest` or `Invoke-RestMethod` +instead of `curl` in the `upload` and `download` stages. + +For example: + +```yaml +upload: + stage: upload + script: + - Invoke-RestMethod -Headers @{ "JOB-TOKEN"="$CI_JOB_TOKEN" } -InFile path/to/file.txt -uri "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt" -Method put +``` + ### Enable or disable generic packages in the Package Registry Support for generic packages is under development but ready for production use. diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md index eaf1d33a938..42ba973f31f 100644 --- a/doc/user/profile/notifications.md +++ b/doc/user/profile/notifications.md @@ -328,5 +328,12 @@ reason `assigned` has this sentence in the footer: - `You are receiving this email because you have been assigned an item on .` -NOTE: Notification of other events is being considered for inclusion in the `X-GitLab-NotificationReason` header. For details, see this [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/20689). + +For example, an alert notification email can have one of +[the alert's](../../operations/incident_management/alerts.md) statuses: + +- `alert_triggered` +- `alert_acknowledged` +- `alert_resolved` +- `alert_ignored` diff --git a/doc/user/project/integrations/irker.md b/doc/user/project/integrations/irker.md index 295300fb55d..79df556ac8f 100644 --- a/doc/user/project/integrations/irker.md +++ b/doc/user/project/integrations/irker.md @@ -4,57 +4,69 @@ group: Ecosystem info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Irker IRC Gateway **(FREE)** +# irker IRC Gateway **(FREE)** -GitLab provides a way to push update messages to an Irker server. When +GitLab provides a way to push update messages to an irker server. When configured, pushes to a project trigger the service to send data directly -to the Irker server. +to the irker server. -See the [project homepage](https://gitlab.com/esr/irker) for further information. +See also the [irker integration API documentation](../../../api/services.md). -## Needed setup +For more information, see the [irker project homepage](https://gitlab.com/esr/irker). -You first need an Irker daemon. You can download the Irker code -[from its repository](https://gitlab.com/esr/irker): +## Set up an irker daemon -```shell -git clone https://gitlab.com/esr/irker.git -``` +You need to set up an irker daemon. To do so: -Once you have downloaded the code, you can run the Python script named `irkerd`. -This script is the gateway script, it acts both as an IRC client, for sending -messages to an IRC server, and as a TCP server, for receiving messages -from the GitLab service. +1. Download the irker code [from its repository](https://gitlab.com/esr/irker): -If the Irker server runs on the same machine, you are done. If not, you + ```shell + git clone https://gitlab.com/esr/irker.git + ``` + +1. Run the Python script named `irkerd`. This is the gateway script. + It acts both as an IRC client, for sending messages to an IRC server, + and as a TCP server, for receiving messages from the GitLab service. + +If the irker server runs on the same machine, you are done. If not, you need to follow the first steps of the next section. +WARNING: +irker does **not** have built-in authentication, which makes it vulnerable to spamming IRC channels if +it is hosted outside of a firewall. To prevent abuse, make sure you run the daemon on a secured +network. For more details, read +[Security analysis of irker](http://www.catb.org/~esr/irker/security.html). + ## Complete these steps in GitLab -1. Navigate to the project you want to configure for notifications. -1. Navigate to the [Integrations page](overview.md#accessing-integrations) -1. Click "Irker". +1. On the top bar, select **Menu > Projects** and find the project you want to + configure for notifications. +1. Navigate to the [Integrations page](overview.md#accessing-integrations). +1. Select **irker (IRC gateway)**. 1. Ensure that the **Active** toggle is enabled. -1. Enter the server host address where `irkerd` runs (defaults to `localhost`) - in the `Server host` field on the Web page -1. Enter the server port of `irkerd` (e.g. defaults to 6659) in the - `Server port` field on the Web page. -1. Optional: if `Default IRC URI` is set, it has to be in the format - `irc[s]://domain.name` and is prepended to each and every channel provided - by the user which is not a full URI. -1. Specify the recipients (e.g. #channel1, user1, etc.) -1. Save or optionally click "Test Settings". +1. Optional. Under **Server host**, enter the server host address where `irkerd` runs. If empty, + it defaults to `localhost`. +1. Optional. Under **Server port**, enter the server port of `irkerd`. If empty, it defaults to `6659`. +1. Optional. Under **Default IRC URI**, enter the default IRC URI, in the format `irc[s]://domain.name`. + It's prepended to every channel or user provided under **Recipients**, which is not a full URI. +1. Under **Recipients**, enter the users or channels to receive updates, separated by spaces + (for example, `#channel1 user1`). For more details, see [Enter irker recipients](#enter-irker-recipients). +1. Optional. Under **Colorize messages**, select the checkbox. irker will highlight your messages. +1. Select **Save changes** or optionally select **Test Settings**. -## Note on Irker recipients +## Enter irker recipients -Irker accepts channel names of the form `chan` and `#chan`, both for the -`#chan` channel. If you want to send messages in query, you need to add -`,isnick` after the channel name, in this form: `Aorimn,isnick`. In this latter -case, `Aorimn` is treated as a nick and no more as a channel name. +If you left the **Default IRC URI** field empty, enter recipients as a full URI: +`irc[s]://irc.network.net[:port]/#channel`. If you entered a default IRC URI there, you can use just +channel or user names. -Irker can also join password-protected channels. Users need to append -`?key=thesecretpassword` to the channel name. When using this feature remember to -**not** put the `#` sign in front of the channel name; failing to do so -results in Irker joining a channel literally named `#chan?key=password` henceforth -leaking the channel key through the `/whois` IRC command (depending on IRC server -configuration). This is due to a long standing Irker bug. +To send messages: + +- To a channel (for example, `#chan`), irker accepts channel names of the form `chan` and + `#chan`. +- To a password-protected channel, append `?key=thesecretpassword` to the channel name, + with the channel password instead of `thesecretpassword`. For example, `chan?key=hunter2`. + Do **not** put the `#` sign in front of the channel name. If you do, irker tries to join a + channel named `#chan?key=password` and so it can leak the channel password through the + `/whois` IRC command. This is due to a long-standing irker bug. +- In a user query, add `,isnick` after the user name. For example, `UserSmith,isnick`. diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md index 977b91be3fa..b75bb4ff605 100644 --- a/doc/user/project/integrations/overview.md +++ b/doc/user/project/integrations/overview.md @@ -41,7 +41,7 @@ Click on the service links to see further configuration instructions and details | [Flowdock](../../../api/services.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No | | [GitHub](github.md) | Obtain statuses for commits and pull requests. | **{dotted-circle}** No | | [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat.| **{dotted-circle}** No | -| [Irker (IRC gateway)](irker.md) | Send IRC messages. | **{dotted-circle}** No | +| [irker (IRC gateway)](irker.md) | Send IRC messages. | **{dotted-circle}** No | | [Jenkins](../../../integration/jenkins.md) | Run CI/CD pipelines with Jenkins. | **{check-circle}** Yes | | JetBrains TeamCity CI | Run CI/CD pipelines with TeamCity. | **{check-circle}** Yes | | [Jira](jira.md) | Use Jira as the issue tracker. | **{dotted-circle}** No | diff --git a/lib/api/environments.rb b/lib/api/environments.rb index 57e548183b0..e50da4264b5 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -77,7 +77,7 @@ module API desc "Delete multiple stopped review apps" do detail "Remove multiple stopped review environments older than a specific age" - success Entities::Environment + success Entities::EnvironmentBasic end params do optional :before, type: Time, desc: "The timestamp before which environments can be deleted. Defaults to 30 days ago.", default: -> { 30.days.ago } @@ -90,8 +90,8 @@ module API result = ::Environments::ScheduleToDeleteReviewAppsService.new(user_project, current_user, params).execute response = { - scheduled_entries: Entities::Environment.represent(result.scheduled_entries), - unprocessable_entries: Entities::Environment.represent(result.unprocessable_entries) + scheduled_entries: Entities::EnvironmentBasic.represent(result.scheduled_entries), + unprocessable_entries: Entities::EnvironmentBasic.represent(result.unprocessable_entries) } if result.success? diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb index 9d41c2f148f..c2d839571a6 100644 --- a/lib/api/namespaces.rb +++ b/lib/api/namespaces.rb @@ -27,12 +27,15 @@ module API end params do optional :search, type: String, desc: "Search query for namespaces" + optional :owned_only, type: Boolean, desc: "Owned namespaces only" use :pagination use :optional_list_params_ee end get do - namespaces = current_user.admin ? Namespace.all : current_user.namespaces + owned_only = params[:owned_only] == true + + namespaces = current_user.admin ? Namespace.all : current_user.namespaces(owned_only: owned_only) namespaces = namespaces.include_route diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a6a7309a616..2a4f61d307f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7988,6 +7988,9 @@ msgstr "" msgid "Collector hostname" msgstr "" +msgid "Colorize messages" +msgstr "" + msgid "ComboSearch is not defined" msgstr "" @@ -18122,6 +18125,42 @@ msgstr "" msgid "Invocations" msgstr "" +msgid "IrkerService|Channels and users separated by whitespaces. %{recipients_docs_link}" +msgstr "" + +msgid "IrkerService|Default IRC URI (optional)" +msgstr "" + +msgid "IrkerService|How to enter channels or users?" +msgstr "" + +msgid "IrkerService|Recipients" +msgstr "" + +msgid "IrkerService|Send update messages to an irker server." +msgstr "" + +msgid "IrkerService|Send update messages to an irker server. Before you can use this, you need to set up the irker daemon. %{docs_link}" +msgstr "" + +msgid "IrkerService|Server host (optional)" +msgstr "" + +msgid "IrkerService|Server port (optional)" +msgstr "" + +msgid "IrkerService|URI to add before each recipient." +msgstr "" + +msgid "IrkerService|irker (IRC gateway)" +msgstr "" + +msgid "IrkerService|irker daemon hostname (defaults to localhost)." +msgstr "" + +msgid "IrkerService|irker daemon port (defaults to 6659)." +msgstr "" + msgid "Is blocked by" msgstr "" diff --git a/spec/features/projects/services/user_activates_irker_spec.rb b/spec/features/projects/services/user_activates_irker_spec.rb index e4d92dc30ff..004aa116bb3 100644 --- a/spec/features/projects/services/user_activates_irker_spec.rb +++ b/spec/features/projects/services/user_activates_irker_spec.rb @@ -2,16 +2,16 @@ require 'spec_helper' -RSpec.describe 'User activates Irker (IRC gateway)' do +RSpec.describe 'User activates irker (IRC gateway)' do include_context 'project service activation' it 'activates service', :js do - visit_project_integration('Irker (IRC gateway)') + visit_project_integration('irker (IRC gateway)') check('Colorize messages') fill_in('Recipients', with: 'irc://chat.freenode.net/#commits') click_test_then_save_integration(expect_test_to_fail: false) - expect(page).to have_content('Irker (IRC gateway) settings saved and active.') + expect(page).to have_content('irker (IRC gateway) settings saved and active.') end end diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb index b936a7f38f6..201a58ba379 100644 --- a/spec/features/projects/services/user_views_services_spec.rb +++ b/spec/features/projects/services/user_views_services_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'User views services', :js do expect(page).to have_content('Atlassian Bamboo') expect(page).to have_content('JetBrains TeamCity') expect(page).to have_content('Asana') - expect(page).to have_content('Irker (IRC gateway)') + expect(page).to have_content('irker (IRC gateway)') expect(page).to have_content('Packagist') end end diff --git a/spec/fixtures/api/schemas/public_api/v4/environments.json b/spec/fixtures/api/schemas/public_api/v4/environments.json new file mode 100644 index 00000000000..f739c06f604 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/environments.json @@ -0,0 +1,9 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties": { + "$ref": "./environment.json" + } + } +} diff --git a/spec/mailers/emails/projects_spec.rb b/spec/mailers/emails/projects_spec.rb index a5b89d16bc2..4855d5becfa 100644 --- a/spec/mailers/emails/projects_spec.rb +++ b/spec/mailers/emails/projects_spec.rb @@ -36,6 +36,27 @@ RSpec.describe Emails::Projects do Notify.prometheus_alert_fired_email(project, user, alert) end + it_behaves_like 'an email with X-GitLab headers containing project details' + + it 'has expected X-GitLab alert headers', :aggregate_failures do + is_expected.to have_header('X-GitLab-Alert-ID', /#{alert.id}/) + is_expected.to have_header('X-GitLab-Alert-IID', /#{alert.iid}/) + is_expected.to have_header('X-GitLab-NotificationReason', "alert_#{alert.state}") + + is_expected.not_to have_header('X-GitLab-Incident-ID', /.+/) + is_expected.not_to have_header('X-GitLab-Incident-IID', /.+/) + end + + context 'with incident' do + let(:alert) { create(:alert_management_alert, :with_issue, :from_payload, payload: payload, project: project) } + let(:incident) { alert.issue } + + it 'has expected X-GitLab incident headers', :aggregate_failures do + is_expected.to have_header('X-GitLab-Incident-ID', /#{incident.id}/) + is_expected.to have_header('X-GitLab-Incident-IID', /#{incident.iid}/) + end + end + context 'with empty payload' do let(:payload) { {} } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8e94c98ab9a..8361b523a13 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1798,6 +1798,15 @@ RSpec.describe User do it { expect(user.namespaces).to contain_exactly(user.namespace, group) } it { expect(user.manageable_namespaces).to contain_exactly(user.namespace, group) } + context 'with owned groups only' do + before do + other_group = create(:group) + other_group.add_developer(user) + end + + it { expect(user.namespaces(owned_only: true)).to contain_exactly(user.namespace, group) } + end + context 'with child groups' do let!(:subgroup) { create(:group, parent: group) } diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 59123c3695a..70869a9f36b 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1419,66 +1419,65 @@ RSpec.describe ProjectPolicy do end describe 'when user is authenticated via CI_JOB_TOKEN', :request_store do - let(:current_user) { developer } - let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) } + using RSpec::Parameterized::TableSyntax - before do - current_user.set_ci_job_token_scope!(job) - scope_project.update!(ci_job_token_scope_enabled: true) + where(:project_visibility, :user_role, :external_user, :scope_project_type, :token_scope_enabled, :result) do + :private | :reporter | false | :same | true | true + :private | :reporter | false | :same | false | true + :private | :reporter | false | :different | true | false + :private | :reporter | false | :different | false | true + :private | :guest | false | :same | true | true + :private | :guest | false | :same | false | true + :private | :guest | false | :different | true | false + :private | :guest | false | :different | false | true + + :internal | :reporter | false | :same | true | true + :internal | :reporter | true | :same | true | true + :internal | :reporter | false | :same | false | true + :internal | :reporter | false | :different | true | true + :internal | :reporter | true | :different | true | false + :internal | :reporter | false | :different | false | true + :internal | :guest | false | :same | true | true + :internal | :guest | true | :same | true | true + :internal | :guest | false | :same | false | true + :internal | :guest | false | :different | true | true + :internal | :guest | true | :different | true | false + :internal | :guest | false | :different | false | true + + :public | :reporter | false | :same | true | true + :public | :reporter | false | :same | false | true + :public | :reporter | false | :different | true | true + :public | :reporter | false | :different | false | true + :public | :guest | false | :same | true | true + :public | :guest | false | :same | false | true + :public | :guest | false | :different | true | true + :public | :guest | false | :different | false | true end - context 'when accessing a private project' do - let(:project) { private_project } + with_them do + let(:current_user) { public_send(user_role) } + let(:project) { public_send("#{project_visibility}_project") } + let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) } - context 'when the job token comes from the same project' do - let(:scope_project) { project } - - it { is_expected.to be_allowed(:developer_access) } - end - - context 'when the job token comes from another project' do - let(:scope_project) { create(:project, :private) } - - before do - scope_project.add_developer(current_user) - end - - it { is_expected.to be_disallowed(:guest_access) } - - context 'when job token scope is disabled' do - before do - scope_project.update!(ci_job_token_scope_enabled: false) - end - - it { is_expected.to be_allowed(:guest_access) } + let(:scope_project) do + if scope_project_type == :same + project + else + create(:project, :private) end end - end - context 'when accessing a public project' do - let(:project) { public_project } - - context 'when the job token comes from the same project' do - let(:scope_project) { project } - - it { is_expected.to be_allowed(:developer_access) } + before do + current_user.set_ci_job_token_scope!(job) + current_user.external = external_user + scope_project.update!(ci_job_token_scope_enabled: token_scope_enabled) end - context 'when the job token comes from another project' do - let(:scope_project) { create(:project, :private) } - - before do - scope_project.add_developer(current_user) - end - - it { is_expected.to be_disallowed(:public_access) } - - context 'when job token scope is disabled' do - before do - scope_project.update!(ci_job_token_scope_enabled: false) - end - - it { is_expected.to be_allowed(:public_access) } + it "enforces the expected permissions" do + if result + is_expected.to be_allowed("#{user_role}_access".to_sym) + else + is_expected.to be_disallowed("#{user_role}_access".to_sym) end end end diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index 5d40e8c529a..bc7bb7523c9 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -360,6 +360,8 @@ RSpec.describe API::Environments do expect(json_response["scheduled_entries"].size).to eq(1) expect(json_response["scheduled_entries"].first["id"]).to eq(old_stopped_review_env.id) expect(json_response["unprocessable_entries"].size).to eq(0) + expect(json_response["scheduled_entries"]).to match_schema('public_api/v4/environments') + expect(json_response["unprocessable_entries"]).to match_schema('public_api/v4/environments') expect(old_stopped_review_env.reload.auto_delete_at).to eq(1.week.from_now) expect(new_stopped_review_env.reload.auto_delete_at).to be_nil diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index 378ee2f3e7c..4091253fb54 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -18,7 +18,7 @@ RSpec.describe API::GenericPackages do let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) } let(:user) { personal_access_token.user } - let(:ci_build) { create(:ci_build, :running, user: user, project: project) } + let(:ci_build) { create(:ci_build, :running, user: user) } let(:snowplow_standard_context_params) { { user: user, project: project, namespace: project.namespace } } def auth_header diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index 0143340de11..e678b6cf1c8 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -11,7 +11,7 @@ RSpec.describe API::GoProxy do let_it_be(:base) { "#{Settings.build_gitlab_go_url}/#{project.full_path}" } let_it_be(:oauth) { create :oauth_access_token, scopes: 'api', resource_owner: user } - let_it_be(:job) { create :ci_build, user: user, status: :running, project: project } + let_it_be(:job) { create :ci_build, user: user, status: :running } let_it_be(:pa_token) { create :personal_access_token, user: user } let_it_be(:modules) do diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index d9f11b19e6e..2bb6d05f54b 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::MavenPackages do let_it_be(:package_file) { package.package_files.with_file_name_like('%.xml').first } let_it_be(:jar_file) { package.package_files.with_file_name_like('%.jar').first } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index 1ed06a40f16..222d8992d1b 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -91,6 +91,19 @@ RSpec.describe API::Namespaces do expect(json_response).to be_an Array expect(json_response.length).to eq(1) end + + context 'with owned_only param' do + it 'returns only owned groups' do + group1.add_developer(user) + group2.add_owner(user) + + get api("/namespaces?owned_only=true", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response.map { |resource| resource['id'] }).to match_array([user.namespace_id, group2.id]) + end + end end end diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index ab74da4bda4..8230061546f 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -78,7 +78,7 @@ RSpec.describe API::NpmProjectPackages do context 'with a job token for a different user' do let_it_be(:other_user) { create(:user) } - let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) } + let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) } let(:headers) { build_token_auth_header(other_job.token) } diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 1ade6e91134..a3200ef6a5b 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -13,7 +13,7 @@ RSpec.describe API::PypiPackages do let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let_it_be(:job) { create(:ci_build, :running, user: user) } let(:headers) { {} } diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index 03e0954e5ab..87b08587904 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -811,7 +811,7 @@ RSpec.describe API::Releases do end context 'when using JOB-TOKEN auth' do - let(:job) { create(:ci_build, user: maintainer, project: project) } + let(:job) { create(:ci_build, user: maintainer) } let(:params) do { name: 'Another release', diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb index 9b104520b52..afa7adad80c 100644 --- a/spec/requests/api/rubygem_packages_spec.rb +++ b/spec/requests/api/rubygem_packages_spec.rb @@ -10,7 +10,7 @@ RSpec.describe API::RubygemPackages do let_it_be_with_reload(:project) { create(:project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let_it_be(:job) { create(:ci_build, :running, user: user) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:headers) { {} } diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb index b04f5ad9a94..6803c09b8c2 100644 --- a/spec/requests/api/terraform/modules/v1/packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb @@ -12,7 +12,7 @@ RSpec.describe API::Terraform::Modules::V1::Packages do let_it_be(:package) { create(:terraform_module_package, project: project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let_it_be(:job) { create(:ci_build, :running, user: user) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index c69a987c00d..b90270356f8 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -11,7 +11,7 @@ RSpec.shared_context 'conan api setup' do let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let(:project) { package.project } - let(:job) { create(:ci_build, :running, user: user, project: project) } + let(:job) { create(:ci_build, :running, user: user) } let(:job_token) { job.token } let(:auth_token) { personal_access_token.token } let(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb index c737091df48..815108be447 100644 --- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb @@ -11,7 +11,7 @@ RSpec.shared_context 'npm api setup' do let_it_be(:package, reload: true) { create(:npm_package, project: project, name: "@#{group.path}/scoped_package") } let_it_be(:token) { create(:oauth_access_token, scopes: 'api', resource_owner: user) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/views/projects/commits/show.html.haml_spec.rb b/spec/views/projects/commits/show.html.haml_spec.rb new file mode 100644 index 00000000000..e5e9906a798 --- /dev/null +++ b/spec/views/projects/commits/show.html.haml_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'projects/commits/show.html.haml' do + let(:project) { create(:project, :repository) } + let(:commits) { [project.commit] } + let(:path) { 'path/to/doc.md' } + + before do + assign(:project, project) + assign(:id, path) + assign(:repository, project.repository) + assign(:commits, commits) + assign(:hidden_commit_count, 0) + + controller.params[:controller] = 'projects/commits' + controller.params[:action] = 'show' + controller.params[:namespace_id] = project.namespace.to_param + controller.params[:project_id] = project.to_param + + allow(view).to receive(:current_user).and_return(nil) + allow(view).to receive(:namespace_project_signatures_path).and_return("/") + end + + context 'tree controls' do + before do + render + end + + it 'renders atom feed button with matching path' do + expect(rendered).to have_link(href: "#{project_commits_path(project, path)}?format=atom") + end + end +end