Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2e3f13a01a
commit
47bb4281f0
|
|
@ -1291,7 +1291,7 @@
|
|||
},
|
||||
"pipeline_variables": {
|
||||
"type": "boolean",
|
||||
"description": "Variables added for manual pipeline runs are passed to downstream pipelines.",
|
||||
"description": "Variables added for manual pipeline runs and scheduled pipelines are passed to downstream pipelines.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1407,7 +1407,7 @@
|
|||
},
|
||||
"pipeline_variables": {
|
||||
"type": "boolean",
|
||||
"description": "Variables added for manual pipeline runs are passed to downstream pipelines.",
|
||||
"description": "Variables added for manual pipeline runs and scheduled pipelines are passed to downstream pipelines.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,11 +72,15 @@ MergeRequest.prototype.initMRBtnListeners = function () {
|
|||
const wipEvent = getParameterValues('merge_request[wip_event]', url)[0];
|
||||
const mobileDropdown = draftToggle.closest('.dropdown.show');
|
||||
|
||||
const loader = document.createElement('span');
|
||||
loader.classList.add('gl-spinner', 'gl-mr-3');
|
||||
|
||||
if (mobileDropdown) {
|
||||
$(mobileDropdown.firstElementChild).dropdown('toggle');
|
||||
}
|
||||
|
||||
draftToggle.setAttribute('disabled', 'disabled');
|
||||
draftToggle.prepend(loader);
|
||||
|
||||
axios
|
||||
.put(draftToggle.href, null, { params: { format: 'json' } })
|
||||
|
|
|
|||
|
|
@ -10,23 +10,14 @@ export function setEndpoints({ commit }, endpoints) {
|
|||
commit(types.SET_ENDPOINTS, endpoints);
|
||||
}
|
||||
|
||||
export function setMrMetadata({ commit }, metadata) {
|
||||
commit(types.SET_MR_METADATA, metadata);
|
||||
}
|
||||
|
||||
export function fetchMrMetadata({ dispatch, state }) {
|
||||
export async function fetchMrMetadata({ state, commit }) {
|
||||
if (state.endpoints?.metadata) {
|
||||
axios
|
||||
.get(state.endpoints.metadata)
|
||||
.then((response) => {
|
||||
dispatch('setMrMetadata', response.data);
|
||||
})
|
||||
.catch(() => {
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/324740
|
||||
// We can't even do a simple console warning here because
|
||||
// the pipeline will fail. However, the issue above will
|
||||
// eventually handle errors appropriately.
|
||||
// console.warn('Failed to load MR Metadata for the Overview tab.');
|
||||
});
|
||||
commit(types.SET_FAILED_TO_LOAD_METADATA, false);
|
||||
try {
|
||||
const { data } = await axios.get(state.endpoints.metadata);
|
||||
commit(types.SET_MR_METADATA, data);
|
||||
} catch (error) {
|
||||
commit(types.SET_FAILED_TO_LOAD_METADATA, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export default () => ({
|
|||
endpoints: {},
|
||||
activeTab: null,
|
||||
mrMetadata: {},
|
||||
failedToLoadMetadata: false,
|
||||
},
|
||||
actions,
|
||||
getters,
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@ export default {
|
|||
SET_ACTIVE_TAB: 'SET_ACTIVE_TAB',
|
||||
SET_ENDPOINTS: 'SET_ENDPOINTS',
|
||||
SET_MR_METADATA: 'SET_MR_METADATA',
|
||||
SET_FAILED_TO_LOAD_METADATA: 'SET_FAILED_TO_LOAD_METADATA',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,4 +10,7 @@ export default {
|
|||
[types.SET_MR_METADATA](state, metadata) {
|
||||
Object.assign(state, { mrMetadata: metadata });
|
||||
},
|
||||
[types.SET_FAILED_TO_LOAD_METADATA](state, value) {
|
||||
Object.assign(state, { failedToLoadMetadata: value });
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,14 +57,15 @@ export default {
|
|||
computed: {
|
||||
...mapGetters(['getDiscussion', 'suggestionsCount', 'getSuggestionsFilePaths']),
|
||||
...mapGetters('diffs', ['suggestionCommitMessage']),
|
||||
...mapState({
|
||||
batchSuggestionsInfo: (state) => state.notes.batchSuggestionsInfo,
|
||||
failedToLoadMetadata: (state) => state.page.failedToLoadMetadata,
|
||||
}),
|
||||
discussion() {
|
||||
if (!this.note.isDraft) return {};
|
||||
|
||||
return this.getDiscussion(this.note.discussion_id);
|
||||
},
|
||||
...mapState({
|
||||
batchSuggestionsInfo: (state) => state.notes.batchSuggestionsInfo,
|
||||
}),
|
||||
noteBody() {
|
||||
return this.note.note;
|
||||
},
|
||||
|
|
@ -165,6 +166,7 @@ export default {
|
|||
:line-type="lineType"
|
||||
:help-page-path="helpPagePath"
|
||||
:default-commit-message="commitMessage"
|
||||
:failed-to-load-metadata="failedToLoadMetadata"
|
||||
@apply="applySuggestion"
|
||||
@applyBatch="applySuggestionBatch"
|
||||
@addToBatch="addSuggestionToBatch"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton, GlAlert } from '@gitlab/ui';
|
||||
import { __, n__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton },
|
||||
components: { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton, GlAlert },
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
|
|
@ -19,6 +19,11 @@ export default {
|
|||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
errorMessage: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -55,6 +60,9 @@ export default {
|
|||
>
|
||||
<gl-dropdown-form class="gl-px-4! gl-m-0!">
|
||||
<label for="commit-message">{{ __('Commit message') }}</label>
|
||||
<gl-alert v-if="errorMessage" variant="danger" :dismissible="false" class="gl-mb-4">
|
||||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
<gl-form-textarea
|
||||
id="commit-message"
|
||||
ref="commitMessage"
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ export default {
|
|||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
failedToLoadMetadata: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
batchSuggestionsCount() {
|
||||
|
|
@ -80,6 +85,7 @@ export default {
|
|||
:help-page-path="helpPagePath"
|
||||
:default-commit-message="defaultCommitMessage"
|
||||
:inapplicable-reason="suggestion.inapplicable_reason"
|
||||
:failed-to-load-metadata="failedToLoadMetadata"
|
||||
@apply="applySuggestion"
|
||||
@applyBatch="applySuggestionBatch"
|
||||
@addToBatch="addSuggestionToBatch"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import { isLoggedIn } from '~/lib/utils/common_utils';
|
|||
import { __ } from '~/locale';
|
||||
import ApplySuggestion from './apply_suggestion.vue';
|
||||
|
||||
const APPLY_SUGGESTION_ERROR_MESSAGE = __(
|
||||
'Unable to fully load the default commit message. You can still apply this suggestion and the commit message will be correct.',
|
||||
);
|
||||
|
||||
export default {
|
||||
components: { GlBadge, GlIcon, GlButton, GlLoadingIcon, ApplySuggestion },
|
||||
directives: { 'gl-tooltip': GlTooltipDirective },
|
||||
|
|
@ -52,6 +56,11 @@ export default {
|
|||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
failedToLoadMetadata: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -94,6 +103,9 @@ export default {
|
|||
|
||||
return true;
|
||||
},
|
||||
applySuggestionErrorMessage() {
|
||||
return this.failedToLoadMetadata ? APPLY_SUGGESTION_ERROR_MESSAGE : null;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
apply(message) {
|
||||
|
|
@ -171,6 +183,7 @@ export default {
|
|||
:disabled="isDisableButton"
|
||||
:default-commit-message="defaultCommitMessage"
|
||||
:batch-suggestions-count="batchSuggestionsCount"
|
||||
:error-message="applySuggestionErrorMessage"
|
||||
class="gl-ml-3"
|
||||
@apply="apply"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ export default {
|
|||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
failedToLoadMetadata: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -60,6 +65,9 @@ export default {
|
|||
noteHtml() {
|
||||
this.reset();
|
||||
},
|
||||
failedToLoadMetadata() {
|
||||
this.reset();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.renderSuggestions();
|
||||
|
|
@ -105,6 +113,7 @@ export default {
|
|||
helpPagePath,
|
||||
defaultCommitMessage,
|
||||
suggestionsCount,
|
||||
failedToLoadMetadata,
|
||||
} = this;
|
||||
const suggestion =
|
||||
suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {};
|
||||
|
|
@ -117,6 +126,7 @@ export default {
|
|||
helpPagePath,
|
||||
defaultCommitMessage,
|
||||
suggestionsCount,
|
||||
failedToLoadMetadata,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -81,11 +81,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
|
|||
|
||||
path = Gitlab::Ci::Build::Artifacts::Path.new(params[:path])
|
||||
|
||||
if ::Feature.enabled?(:ci_safe_artifact_content_type, project, default_enabled: :yaml)
|
||||
send_artifacts_entry(artifacts_file, path)
|
||||
else
|
||||
legacy_send_artifacts_entry(artifacts_file, path)
|
||||
end
|
||||
send_artifacts_entry(artifacts_file, path)
|
||||
end
|
||||
|
||||
def keep
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Ci
|
||||
class RunnerUpgradeStatusTypeEnum < BaseEnum
|
||||
graphql_name 'CiRunnerUpgradeStatusType'
|
||||
|
||||
value 'NOT_AVAILABLE',
|
||||
description: "An update is not available for the runner.",
|
||||
value: :not_available
|
||||
|
||||
value 'AVAILABLE',
|
||||
description: "An update is available for the runner.",
|
||||
value: :available
|
||||
|
||||
value 'RECOMMENDED',
|
||||
description: "An update is available and recommended for the runner.",
|
||||
value: :recommended
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -35,12 +35,6 @@ module WorkhorseHelper
|
|||
head :ok
|
||||
end
|
||||
|
||||
# Send an entry from artifacts through Workhorse
|
||||
def legacy_send_artifacts_entry(file, entry)
|
||||
headers.store(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
|
||||
head :ok
|
||||
end
|
||||
|
||||
# Send an entry from artifacts through Workhorse and set safe content type
|
||||
def send_artifacts_entry(file, entry)
|
||||
headers.store(*Gitlab::Workhorse.send_artifacts_entry(file, entry))
|
||||
|
|
|
|||
|
|
@ -274,7 +274,8 @@ module Ci
|
|||
|
||||
# The order of this list refers to the priority of the variables
|
||||
downstream_yaml_variables(expand_variables) +
|
||||
downstream_pipeline_variables(expand_variables)
|
||||
downstream_pipeline_variables(expand_variables) +
|
||||
downstream_pipeline_schedule_variables(expand_variables)
|
||||
end
|
||||
|
||||
def downstream_yaml_variables(expand_variables)
|
||||
|
|
@ -293,6 +294,15 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def downstream_pipeline_schedule_variables(expand_variables)
|
||||
return [] unless forward_pipeline_variables?
|
||||
return [] unless pipeline.pipeline_schedule
|
||||
|
||||
pipeline.pipeline_schedule.variables.to_a.map do |variable|
|
||||
{ key: variable.key, value: ::ExpandVariables.expand(variable.value, expand_variables) }
|
||||
end
|
||||
end
|
||||
|
||||
def forward_yaml_variables?
|
||||
strong_memoize(:forward_yaml_variables) do
|
||||
result = options&.dig(:trigger, :forward, :yaml_variables)
|
||||
|
|
|
|||
|
|
@ -509,7 +509,15 @@ class Note < ApplicationRecord
|
|||
# Instead of calling touch which is throttled via ThrottledTouch concern,
|
||||
# we bump the updated_at column directly. This also prevents executing
|
||||
# after_commit callbacks that we don't need.
|
||||
update_column(:updated_at, Time.current)
|
||||
attributes_to_update = { updated_at: Time.current }
|
||||
|
||||
# Notes that were edited before the `last_edited_at` column was added, fall back to `updated_at` for the edit time.
|
||||
# We copy this over to the correct column so we don't erroneously change the edit timestamp.
|
||||
if updated_by_id.present? && read_attribute(:last_edited_at).blank?
|
||||
attributes_to_update[:last_edited_at] = updated_at
|
||||
end
|
||||
|
||||
update_columns(attributes_to_update)
|
||||
end
|
||||
|
||||
def expire_etag_cache
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- return unless Gitlab::Tracking.enabled?
|
||||
|
||||
- namespace = @group || @project&.namespace
|
||||
- namespace = @group || @project&.namespace || @namespace
|
||||
|
||||
= javascript_tag do
|
||||
:plain
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: ci_safe_artifact_content_type
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83826
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357664
|
||||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::pipeline insights
|
||||
default_enabled: false
|
||||
|
|
@ -23,11 +23,11 @@ GitLab can stream a single event more than once to the same destination. Use the
|
|||
## Add a new event streaming destination
|
||||
|
||||
WARNING:
|
||||
Event streaming destinations will receive **all** audit event data, which could include sensitive information. Make sure you trust the destination endpoint.
|
||||
Event streaming destinations receive **all** audit event data, which could include sensitive information. Make sure you trust the destination endpoint.
|
||||
|
||||
### Add event streaming destination using GitLab UI
|
||||
### Use the GitLab UI
|
||||
|
||||
Users with at least the Owner role of a group can add event streaming destinations for it:
|
||||
Users with at least the Owner role for a group can add event streaming destinations for it:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Security & Compliance > Audit events**
|
||||
|
|
@ -41,10 +41,10 @@ Event streaming is enabled if:
|
|||
- No warning is shown.
|
||||
- The added endpoint is displayed in the UI.
|
||||
|
||||
### Add event streaming destination using the API
|
||||
### Use the API
|
||||
|
||||
To enable event streaming, a group owner must add a new event streaming destination using the `externalAuditEventDestinationCreate` mutation
|
||||
in the GraphQL API.
|
||||
To enable event streaming and add a destination, users with at least the Owner role for a group must use the
|
||||
`externalAuditEventDestinationCreate` mutation in the GraphQL API.
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
|
|
@ -66,19 +66,24 @@ Event streaming is enabled if:
|
|||
- The returned `errors` object is empty.
|
||||
- The API responds with `200 OK`.
|
||||
|
||||
## List currently enabled streaming destinations
|
||||
## List streaming destinations
|
||||
|
||||
### List currently enabled streaming destination using GitLab UI
|
||||
Users with at least the Owner role for a group can list event streaming destinations.
|
||||
|
||||
Users with at least the Owner role of a group can list event streaming destinations:
|
||||
### Use the GitLab UI
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336411) in GitLab 14.9.
|
||||
|
||||
Users with at least the Owner role for a group can list event streaming destinations:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Security & Compliance > Audit events**
|
||||
1. On the main area, select **Streams** tab.
|
||||
|
||||
### List currently enabled streaming destinations using the API
|
||||
### Use the API
|
||||
|
||||
Group owners can view a list of event streaming destinations at any time using the `externalAuditEventDestinations` query type.
|
||||
Users with at least the Owner role for a group can view a list of event streaming destinations at any time using the
|
||||
`externalAuditEventDestinations` query type.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
|
|
@ -97,27 +102,34 @@ query {
|
|||
|
||||
If the resulting list is empty, then audit event streaming is not enabled for that group.
|
||||
|
||||
## Delete currently enabled streaming destination
|
||||
## Delete streaming destinations
|
||||
|
||||
Group Owners can delete event streaming destinations at any time using the `deleteAuditEventDestinations` mutation type.
|
||||
Users with at least the Owner role for a group can delete event streaming destinations using the
|
||||
`deleteAuditEventDestinations` mutation type.
|
||||
|
||||
### Delete currently enabled streaming using GitLab UI
|
||||
When the last destination is successfully deleted, event streaming is disabled for the group.
|
||||
|
||||
Uses with at least the Owner role of a group can delete event streaming destination.
|
||||
### Use the GitLab UI
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336411) in GitLab 14.9.
|
||||
|
||||
Users with at least the Owner role for a group can delete event streaming destinations.
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Security & Compliance > Audit events**
|
||||
1. On the main area, select **Streams** tab.
|
||||
1. Select **{remove}** at the right side of each item.
|
||||
|
||||
The external streaming destination is delete when:
|
||||
The external streaming destination is deleted when:
|
||||
|
||||
- No warning is shown.
|
||||
- The deleted endpoint is not displayed in the UI.
|
||||
|
||||
### Delete currently enabled streaming using the API
|
||||
### Use the API
|
||||
|
||||
You can delete an event streaming destination by specifying an ID. Get the required ID by [listing the details](audit_event_streaming.md#list-currently-enabled-streaming-destinations-using-the-api) of event streaming destinations.
|
||||
Delete an event streaming destination by specifying an ID. Get the required ID by
|
||||
[listing the details](audit_event_streaming.md#use-the-api-1) of event
|
||||
streaming destinations.
|
||||
|
||||
```graphql
|
||||
|
||||
|
|
@ -134,8 +146,6 @@ Destination is deleted if:
|
|||
- The returned `errors` object is empty.
|
||||
- The API responds with `200 OK`.
|
||||
|
||||
When the last destination is successfully deleted, event streaming is disabled for the group.
|
||||
|
||||
## Verify event authenticity
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345424) in GitLab 14.8.
|
||||
|
|
@ -144,7 +154,7 @@ Each streaming destination has a unique verification token (`verificationToken`)
|
|||
token is generated when the event destination is created and cannot be changed.
|
||||
|
||||
Each streamed event contains a random alphanumeric identifier for the `X-Gitlab-Event-Streaming-Token` HTTP header that can be verified against
|
||||
the destination's value when [listing streaming destinations](#list-currently-enabled-streaming-destinations).
|
||||
the destination's value when [listing streaming destinations](#list-streaming-destinations).
|
||||
|
||||
## Audit event streaming on Git operations
|
||||
|
||||
|
|
@ -163,9 +173,9 @@ Audit events are not captured for users that are not signed in. For example, whe
|
|||
|
||||
To configure streaming audit events for Git operations, see [Add a new event streaming destination](#add-a-new-event-streaming-destination).
|
||||
|
||||
### Request headers
|
||||
### Headers
|
||||
|
||||
Request headers are formatted as follows:
|
||||
Headers are formatted as follows:
|
||||
|
||||
```plaintext
|
||||
POST /logs HTTP/1.1
|
||||
|
|
@ -174,7 +184,7 @@ Content-Type: application/x-www-form-urlencoded
|
|||
X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN>
|
||||
```
|
||||
|
||||
### Example responses for SSH events
|
||||
### Example payloads for SSH events
|
||||
|
||||
Fetch:
|
||||
|
||||
|
|
@ -236,7 +246,7 @@ Push:
|
|||
}
|
||||
```
|
||||
|
||||
### Example responses for HTTP and HTTPS events
|
||||
### Example payloads for HTTP and HTTPS events
|
||||
|
||||
Fetch:
|
||||
|
||||
|
|
@ -298,7 +308,7 @@ Push:
|
|||
}
|
||||
```
|
||||
|
||||
### Example responses for events from GitLab UI download button
|
||||
### Example payloads for events from GitLab UI download button
|
||||
|
||||
Fetch:
|
||||
|
||||
|
|
@ -333,9 +343,9 @@ Fetch:
|
|||
|
||||
Stream audit events that relate to merge approval actions performed within a project.
|
||||
|
||||
### Request headers
|
||||
### Headers
|
||||
|
||||
Request headers are formatted as follows:
|
||||
Headers are formatted as follows:
|
||||
|
||||
```plaintext
|
||||
POST /logs HTTP/1.1
|
||||
|
|
@ -344,7 +354,7 @@ Content-Type: application/x-www-form-urlencoded
|
|||
X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN>
|
||||
```
|
||||
|
||||
### Example request body
|
||||
### Example payload
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9450,6 +9450,7 @@ Represents the total number of issues and their weights for a particular day.
|
|||
| <a id="cirunnershortsha"></a>`shortSha` | [`String`](#string) | First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID. |
|
||||
| <a id="cirunnertaglist"></a>`tagList` | [`[String!]`](#string) | Tags associated with the runner. |
|
||||
| <a id="cirunnertokenexpiresat"></a>`tokenExpiresAt` | [`Time`](#time) | Runner token expiration time. |
|
||||
| <a id="cirunnerupgradestatus"></a>`upgradeStatus` **{warning-solid}** | [`CiRunnerUpgradeStatusType`](#cirunnerupgradestatustype) | **Deprecated** in 14.10. This feature is in Alpha, and can be removed or changed at any point. |
|
||||
| <a id="cirunneruserpermissions"></a>`userPermissions` | [`RunnerPermissions!`](#runnerpermissions) | Permissions for the current user on the resource. |
|
||||
| <a id="cirunnerversion"></a>`version` | [`String`](#string) | Version of the runner. |
|
||||
|
||||
|
|
@ -17841,6 +17842,14 @@ Values for sorting runners.
|
|||
| <a id="cirunnertypeinstance_type"></a>`INSTANCE_TYPE` | A runner that is instance type. |
|
||||
| <a id="cirunnertypeproject_type"></a>`PROJECT_TYPE` | A runner that is project type. |
|
||||
|
||||
### `CiRunnerUpgradeStatusType`
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="cirunnerupgradestatustypeavailable"></a>`AVAILABLE` | An update is available for the runner. |
|
||||
| <a id="cirunnerupgradestatustypenot_available"></a>`NOT_AVAILABLE` | An update is not available for the runner. |
|
||||
| <a id="cirunnerupgradestatustyperecommended"></a>`RECOMMENDED` | An update is available and recommended for the runner. |
|
||||
|
||||
### `CodeQualityDegradationSeverity`
|
||||
|
||||
| Value | Description |
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@ The access levels are defined in the `Gitlab::Access` module. Currently, these l
|
|||
- Maintainer (`40`)
|
||||
- Owner (`50`) - Only valid to set for groups
|
||||
|
||||
WARNING:
|
||||
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299),
|
||||
projects in personal namespaces don't show owner (`50`) permission
|
||||
for owner.
|
||||
NOTE:
|
||||
In [GitLab 14.9](https://gitlab.com/gitlab-org/gitlab/-/issues/351211) and later, projects in personal namespaces have an `access_level` of `50`(Owner).
|
||||
In GitLab 14.8 and earlier, projects in personal namespaces have an `access_level` of `40` (Maintainer) due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299)
|
||||
|
||||
## Limitations
|
||||
|
||||
|
|
|
|||
|
|
@ -3714,7 +3714,7 @@ and [multi-project pipelines](../pipelines/multi_project_pipelines.md).
|
|||
|
||||
- `yaml_variables`: `true` (default), or `false`. When `true`, variables defined
|
||||
in the trigger job are passed to downstream pipelines.
|
||||
- `pipeline_variables`: `true` or `false` (default). When `true`, [manual pipeline variables](../variables/index.md#override-a-defined-cicd-variable)
|
||||
- `pipeline_variables`: `true` or `false` (default). When `true`, [manual pipeline variables](../variables/index.md#override-a-defined-cicd-variable) and [scheduled pipeline variables](../pipelines/schedules.md#add-a-pipeline-schedule)
|
||||
are passed to downstream pipelines.
|
||||
|
||||
**Example of `trigger:forward`**:
|
||||
|
|
|
|||
|
|
@ -148,6 +148,9 @@ This sync job runs daily around 3AM UTC. If the job fails, it is retried up to 1
|
|||
|
||||
The daily job provides **only** the following information to the Customers Portal:
|
||||
|
||||
- Company name
|
||||
- Licensee name
|
||||
- Licensee email
|
||||
- Date
|
||||
- Timestamp
|
||||
- License key
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ You can [configure](#customizing-the-container-scanning-settings) analyzers by u
|
|||
| `CS_DISABLE_DEPENDENCY_LIST` | `"false"` | Disable Dependency Scanning for packages installed in the scanned image. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6. | All |
|
||||
| `CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` | `"true"` | Disable scanning for language-specific packages installed in the scanned image. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6. | All |
|
||||
| `CS_DOCKER_INSECURE` | `"false"` | Allow access to secure Docker registries using HTTPS without validating the certificates. | All |
|
||||
| `CS_IMAGE_SUFFIX` | `""` | Suffix added to `CS_ANALYZER_IMAGE`. If set to `-fips`, `FIPS-enabled` image is used for scan. See [FIPS-enabled images](#fips-enabled-images) for more details. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7630) in GitLab 14.10. | All |
|
||||
| `CS_IGNORE_UNFIXED` | `"false"` | Ignore vulnerabilities that are not fixed. | All |
|
||||
| `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. Works with all scanners, but the registry must listen on port `80/tcp` for Trivy to work. | All |
|
||||
| `CS_SEVERITY_THRESHOLD` | `UNKNOWN` | Severity level threshold. The scanner outputs vulnerabilities with severity level higher than or equal to this threshold. Supported levels are Unknown, Low, Medium, High, and Critical. | Trivy |
|
||||
|
|
@ -238,20 +239,27 @@ Support depends on the scanner:
|
|||
- [Grype](https://github.com/anchore/grype#grype)
|
||||
- [Trivy](https://aquasecurity.github.io/trivy/latest/vulnerability/detection/os/) (Default).
|
||||
|
||||
#### UBI-based images
|
||||
#### FIPS-enabled images
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5775) in GitLab 14.1.
|
||||
|
||||
GitLab also offers [Red Hat UBI](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image)
|
||||
versions of the container-scanning images. You can therefore replace standard images with UBI-based
|
||||
images. To configure the images, set the `CS_ANALYZER_IMAGE` variable to the standard tag plus the
|
||||
`-ubi` extension.
|
||||
GitLab also offers [FIPS-enabled Red Hat UBI](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image)
|
||||
versions of the container-scanning images. You can therefore replace standard images with FIPS-enabled
|
||||
images. To configure the images, set the `CS_IMAGE_SUFFIX` to `-fips` or modify the `CS_ANALYZER_IMAGE` variable to the
|
||||
standard tag plus the `-fips` extension.
|
||||
|
||||
| Scanner name | `CS_ANALYZER_IMAGE` |
|
||||
| --------------- | ------------------- |
|
||||
| Default (Trivy) | `registry.gitlab.com/security-products/container-scanning:4-ubi` |
|
||||
| Grype | `registry.gitlab.com/security-products/container-scanning/grype:4-ubi` |
|
||||
| Trivy | `registry.gitlab.com/security-products/container-scanning/trivy:4-ubi` |
|
||||
| Default (Trivy) | `registry.gitlab.com/security-products/container-scanning:4-fips` |
|
||||
| Grype | `registry.gitlab.com/security-products/container-scanning/grype:4-fips` |
|
||||
| Trivy | `registry.gitlab.com/security-products/container-scanning/trivy:4-fips` |
|
||||
|
||||
NOTE:
|
||||
Prior to GitLab 15.0, the `-ubi` image extension is also available. GitLab 15.0 and later only
|
||||
support `-fips`.
|
||||
|
||||
Starting with GitLab 14.10, `-fips` is automatically added to `CS_ANALYZER_IMAGE` when FIPS mode is
|
||||
enabled in the GitLab instance.
|
||||
|
||||
### Enable Container Scanning through an automatic merge request
|
||||
|
||||
|
|
|
|||
|
|
@ -449,6 +449,22 @@ The labels higher in the list get higher priority.
|
|||
To learn what happens when you sort by priority or label priority, see
|
||||
[Sorting and ordering issue lists](issues/sorting_issue_lists.md).
|
||||
|
||||
## Real-time changes to labels
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241538) in GitLab 14.10 with a [feature flag](../../administration/feature_flags.md) named `realtime_labels`, disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an
|
||||
administrator to [enable the feature flag](../../administration/feature_flags.md) named `realtime_labels`.
|
||||
On GitLab.com, this feature is unavailable.
|
||||
|
||||
Changed labels are immediately visible to other users, without refreshing the page, on the following:
|
||||
|
||||
- Epics
|
||||
- Incidents
|
||||
- Issues
|
||||
- Merge requests
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Some label titles end with `_duplicate<number>`
|
||||
|
|
|
|||
|
|
@ -60,11 +60,7 @@ module API
|
|||
|
||||
bad_request! unless path.valid?
|
||||
|
||||
if ::Feature.enabled?(:ci_safe_artifact_content_type, build&.project, default_enabled: :yaml)
|
||||
send_artifacts_entry(build.artifacts_file, path)
|
||||
else
|
||||
legacy_send_artifacts_entry(build.artifacts_file, path)
|
||||
end
|
||||
send_artifacts_entry(build.artifacts_file, path)
|
||||
end
|
||||
|
||||
desc 'Download the artifacts archive from a job' do
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ variables:
|
|||
CS_ANALYZER_IMAGE: registry.gitlab.com/security-products/container-scanning:4
|
||||
|
||||
container_scanning:
|
||||
image: "$CS_ANALYZER_IMAGE"
|
||||
image: "$CS_ANALYZER_IMAGE$CS_IMAGE_SUFFIX"
|
||||
stage: test
|
||||
variables:
|
||||
# To provide a `vulnerability-allowlist.yml` file, override the GIT_STRATEGY variable in your
|
||||
|
|
@ -46,5 +46,11 @@ container_scanning:
|
|||
rules:
|
||||
- if: $CONTAINER_SCANNING_DISABLED
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bcontainer_scanning\b/ &&
|
||||
$CI_GITLAB_FIPS_MODE == "true" &&
|
||||
$CS_ANALYZER_IMAGE !~ /-(fips|ubi)\z/
|
||||
variables:
|
||||
CS_IMAGE_SUFFIX: -fips
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$GITLAB_FEATURES =~ /\bcontainer_scanning\b/
|
||||
|
|
|
|||
|
|
@ -7348,6 +7348,9 @@ msgstr ""
|
|||
msgid "Checkout|Subtotal"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Success: subscription"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checkout|Tax"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40102,6 +40105,9 @@ msgstr ""
|
|||
msgid "Unable to find Jira project to import data from."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to fully load the default commit message. You can still apply this suggestion and the commit message will be correct."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to generate new instance ID"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -381,18 +381,6 @@ RSpec.describe Projects::ArtifactsController do
|
|||
let(:store) { ObjectStorage::Store::LOCAL }
|
||||
let(:archive_path) { JobArtifactUploader.root }
|
||||
end
|
||||
|
||||
context 'when ci_safe_artifact_content_type is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_safe_artifact_content_type: false)
|
||||
end
|
||||
|
||||
it 'does not let workhorse set content type' do
|
||||
subject
|
||||
|
||||
expect(response.headers).not_to include('Gitlab-Workhorse-Detect-Content-Type')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the artifact is not zip' do
|
||||
|
|
|
|||
|
|
@ -379,4 +379,41 @@ RSpec.describe 'User comments on a diff', :js do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'failed to load metadata' do
|
||||
let(:dummy_controller) do
|
||||
Class.new(Projects::MergeRequests::DiffsController) do
|
||||
def diffs_metadata
|
||||
render json: '', status: :internal_server_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_const('Projects::MergeRequests::DiffsController', dummy_controller)
|
||||
|
||||
click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
|
||||
|
||||
page.within('.js-discussion-note-form') do
|
||||
fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
|
||||
click_button('Add comment now')
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
visit(project_merge_request_path(project, merge_request))
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'displays an error' do
|
||||
page.within('.discussion-notes') do
|
||||
click_button('Apply suggestion')
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Unable to fully load the default commit message. You can still apply this suggestion and the commit message will be correct.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,64 +1,37 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import { setEndpoints, setMrMetadata, fetchMrMetadata } from '~/mr_notes/stores/actions';
|
||||
import mutationTypes from '~/mr_notes/stores/mutation_types';
|
||||
import { createStore } from '~/mr_notes/stores';
|
||||
|
||||
describe('MR Notes Mutator Actions', () => {
|
||||
describe('setEndpoints', () => {
|
||||
it('should trigger the SET_ENDPOINTS state mutation', (done) => {
|
||||
const endpoints = { endpointA: 'a' };
|
||||
let store;
|
||||
|
||||
testAction(
|
||||
setEndpoints,
|
||||
endpoints,
|
||||
{},
|
||||
[
|
||||
{
|
||||
type: mutationTypes.SET_ENDPOINTS,
|
||||
payload: endpoints,
|
||||
},
|
||||
],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
});
|
||||
|
||||
describe('setMrMetadata', () => {
|
||||
it('should trigger the SET_MR_METADATA state mutation', async () => {
|
||||
const mrMetadata = { propA: 'a', propB: 'b' };
|
||||
describe('setEndpoints', () => {
|
||||
it('sets endpoints', async () => {
|
||||
const endpoints = { endpointA: 'a' };
|
||||
|
||||
await testAction(
|
||||
setMrMetadata,
|
||||
mrMetadata,
|
||||
{},
|
||||
[
|
||||
{
|
||||
type: mutationTypes.SET_MR_METADATA,
|
||||
payload: mrMetadata,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
await store.dispatch('setEndpoints', endpoints);
|
||||
|
||||
expect(store.state.page.endpoints).toEqual(endpoints);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchMrMetadata', () => {
|
||||
const mrMetadata = { meta: true, data: 'foo' };
|
||||
const state = {
|
||||
endpoints: {
|
||||
metadata: 'metadata',
|
||||
},
|
||||
};
|
||||
const metadata = 'metadata';
|
||||
const endpoints = { metadata };
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
await store.dispatch('setEndpoints', endpoints);
|
||||
|
||||
mock = new MockAdapter(axios);
|
||||
|
||||
mock.onGet(state.endpoints.metadata).reply(200, mrMetadata);
|
||||
mock.onGet(metadata).reply(200, mrMetadata);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -66,27 +39,26 @@ describe('MR Notes Mutator Actions', () => {
|
|||
});
|
||||
|
||||
it('should fetch the data from the API', async () => {
|
||||
await fetchMrMetadata({ state, dispatch: () => {} });
|
||||
await store.dispatch('fetchMrMetadata');
|
||||
|
||||
await axios.waitForAll();
|
||||
|
||||
expect(mock.history.get).toHaveLength(1);
|
||||
expect(mock.history.get[0].url).toBe(state.endpoints.metadata);
|
||||
expect(mock.history.get[0].url).toBe(metadata);
|
||||
});
|
||||
|
||||
it('should set the fetched data into state', () => {
|
||||
return testAction(
|
||||
fetchMrMetadata,
|
||||
{},
|
||||
state,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'setMrMetadata',
|
||||
payload: mrMetadata,
|
||||
},
|
||||
],
|
||||
);
|
||||
it('should set the fetched data into state', async () => {
|
||||
await store.dispatch('fetchMrMetadata');
|
||||
|
||||
expect(store.state.page.mrMetadata).toEqual(mrMetadata);
|
||||
});
|
||||
|
||||
it('should set failedToLoadMetadata flag when request fails', async () => {
|
||||
mock.onGet(metadata).reply(500);
|
||||
|
||||
await store.dispatch('fetchMrMetadata');
|
||||
|
||||
expect(store.state.page.failedToLoadMetadata).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlDropdown, GlFormTextarea, GlButton } from '@gitlab/ui';
|
||||
import { GlDropdown, GlFormTextarea, GlButton, GlAlert } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import ApplySuggestionComponent from '~/vue_shared/components/markdown/apply_suggestion.vue';
|
||||
|
||||
|
|
@ -10,9 +10,10 @@ describe('Apply Suggestion component', () => {
|
|||
wrapper = shallowMount(ApplySuggestionComponent, { propsData: { ...propsData, ...props } });
|
||||
};
|
||||
|
||||
const findDropdown = () => wrapper.find(GlDropdown);
|
||||
const findTextArea = () => wrapper.find(GlFormTextarea);
|
||||
const findApplyButton = () => wrapper.find(GlButton);
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findTextArea = () => wrapper.findComponent(GlFormTextarea);
|
||||
const findApplyButton = () => wrapper.findComponent(GlButton);
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
beforeEach(() => createWrapper());
|
||||
|
||||
|
|
@ -53,6 +54,20 @@ describe('Apply Suggestion component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
it('displays an error message', () => {
|
||||
const errorMessage = 'Error message';
|
||||
createWrapper({ errorMessage });
|
||||
|
||||
const alert = findAlert();
|
||||
|
||||
expect(alert.exists()).toBe(true);
|
||||
expect(alert.props('variant')).toBe('danger');
|
||||
expect(alert.props('dismissible')).toBe(false);
|
||||
expect(alert.text()).toBe(errorMessage);
|
||||
});
|
||||
});
|
||||
|
||||
describe('apply suggestion', () => {
|
||||
it('emits an apply event with no message if no message was added', () => {
|
||||
findTextArea().vm.$emit('input', null);
|
||||
|
|
|
|||
|
|
@ -288,6 +288,26 @@ RSpec.describe Ci::Bridge do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the pipeline runs from a pipeline schedule' do
|
||||
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
|
||||
let(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
|
||||
|
||||
let(:options) do
|
||||
{ trigger: { project: 'my/project', forward: { pipeline_variables: true } } }
|
||||
end
|
||||
|
||||
before do
|
||||
pipeline_schedule.variables.create!(key: 'schedule_var_key', value: 'schedule var value')
|
||||
end
|
||||
|
||||
it 'adds the schedule variable' do
|
||||
expect(bridge.downstream_variables).to contain_exactly(
|
||||
{ key: 'BRIDGE', value: 'cross' },
|
||||
{ key: 'schedule_var_key', value: 'schedule var value' }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1753,4 +1753,27 @@ RSpec.describe Note do
|
|||
expect(note.commands_changes.keys).to contain_exactly(:emoji_award, :time_estimate, :spend_time)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bump_updated_at', :freeze_time do
|
||||
it 'sets updated_at to the current timestamp' do
|
||||
note = create(:note, updated_at: 1.day.ago)
|
||||
|
||||
note.bump_updated_at
|
||||
note.reload
|
||||
|
||||
expect(note.updated_at).to be_like_time(Time.current)
|
||||
end
|
||||
|
||||
context 'with legacy edited note' do
|
||||
it 'copies updated_at to last_edited_at before bumping the timestamp' do
|
||||
note = create(:note, updated_at: 1.day.ago, updated_by: create(:user), last_edited_at: nil)
|
||||
|
||||
note.bump_updated_at
|
||||
note.reload
|
||||
|
||||
expect(note.last_edited_at).to be_like_time(1.day.ago)
|
||||
expect(note.updated_at).to be_like_time(Time.current)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -561,16 +561,6 @@ RSpec.describe API::Ci::JobArtifacts do
|
|||
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/,
|
||||
'Gitlab-Workhorse-Detect-Content-Type' => 'true')
|
||||
end
|
||||
|
||||
context 'when ci_safe_artifact_content_type is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_safe_artifact_content_type: false)
|
||||
end
|
||||
|
||||
it 'does not let workhorse set content type' do
|
||||
expect(response.headers).not_to include('Gitlab-Workhorse-Detect-Content-Type')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is public with builds access disabled' do
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ RSpec.describe 'Query.runner(id)' do
|
|||
|
||||
let_it_be(:active_project_runner) { create(:ci_runner, :project) }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
|
||||
end
|
||||
|
||||
shared_examples 'runner details fetch' do
|
||||
let(:query) do
|
||||
wrap_fields(query_graphql_path(query_path, all_graphql_fields_for('CiRunner')))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ RSpec.describe 'Query.runners' do
|
|||
end
|
||||
|
||||
before do
|
||||
allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status)
|
||||
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue