Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b90d8b54a4
commit
6f2b1c32f3
123 changed files with 860 additions and 244 deletions
|
@ -1 +1 @@
|
|||
059a82773ec2b5afc115442270d663cccc68451c
|
||||
d1f4340a1123d2436c7544d6ba64635c4c8f6104
|
||||
|
|
|
@ -41,10 +41,17 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// If this is true, we need to present the captcha modal to the user.
|
||||
// When the modal is shown we will also initialize and render the form.
|
||||
if (this.needsCaptchaResponse) {
|
||||
this.$refs.modal.show();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitReceivedCaptchaResponse(captchaResponse) {
|
||||
this.$emit('receivedCaptchaResponse', captchaResponse);
|
||||
this.$refs.modal.hide();
|
||||
this.$emit('receivedCaptchaResponse', captchaResponse);
|
||||
},
|
||||
emitNullReceivedCaptchaResponse() {
|
||||
this.emitReceivedCaptchaResponse(null);
|
||||
|
@ -103,6 +110,7 @@ export default {
|
|||
:action-cancel="{ text: __('Cancel') }"
|
||||
@shown="shown"
|
||||
@hide="hide"
|
||||
@hidden="$emit('hidden')"
|
||||
>
|
||||
<div ref="captcha"></div>
|
||||
<p>{{ __('We want to be sure it is you, please confirm you are not a robot.') }}</p>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
const supportedMethods = ['patch', 'post', 'put'];
|
||||
|
||||
export function registerCaptchaModalInterceptor(axios) {
|
||||
return axios.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(err) => {
|
||||
if (
|
||||
supportedMethods.includes(err?.config?.method) &&
|
||||
err?.response?.data?.needs_captcha_response
|
||||
) {
|
||||
const { data } = err.response;
|
||||
const captchaSiteKey = data.captcha_site_key;
|
||||
const spamLogId = data.spam_log_id;
|
||||
// eslint-disable-next-line promise/no-promise-in-callback
|
||||
return import('~/captcha/wait_for_captcha_to_be_solved')
|
||||
.then(({ waitForCaptchaToBeSolved }) => waitForCaptchaToBeSolved(captchaSiteKey))
|
||||
.then((captchaResponse) => {
|
||||
const errConfig = err.config;
|
||||
const originalData = JSON.parse(errConfig.data);
|
||||
return axios({
|
||||
method: errConfig.method,
|
||||
url: errConfig.url,
|
||||
data: {
|
||||
...originalData,
|
||||
captcha_response: captchaResponse,
|
||||
spam_log_id: spamLogId,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(err);
|
||||
},
|
||||
);
|
||||
}
|
10
app/assets/javascripts/captcha/unsolved_captcha_error.js
Normal file
10
app/assets/javascripts/captcha/unsolved_captcha_error.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
class UnsolvedCaptchaError extends Error {
|
||||
constructor(message) {
|
||||
super(message || __('You must solve the CAPTCHA in order to submit'));
|
||||
this.name = 'UnsolvedCaptchaError';
|
||||
}
|
||||
}
|
||||
|
||||
export default UnsolvedCaptchaError;
|
|
@ -0,0 +1,53 @@
|
|||
import Vue from 'vue';
|
||||
import CaptchaModal from '~/captcha/captcha_modal.vue';
|
||||
import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
|
||||
|
||||
/**
|
||||
* Opens a Captcha Modal with provided captchaSiteKey.
|
||||
*
|
||||
* Returns a Promise which resolves if the captcha is solved correctly, and rejects
|
||||
* if the captcha process is aborted.
|
||||
*
|
||||
* @param captchaSiteKey
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function waitForCaptchaToBeSolved(captchaSiteKey) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let captchaModalElement = document.createElement('div');
|
||||
|
||||
document.body.append(captchaModalElement);
|
||||
|
||||
let captchaModalVueInstance = new Vue({
|
||||
el: captchaModalElement,
|
||||
render: (createElement) => {
|
||||
return createElement(CaptchaModal, {
|
||||
props: {
|
||||
captchaSiteKey,
|
||||
needsCaptchaResponse: true,
|
||||
},
|
||||
on: {
|
||||
hidden: () => {
|
||||
// Cleaning up the modal from the DOM
|
||||
captchaModalVueInstance.$destroy();
|
||||
captchaModalVueInstance.$el.remove();
|
||||
captchaModalElement.remove();
|
||||
|
||||
captchaModalElement = null;
|
||||
captchaModalVueInstance = null;
|
||||
},
|
||||
receivedCaptchaResponse: (captchaResponse) => {
|
||||
if (captchaResponse) {
|
||||
resolve(captchaResponse);
|
||||
} else {
|
||||
// reject the promise with a custom exception, allowing consuming apps to
|
||||
// adjust their error handling, if appropriate.
|
||||
const error = new UnsolvedCaptchaError();
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
|
@ -5,7 +5,6 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
|
|||
import Poll from '~/lib/utils/poll';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import recaptchaModalImplementor from '~/vue_shared/mixins/recaptcha_modal_implementor';
|
||||
import { IssuableStatus, IssuableStatusText, IssuableType } from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import Service from '../services/index';
|
||||
|
@ -25,7 +24,6 @@ export default {
|
|||
formComponent,
|
||||
PinnedLinks,
|
||||
},
|
||||
mixins: [recaptchaModalImplementor],
|
||||
props: {
|
||||
endpoint: {
|
||||
required: true,
|
||||
|
@ -250,6 +248,7 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.flashContainer = null;
|
||||
this.service = new Service(this.endpoint);
|
||||
this.poll = new Poll({
|
||||
resource: this.service,
|
||||
|
@ -289,7 +288,7 @@ export default {
|
|||
methods: {
|
||||
handleBeforeUnloadEvent(e) {
|
||||
const event = e;
|
||||
if (this.showForm && this.issueChanged && !this.showRecaptcha) {
|
||||
if (this.showForm && this.issueChanged) {
|
||||
event.returnValue = __('Are you sure you want to lose your issue information?');
|
||||
}
|
||||
return undefined;
|
||||
|
@ -347,10 +346,10 @@ export default {
|
|||
},
|
||||
|
||||
updateIssuable() {
|
||||
this.clearFlash();
|
||||
return this.service
|
||||
.updateIssuable(this.store.formState)
|
||||
.then((res) => res.data)
|
||||
.then((data) => this.checkForSpam(data))
|
||||
.then((data) => {
|
||||
if (!window.location.pathname.includes(data.web_url)) {
|
||||
visitUrl(data.web_url);
|
||||
|
@ -361,30 +360,24 @@ export default {
|
|||
eventHub.$emit('close.form');
|
||||
})
|
||||
.catch((error = {}) => {
|
||||
const { name, response = {} } = error;
|
||||
const { message, response = {} } = error;
|
||||
|
||||
if (name === 'SpamError') {
|
||||
this.openRecaptcha();
|
||||
} else {
|
||||
let errMsg = this.defaultErrorMessage;
|
||||
this.store.setFormState({
|
||||
updateLoading: false,
|
||||
});
|
||||
|
||||
if (response.data && response.data.errors) {
|
||||
errMsg += `. ${response.data.errors.join(' ')}`;
|
||||
}
|
||||
let errMsg = this.defaultErrorMessage;
|
||||
|
||||
createFlash(errMsg);
|
||||
if (response.data && response.data.errors) {
|
||||
errMsg += `. ${response.data.errors.join(' ')}`;
|
||||
} else if (message) {
|
||||
errMsg += `. ${message}`;
|
||||
}
|
||||
|
||||
this.flashContainer = createFlash(errMsg);
|
||||
});
|
||||
},
|
||||
|
||||
closeRecaptchaModal() {
|
||||
this.store.setFormState({
|
||||
updateLoading: false,
|
||||
});
|
||||
|
||||
this.closeRecaptcha();
|
||||
},
|
||||
|
||||
deleteIssuable(payload) {
|
||||
return this.service
|
||||
.deleteIssuable(payload)
|
||||
|
@ -409,6 +402,13 @@ export default {
|
|||
showStickyHeader() {
|
||||
this.isStickyHeaderShowing = true;
|
||||
},
|
||||
|
||||
clearFlash() {
|
||||
if (this.flashContainer) {
|
||||
this.flashContainer.style.display = 'none';
|
||||
this.flashContainer = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -430,13 +430,6 @@ export default {
|
|||
:enable-autocomplete="enableAutocomplete"
|
||||
:issuable-type="issuableType"
|
||||
/>
|
||||
|
||||
<recaptcha-modal
|
||||
v-show="showRecaptcha"
|
||||
ref="recaptchaModal"
|
||||
:html="recaptchaHTML"
|
||||
@close="closeRecaptchaModal"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<title-component
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { registerCaptchaModalInterceptor } from '~/captcha/captcha_modal_axios_interceptor';
|
||||
import axios from '../../lib/utils/axios_utils';
|
||||
|
||||
export default class Service {
|
||||
constructor(endpoint) {
|
||||
this.endpoint = `${endpoint}.json`;
|
||||
this.realtimeEndpoint = `${endpoint}/realtime_changes`;
|
||||
registerCaptchaModalInterceptor(axios);
|
||||
}
|
||||
|
||||
getData() {
|
||||
|
|
|
@ -19,6 +19,7 @@ const httpStatusCodes = {
|
|||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
METHOD_NOT_ALLOWED: 405,
|
||||
CONFLICT: 409,
|
||||
GONE: 410,
|
||||
UNPROCESSABLE_ENTITY: 422,
|
||||
|
|
|
@ -21,7 +21,7 @@ export default {
|
|||
GlSprintf,
|
||||
},
|
||||
props: {
|
||||
defaultBranch: {
|
||||
currentBranch: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
|
@ -40,23 +40,23 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
message: this.defaultMessage,
|
||||
branch: this.defaultBranch,
|
||||
openMergeRequest: false,
|
||||
targetBranch: this.currentBranch,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isDefaultBranch() {
|
||||
return this.branch === this.defaultBranch;
|
||||
isCurrentBranchTarget() {
|
||||
return this.targetBranch === this.currentBranch;
|
||||
},
|
||||
submitDisabled() {
|
||||
return !(this.message && this.branch);
|
||||
return !(this.message && this.targetBranch);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
this.$emit('submit', {
|
||||
message: this.message,
|
||||
branch: this.branch,
|
||||
targetBranch: this.targetBranch,
|
||||
openMergeRequest: this.openMergeRequest,
|
||||
});
|
||||
},
|
||||
|
@ -100,12 +100,12 @@ export default {
|
|||
>
|
||||
<gl-form-input
|
||||
id="target-branch-field"
|
||||
v-model="branch"
|
||||
v-model="targetBranch"
|
||||
class="gl-font-monospace!"
|
||||
required
|
||||
/>
|
||||
<gl-form-checkbox
|
||||
v-if="!isDefaultBranch"
|
||||
v-if="!isCurrentBranchTarget"
|
||||
v-model="openMergeRequest"
|
||||
data-testid="new-mr-checkbox"
|
||||
class="gl-mt-3"
|
||||
|
|
|
@ -4,6 +4,7 @@ import { __, s__, sprintf } from '~/locale';
|
|||
import { COMMIT_FAILURE, COMMIT_SUCCESS } from '../../constants';
|
||||
import commitCIFile from '../../graphql/mutations/commit_ci_file.mutation.graphql';
|
||||
import getCommitSha from '../../graphql/queries/client/commit_sha.graphql';
|
||||
import getCurrentBranch from '../../graphql/queries/client/current_branch.graphql';
|
||||
|
||||
import CommitForm from './commit_form.vue';
|
||||
|
||||
|
@ -21,7 +22,7 @@ export default {
|
|||
components: {
|
||||
CommitForm,
|
||||
},
|
||||
inject: ['projectFullPath', 'ciConfigPath', 'defaultBranch', 'newMergeRequestPath'],
|
||||
inject: ['projectFullPath', 'ciConfigPath', 'newMergeRequestPath'],
|
||||
props: {
|
||||
ciFileContent: {
|
||||
type: String,
|
||||
|
@ -38,6 +39,9 @@ export default {
|
|||
commitSha: {
|
||||
query: getCommitSha,
|
||||
},
|
||||
currentBranch: {
|
||||
query: getCurrentBranch,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
defaultCommitMessage() {
|
||||
|
@ -49,13 +53,13 @@ export default {
|
|||
const url = mergeUrlParams(
|
||||
{
|
||||
[MR_SOURCE_BRANCH]: sourceBranch,
|
||||
[MR_TARGET_BRANCH]: this.defaultBranch,
|
||||
[MR_TARGET_BRANCH]: this.currentBranch,
|
||||
},
|
||||
this.newMergeRequestPath,
|
||||
);
|
||||
redirectTo(url);
|
||||
},
|
||||
async onCommitSubmit({ message, branch, openMergeRequest }) {
|
||||
async onCommitSubmit({ message, targetBranch, openMergeRequest }) {
|
||||
this.isSaving = true;
|
||||
|
||||
try {
|
||||
|
@ -67,8 +71,8 @@ export default {
|
|||
mutation: commitCIFile,
|
||||
variables: {
|
||||
projectPath: this.projectFullPath,
|
||||
branch,
|
||||
startBranch: this.defaultBranch,
|
||||
branch: targetBranch,
|
||||
startBranch: this.currentBranch,
|
||||
message,
|
||||
filePath: this.ciConfigPath,
|
||||
content: this.ciFileContent,
|
||||
|
@ -86,7 +90,7 @@ export default {
|
|||
if (errors?.length) {
|
||||
this.$emit('showError', { type: COMMIT_FAILURE, reasons: errors });
|
||||
} else if (openMergeRequest) {
|
||||
this.redirectToNewMergeRequest(branch);
|
||||
this.redirectToNewMergeRequest(targetBranch);
|
||||
} else {
|
||||
this.$emit('commit', { type: COMMIT_SUCCESS });
|
||||
}
|
||||
|
@ -105,7 +109,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<commit-form
|
||||
:default-branch="defaultBranch"
|
||||
:current-branch="currentBranch"
|
||||
:default-message="defaultCommitMessage"
|
||||
:is-saving="isSaving"
|
||||
@cancel="onCommitCancel"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
query getCurrentBranch {
|
||||
currentBranch @client
|
||||
}
|
|
@ -22,6 +22,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
|
|||
const {
|
||||
// Add to apollo cache as it can be updated by future queries
|
||||
commitSha,
|
||||
initialBranchName,
|
||||
// Add to provide/inject API for static values
|
||||
ciConfigPath,
|
||||
defaultBranch,
|
||||
|
@ -42,6 +43,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
|
|||
|
||||
apolloProvider.clients.defaultClient.cache.writeData({
|
||||
data: {
|
||||
currentBranch: initialBranchName || defaultBranch,
|
||||
commitSha,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import PipelineEditorEmptyState from './components/ui/pipeline_editor_empty_stat
|
|||
import { COMMIT_FAILURE, COMMIT_SUCCESS, DEFAULT_FAILURE, LOAD_FAILURE_UNKNOWN } from './constants';
|
||||
import getBlobContent from './graphql/queries/blob_content.graphql';
|
||||
import getCiConfigData from './graphql/queries/ci_config.graphql';
|
||||
import getCurrentBranch from './graphql/queries/client/current_branch.graphql';
|
||||
import PipelineEditorHome from './pipeline_editor_home.vue';
|
||||
|
||||
export default {
|
||||
|
@ -23,9 +24,6 @@ export default {
|
|||
ciConfigPath: {
|
||||
default: '',
|
||||
},
|
||||
defaultBranch: {
|
||||
default: null,
|
||||
},
|
||||
projectFullPath: {
|
||||
default: '',
|
||||
},
|
||||
|
@ -58,7 +56,7 @@ export default {
|
|||
return {
|
||||
projectPath: this.projectFullPath,
|
||||
path: this.ciConfigPath,
|
||||
ref: this.defaultBranch,
|
||||
ref: this.currentBranch,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
|
@ -97,6 +95,9 @@ export default {
|
|||
this.reportFailure(LOAD_FAILURE_UNKNOWN);
|
||||
},
|
||||
},
|
||||
currentBranch: {
|
||||
query: getCurrentBranch,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasUnsavedChanges() {
|
||||
|
|
|
@ -221,7 +221,10 @@ export default {
|
|||
this.captchaResponse = captchaResponse;
|
||||
|
||||
if (this.captchaResponse) {
|
||||
// If the user solved the captcha resubmit the form.
|
||||
// If the user solved the captcha, resubmit the form.
|
||||
// NOTE: we do not need to clear out the captchaResponse and spamLogId
|
||||
// data values after submit, because this component always does a full page reload.
|
||||
// Otherwise, we would need to.
|
||||
this.handleFormSubmit();
|
||||
} else {
|
||||
// If the user didn't solve the captcha (e.g. they just closed the modal),
|
||||
|
|
|
@ -463,7 +463,7 @@ class ApplicationController < ActionController::Base
|
|||
feature_category: feature_category) do
|
||||
yield
|
||||
ensure
|
||||
@current_context = Labkit::Context.current.to_h
|
||||
@current_context = Gitlab::ApplicationContext.current
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
module SpammableActions
|
||||
extend ActiveSupport::Concern
|
||||
include Spam::Concerns::HasSpamActionResponseFields
|
||||
|
||||
included do
|
||||
before_action :authorize_submit_spammable!, only: :mark_as_spam
|
||||
|
@ -25,14 +26,20 @@ module SpammableActions
|
|||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
# NOTE: format.html is still used by issue create, and uses the legacy HAML
|
||||
# `_recaptcha_form.html.haml` rendered via the `projects/issues/verify` template.
|
||||
render :verify
|
||||
end
|
||||
|
||||
format.json do
|
||||
locals = { spammable: spammable, script: false, has_submit: false }
|
||||
recaptcha_html = render_to_string(partial: 'shared/recaptcha_form', formats: :html, locals: locals)
|
||||
# format.json is used by all new Vue-based CAPTCHA implementations, which
|
||||
# handle all of the CAPTCHA form rendering on the client via the Pajamas-based
|
||||
# app/assets/javascripts/captcha/captcha_modal.vue
|
||||
|
||||
render json: { recaptcha_html: recaptcha_html }
|
||||
# NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response
|
||||
# which requires a CAPTCHA to be solved in order for the request to be resubmitted.
|
||||
# See https://stackoverflow.com/q/26547466/25192
|
||||
render json: spam_action_response_fields(spammable), status: :conflict
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -58,7 +65,7 @@ module SpammableActions
|
|||
|
||||
# After this newer GraphQL/JS API process is fully supported by the backend, we can remove the
|
||||
# check for the 'g-recaptcha-response' field and other HTML/HAML form-specific support.
|
||||
captcha_response = params['g-recaptcha-response']
|
||||
captcha_response = params['g-recaptcha-response'] || params[:captcha_response]
|
||||
|
||||
{
|
||||
request: request,
|
||||
|
|
|
@ -50,6 +50,8 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def pipelines
|
||||
set_pipeline_feature_flag
|
||||
|
||||
@pipelines = @commit.pipelines.order(id: :desc)
|
||||
@pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref]
|
||||
|
||||
|
@ -124,6 +126,10 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def set_pipeline_feature_flag
|
||||
push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def create_new_branch?
|
||||
params[:create_merge_request].present? || !can?(current_user, :push_code, @project)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Mutations
|
|||
|
||||
ADMIN_MESSAGE = 'You must be an admin to use this mutation'
|
||||
|
||||
Labkit::Context::KNOWN_KEYS.each do |key|
|
||||
Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
|
||||
argument key,
|
||||
GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
|
|
|
@ -89,7 +89,7 @@ class NotifyPreview < ActionMailer::Preview
|
|||
end
|
||||
|
||||
def merge_request_status_email
|
||||
Notify.merge_request_status_email(user.id, merge_request.id, 'closed', user.id).message
|
||||
Notify.merge_request_status_email(user.id, merge_request.id, 'reopened', user.id).message
|
||||
end
|
||||
|
||||
def merged_merge_request_email
|
||||
|
|
|
@ -6,6 +6,8 @@ class WebHookLog < ApplicationRecord
|
|||
include DeleteWithLimit
|
||||
include CreatedAtFilterable
|
||||
|
||||
self.primary_key = :id
|
||||
|
||||
belongs_to :web_hook
|
||||
|
||||
serialize :request_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
|
17
app/models/hooks/web_hook_log_partitioned.rb
Normal file
17
app/models/hooks/web_hook_log_partitioned.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This model is not yet intended to be used.
|
||||
# It is in a transitioning phase while we are partitioning
|
||||
# the web_hook_logs table on the database-side.
|
||||
# Please refer to https://gitlab.com/groups/gitlab-org/-/epics/5558
|
||||
# for details.
|
||||
# rubocop:disable Gitlab/NamespacedClass: This is a temporary class with no relevant namespace
|
||||
# WebHook, WebHookLog and all hooks are defined outside of a namespace
|
||||
class WebHookLogPartitioned < ApplicationRecord
|
||||
include PartitionedTable
|
||||
|
||||
self.table_name = 'web_hook_logs_part_0c5294f417'
|
||||
self.primary_key = :id
|
||||
|
||||
partitioned_by :created_at, strategy: :monthly
|
||||
end
|
|
@ -30,7 +30,6 @@ class Note < ApplicationRecord
|
|||
|
||||
# Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with notes.
|
||||
# See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/10392/diffs#note_28719102
|
||||
alias_attribute :last_edited_at, :updated_at
|
||||
alias_attribute :last_edited_by, :updated_by
|
||||
|
||||
# Attribute containing rendered and redacted Markdown as generated by
|
||||
|
@ -349,7 +348,13 @@ class Note < ApplicationRecord
|
|||
!system?
|
||||
end
|
||||
|
||||
# Since we're using `updated_at` as `last_edited_at`, it could be touched by transforming / resolving a note.
|
||||
# We used `last_edited_at` as an alias of `updated_at` before.
|
||||
# This makes it compatible with the previous way without data migration.
|
||||
def last_edited_at
|
||||
super || updated_at
|
||||
end
|
||||
|
||||
# Since we used `updated_at` as `last_edited_at`, it could be touched by transforming / resolving a note.
|
||||
# This makes sure it is only marked as edited when the note body is updated.
|
||||
def edited?
|
||||
return false if updated_by.blank?
|
||||
|
|
|
@ -7,12 +7,7 @@ module Notes
|
|||
|
||||
old_mentioned_users = note.mentioned_users(current_user).to_a
|
||||
|
||||
note.assign_attributes(params.merge(updated_by: current_user))
|
||||
|
||||
note.with_transaction_returning_status do
|
||||
update_confidentiality(note)
|
||||
note.save
|
||||
end
|
||||
note.assign_attributes(params)
|
||||
|
||||
track_note_edit_usage_for_issues(note) if note.for_issue?
|
||||
track_note_edit_usage_for_merge_requests(note) if note.for_merge_request?
|
||||
|
@ -28,6 +23,15 @@ module Notes
|
|||
note.note = content
|
||||
end
|
||||
|
||||
if note.note_changed?
|
||||
note.assign_attributes(last_edited_at: Time.current, updated_by: current_user)
|
||||
end
|
||||
|
||||
note.with_transaction_returning_status do
|
||||
update_confidentiality(note)
|
||||
note.save
|
||||
end
|
||||
|
||||
unless only_commands || note.for_personal_snippet?
|
||||
note.create_new_cross_references!(current_user)
|
||||
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
.form-text.text-muted
|
||||
Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -65,4 +65,4 @@
|
|||
|
||||
= render_if_exists 'admin/application_settings/updating_name_disabled_for_users', form: f
|
||||
= render_if_exists 'admin/application_settings/availability_on_namespace_setting', form: f
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-success qa-save-changes-button'
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm qa-save-changes-button'
|
||||
|
|
|
@ -68,4 +68,4 @@
|
|||
= _("The default CI configuration path for new projects.").html_safe
|
||||
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'custom-cicd-configuration-path'), target: '_blank'
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
= link_to sprite_icon('question-o'),
|
||||
help_page_path('user/admin_area/diff_limits',
|
||||
anchor: 'maximum-diff-patch-size')
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-success'
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -33,4 +33,4 @@
|
|||
.form-text.text-muted
|
||||
= _('AWS Secret Access Key. Only required if not using role instance credentials')
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -33,4 +33,4 @@
|
|||
.form-text.text-muted
|
||||
= _('By default, GitLab sends emails to help guide users through the onboarding process.')
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -47,4 +47,4 @@
|
|||
.form-group
|
||||
= f.label :external_authorization_service_default_label, _('Default classification label'), class: 'label-bold'
|
||||
= f.text_field :external_authorization_service_default_label, class: 'form-control gl-form-input'
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
.form-text.text-muted
|
||||
Medium operation timeout (in seconds). This should be a value between the Fast and the Default timeout.
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -25,4 +25,4 @@
|
|||
= f.text_field :gitpod_url, class: 'form-control gl-form-input', placeholder: s_('Gitpod|e.g. https://gitpod.example.com')
|
||||
.form-text.text-muted
|
||||
= s_('Gitpod|Add the URL to your Gitpod instance configured to read your GitLab projects.')
|
||||
= f.submit s_('Save changes'), class: 'gl-button btn btn-success'
|
||||
= f.submit s_('Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -14,4 +14,4 @@
|
|||
= f.label :grafana_url, _('Grafana URL'), class: 'label-bold'
|
||||
= f.text_field :grafana_url, class: 'form-control gl-form-input', placeholder: '/-/grafana'
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
= f.label :help_page_documentation_base_url, _('Documentation pages URL'), class: 'label-bold'
|
||||
= f.text_field :help_page_documentation_base_url, class: 'form-control gl-form-input', placeholder: 'https://docs.gitlab.com'
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
= f.label :group_download_export_limit, _('Max Group Export Download requests per minute per user'), class: 'label-bold'
|
||||
= f.number_field :group_download_export_limit, class: 'form-control gl-form-input'
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
%span.form-text.text-muted
|
||||
= (_("Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used.") % { branch_name_default: fallback_branch_name } ).html_safe
|
||||
|
||||
= f.submit _('Save changes'), class: 'gl-button btn-success'
|
||||
= f.submit _('Save changes'), class: 'gl-button btn-confirm'
|
||||
|
|
|
@ -57,4 +57,4 @@
|
|||
= _('A plain-text response to show to clients that hit the rate limit.')
|
||||
= f.text_area :rate_limiting_response_text, placeholder: ::Gitlab::Throttle::DEFAULT_RATE_LIMITING_RESPONSE_TEXT, class: 'form-control gl-form-input', rows: 5
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
= f.label :issues_create_limit, 'Max requests per minute per user', class: 'label-bold'
|
||||
= f.number_field :issues_create_limit, class: 'form-control gl-form-input'
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -30,4 +30,4 @@
|
|||
= f.check_box format[:name], class: 'form-check-input'
|
||||
= f.label format[:name], format[:label], class: 'form-check-label'
|
||||
|
||||
= f.submit _('Save changes'), class: "btn gl-button btn-success"
|
||||
= f.submit _('Save changes'), class: "btn gl-button btn-confirm"
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
= f.label :time_tracking_limit_to_hours, class: 'form-check-label' do
|
||||
= _('Limit display of time tracking units to hours.')
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -9,4 +9,4 @@
|
|||
= f.label :notes_create_limit_allowlist, _('List of users to be excluded from the limit'), class: 'label-bold'
|
||||
= f.text_area :notes_create_limit_allowlist_raw, placeholder: 'username1, username2', class: 'form-control gl-form-input', rows: 5
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -27,4 +27,4 @@
|
|||
%span.form-text.text-muted
|
||||
= _('Resolves IP addresses once and uses them to submit requests')
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -47,4 +47,4 @@
|
|||
.form-group
|
||||
= f.label :generic_packages_max_file_size, _('Generic package file size in bytes'), class: 'label-bold'
|
||||
= f.number_field :generic_packages_max_file_size, class: 'form-control gl-form-input'
|
||||
= f.submit _('Save %{name} size limits').html_safe % { name: plan.name.capitalize }, class: 'btn gl-button btn-success'
|
||||
= f.submit _('Save %{name} size limits').html_safe % { name: plan.name.capitalize }, class: 'btn gl-button btn-confirm'
|
||||
|
|
|
@ -41,4 +41,4 @@
|
|||
- terms_of_service_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: lets_encrypt_terms_of_service_admin_application_settings_path }
|
||||
= _("I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end} (PDF)").html_safe % { link_start: terms_of_service_link_start, link_end: '</a>'.html_safe }
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
.form-text.text-muted
|
||||
= _('Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push event will be created. Bulk push event will be created if it surpasses that value.')
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -11,4 +11,4 @@
|
|||
= f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-bold'
|
||||
= f.text_field :performance_bar_allowed_group_path, class: 'form-control gl-form-input', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
|
||||
|
||||
= f.submit 'Save changes', class: 'gl-button btn btn-success qa-save-changes-button'
|
||||
= f.submit 'Save changes', class: 'gl-button btn btn-confirm qa-save-changes-button'
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
= link_to "PlantUML", "http://plantuml.com"
|
||||
diagrams in Asciidoc documents using an external PlantUML service.
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -30,4 +30,4 @@
|
|||
A method call is only tracked when it takes longer to complete than
|
||||
the given amount of milliseconds.
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -28,4 +28,4 @@
|
|||
= _('All paths are relative to the GitLab URL. Do not include %{relative_url_link_start}relative URL%{relative_url_link_end}.').html_safe % { relative_url_link_start: relative_url_link_start, relative_url_link_end: '</a>'.html_safe }
|
||||
= f.text_area :protected_paths_raw, placeholder: '/users/sign_in,/users/password', class: 'form-control gl-form-input', rows: 10
|
||||
|
||||
= f.submit 'Save changes', class: 'gl-button btn btn-success'
|
||||
= f.submit 'Save changes', class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -14,4 +14,4 @@
|
|||
installations. Set to 0 to completely disable polling.
|
||||
= link_to sprite_icon('question-o'), help_page_path('administration/polling')
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
.form-text.text-muted
|
||||
= _("The maximum number of tags that a single worker accepts for cleanup. If the number of tags goes above this limit, the list of tags to delete is truncated to this number. To remove this limit, set it to 0.")
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -55,4 +55,4 @@
|
|||
.form-text.text-muted
|
||||
Number of Git pushes after which 'git gc' is run.
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -14,4 +14,4 @@
|
|||
|
||||
= render_if_exists 'admin/application_settings/mirror_settings', form: f
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
%span.form-text.text-muted#static_objects_external_storage_auth_token_help_block
|
||||
= _('A secure token that identifies an external storage request.')
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -22,4 +22,4 @@
|
|||
= f.text_field attribute[:name], class: 'form-text-input', value: attribute[:value]
|
||||
= f.label attribute[:label], attribute[:label], class: 'label-bold form-check-label'
|
||||
%br
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success qa-save-changes-button"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -57,4 +57,4 @@
|
|||
= f.label :sign_in_text, _('Sign-in text'), class: 'label-bold'
|
||||
= f.text_area :sign_in_text, class: 'form-control gl-form-input', rows: 4
|
||||
.form-text.text-muted Markdown enabled
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -77,4 +77,4 @@
|
|||
= f.label :after_sign_up_text, class: 'label-bold'
|
||||
= f.text_area :after_sign_up_text, class: 'form-control gl-form-input', rows: 4
|
||||
.form-text.text-muted Markdown enabled
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
|
||||
|
|
|
@ -26,4 +26,4 @@
|
|||
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
|
||||
= f.text_field :snowplow_cookie_domain, class: 'form-control gl-form-input'
|
||||
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-success'
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -35,4 +35,4 @@
|
|||
= f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|e.g. https://sourcegraph.example.com')
|
||||
.form-text.text-muted
|
||||
= s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.')
|
||||
= f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-success'
|
||||
= f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -79,4 +79,4 @@
|
|||
= f.label :spam_check_endpoint_url, _('URL of the external Spam Check endpoint'), class: 'label-bold'
|
||||
= f.text_field :spam_check_endpoint_url, class: 'form-control gl-form-input'
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
.form-text.text-muted
|
||||
Maximum time for web terminal websocket connection (in seconds).
|
||||
0 for unlimited.
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
= f.text_area :terms, class: 'form-control gl-form-input', rows: 8
|
||||
.form-text.text-muted
|
||||
= _("Markdown enabled")
|
||||
= f.submit _("Save changes"), class: "gl-button btn btn-success"
|
||||
= f.submit _("Save changes"), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
= f.check_box :hide_third_party_offers, class: 'form-check-input'
|
||||
= f.label :hide_third_party_offers, _('Do not display offers from third parties within GitLab'), class: 'form-check-label'
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -37,4 +37,4 @@
|
|||
- deactivating_usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: deactivating_usage_ping_path }
|
||||
= s_('For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}.').html_safe % { deactivating_usage_ping_link_start: deactivating_usage_ping_link_start, deactivating_usage_ping_link_end: '</a>'.html_safe }
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-success"
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -74,4 +74,4 @@
|
|||
= f.label :disable_feed_token, class: 'form-check-label' do
|
||||
= s_('AdminSettings|Disable feed token')
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
= s_('IDE|Live Preview')
|
||||
%span.form-text.text-muted
|
||||
= s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview.')
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
||||
= render_if_exists 'admin/application_settings/maintenance_mode_settings_form'
|
||||
= render 'admin/application_settings/gitpod'
|
||||
|
|
|
@ -6,3 +6,4 @@ Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @m
|
|||
|
||||
Author: #{sanitize_name(@merge_request.author_name)}
|
||||
= assignees_label(@merge_request)
|
||||
= reviewers_label(@merge_request)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"commit-sha" => @project.commit ? @project.commit.sha : '',
|
||||
"default-branch" => @project.default_branch,
|
||||
"empty-state-illustration-path" => image_path('illustrations/empty-state/empty-dag-md.svg'),
|
||||
"initial-branch-name": params[:branch_name],
|
||||
"lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'),
|
||||
"new-merge-request-path" => namespace_project_new_merge_request_path,
|
||||
"project-path" => @project.path,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
%section.settings.project-mirror-settings.no-animate#js-push-remote-settings{ class: mirror_settings_class, data: { qa_selector: 'mirroring_repositories_settings_content' } }
|
||||
.settings-header
|
||||
%h4= _('Mirroring repositories')
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Mirroring repositories')
|
||||
%button.btn.gl-button.btn-default.js-settings-toggle
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
|
|
|
@ -18,7 +18,7 @@ module ApplicationWorker
|
|||
set_queue
|
||||
|
||||
def structured_payload(payload = {})
|
||||
context = Labkit::Context.current.to_h.merge(
|
||||
context = Gitlab::ApplicationContext.current.merge(
|
||||
'class' => self.class.name,
|
||||
'job_status' => 'running',
|
||||
'queue' => self.class.queue,
|
||||
|
|
|
@ -15,7 +15,7 @@ module CronjobQueue
|
|||
# Cronjobs never get scheduled with arguments, so this is safe to
|
||||
# override
|
||||
def context_for_arguments(_args)
|
||||
return if Gitlab::ApplicationContext.current_context_include?('meta.caller_id')
|
||||
return if Gitlab::ApplicationContext.current_context_include?(:caller_id)
|
||||
|
||||
Gitlab::ApplicationContext.new(caller_id: "Cronjob")
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Project Settings Repository Mirroring repositories header expands/collapses on click / tap
|
||||
merge_request: 55229
|
||||
author: Daniel Schömer
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add reviewers detail to merge request status email
|
||||
merge_request: 55584
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/322901-fix-pagerduty-webhook.yml
Normal file
5
changelogs/unreleased/322901-fix-pagerduty-webhook.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'Fixes: No such file or directory lib/pager_duty/validator/schemas/message.json'
|
||||
merge_request: 55725
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add web_hook_logs partitioning migration
|
||||
merge_request: 55938
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move to btn-confirm from btn-success in admin application_settings directory
|
||||
merge_request: 55263
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
5
changelogs/unreleased/fix-edited-timestamp.yml
Normal file
5
changelogs/unreleased/fix-edited-timestamp.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix edited timestamp updated when transforming / resolving comments
|
||||
merge_request: 55671
|
||||
author: Mycroft Kang @TaehyeokKang
|
||||
type: fixed
|
5
changelogs/unreleased/pipeline-editor-branch-param.yml
Normal file
5
changelogs/unreleased/pipeline-editor-branch-param.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow users to work on non-default branch in pipeline editor
|
||||
merge_request: 55413
|
||||
author:
|
||||
type: added
|
|
@ -4,6 +4,7 @@
|
|||
# (even with eager loading disabled).
|
||||
|
||||
Gitlab::Database::Partitioning::PartitionCreator.register(AuditEvent)
|
||||
Gitlab::Database::Partitioning::PartitionCreator.register(WebHookLogPartitioned)
|
||||
|
||||
begin
|
||||
Gitlab::Database::Partitioning::PartitionCreator.new.create_partitions unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddLastEditedAtAndLastEditedByIdToNotes < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :notes, :last_edited_at, :datetime_with_timezone
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :notes, :last_edited_at
|
||||
end
|
||||
end
|
||||
end
|
19
db/migrate/20210306121300_partition_web_hook_logs.rb
Normal file
19
db/migrate/20210306121300_partition_web_hook_logs.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PartitionWebHookLogs < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
include Gitlab::Database::PartitioningMigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
partition_table_by_date :web_hook_logs, :created_at
|
||||
end
|
||||
|
||||
def down
|
||||
drop_partitioned_table_for :web_hook_logs
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BackfillPartitionedWebHookLogs < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::PartitioningMigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
enqueue_partitioning_data_migration :web_hook_logs
|
||||
end
|
||||
|
||||
def down
|
||||
cleanup_partitioning_data_migration :web_hook_logs
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210303053341
Normal file
1
db/schema_migrations/20210303053341
Normal file
|
@ -0,0 +1 @@
|
|||
3bd7e839c4f93716a7e893bf9184306a1fcfd401e5b54f4393e5138e2776f5e0
|
1
db/schema_migrations/20210306121300
Normal file
1
db/schema_migrations/20210306121300
Normal file
|
@ -0,0 +1 @@
|
|||
44d53ac15c5e54c2f1c825286155dec643b82573184026caaf08288512168aef
|
1
db/schema_migrations/20210306121310
Normal file
1
db/schema_migrations/20210306121310
Normal file
|
@ -0,0 +1 @@
|
|||
90072e3dee4517061ff9e08decda7fecb9cc9b38a56345c09685e3cce48a8b66
|
|
@ -41,6 +41,62 @@ RETURN NULL;
|
|||
END
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION table_sync_function_29bc99d6db() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF (TG_OP = 'DELETE') THEN
|
||||
DELETE FROM web_hook_logs_part_0c5294f417 where id = OLD.id;
|
||||
ELSIF (TG_OP = 'UPDATE') THEN
|
||||
UPDATE web_hook_logs_part_0c5294f417
|
||||
SET web_hook_id = NEW.web_hook_id,
|
||||
trigger = NEW.trigger,
|
||||
url = NEW.url,
|
||||
request_headers = NEW.request_headers,
|
||||
request_data = NEW.request_data,
|
||||
response_headers = NEW.response_headers,
|
||||
response_body = NEW.response_body,
|
||||
response_status = NEW.response_status,
|
||||
execution_duration = NEW.execution_duration,
|
||||
internal_error_message = NEW.internal_error_message,
|
||||
updated_at = NEW.updated_at,
|
||||
created_at = NEW.created_at
|
||||
WHERE web_hook_logs_part_0c5294f417.id = NEW.id;
|
||||
ELSIF (TG_OP = 'INSERT') THEN
|
||||
INSERT INTO web_hook_logs_part_0c5294f417 (id,
|
||||
web_hook_id,
|
||||
trigger,
|
||||
url,
|
||||
request_headers,
|
||||
request_data,
|
||||
response_headers,
|
||||
response_body,
|
||||
response_status,
|
||||
execution_duration,
|
||||
internal_error_message,
|
||||
updated_at,
|
||||
created_at)
|
||||
VALUES (NEW.id,
|
||||
NEW.web_hook_id,
|
||||
NEW.trigger,
|
||||
NEW.url,
|
||||
NEW.request_headers,
|
||||
NEW.request_data,
|
||||
NEW.response_headers,
|
||||
NEW.response_body,
|
||||
NEW.response_status,
|
||||
NEW.execution_duration,
|
||||
NEW.internal_error_message,
|
||||
NEW.updated_at,
|
||||
NEW.created_at);
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
|
||||
END
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION table_sync_function_29bc99d6db() IS 'Partitioning migration: table sync for web_hook_logs table';
|
||||
|
||||
CREATE FUNCTION table_sync_function_2be879775d() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
|
@ -114,6 +170,23 @@ CREATE TABLE audit_events (
|
|||
)
|
||||
PARTITION BY RANGE (created_at);
|
||||
|
||||
CREATE TABLE web_hook_logs_part_0c5294f417 (
|
||||
id bigint NOT NULL,
|
||||
web_hook_id integer NOT NULL,
|
||||
trigger character varying,
|
||||
url character varying,
|
||||
request_headers text,
|
||||
request_data text,
|
||||
response_headers text,
|
||||
response_body text,
|
||||
response_status character varying,
|
||||
execution_duration double precision,
|
||||
internal_error_message character varying,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
created_at timestamp without time zone NOT NULL
|
||||
)
|
||||
PARTITION BY RANGE (created_at);
|
||||
|
||||
CREATE TABLE product_analytics_events_experimental (
|
||||
id bigint NOT NULL,
|
||||
project_id integer NOT NULL,
|
||||
|
@ -14613,7 +14686,8 @@ CREATE TABLE notes (
|
|||
change_position text,
|
||||
resolved_by_push boolean,
|
||||
review_id bigint,
|
||||
confidential boolean
|
||||
confidential boolean,
|
||||
last_edited_at timestamp with time zone
|
||||
);
|
||||
|
||||
CREATE SEQUENCE notes_id_seq
|
||||
|
@ -21254,6 +21328,9 @@ ALTER TABLE ONLY vulnerability_statistics
|
|||
ALTER TABLE ONLY vulnerability_user_mentions
|
||||
ADD CONSTRAINT vulnerability_user_mentions_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY web_hook_logs_part_0c5294f417
|
||||
ADD CONSTRAINT web_hook_logs_part_0c5294f417_pkey PRIMARY KEY (id, created_at);
|
||||
|
||||
ALTER TABLE ONLY web_hook_logs
|
||||
ADD CONSTRAINT web_hook_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -24319,6 +24396,8 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p
|
|||
|
||||
ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_partitions_static.product_analytics_events_experimental_63_pkey;
|
||||
|
||||
CREATE TRIGGER table_sync_trigger_b99eb6998c AFTER INSERT OR DELETE OR UPDATE ON web_hook_logs FOR EACH ROW EXECUTE PROCEDURE table_sync_function_29bc99d6db();
|
||||
|
||||
CREATE TRIGGER table_sync_trigger_ee39a25f9d AFTER INSERT OR DELETE OR UPDATE ON audit_events FOR EACH ROW EXECUTE PROCEDURE table_sync_function_2be879775d();
|
||||
|
||||
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON services FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_issue_tracker();
|
||||
|
|
|
@ -14,10 +14,12 @@ Find more about them [in Audit Events documentation](audit_events.md).
|
|||
System log files are typically plain text in a standard log file format.
|
||||
This guide talks about how to read and use these system log files.
|
||||
|
||||
Read more about how to
|
||||
[customize logging on Omnibus GitLab installations](https://docs.gitlab.com/omnibus/settings/logs.html)
|
||||
Read more about the log system and using the logs:
|
||||
|
||||
- [Customize logging on Omnibus GitLab installations](https://docs.gitlab.com/omnibus/settings/logs.html)
|
||||
including adjusting log retention, log forwarding,
|
||||
switching logs from JSON to plain text logging, and more.
|
||||
- [How to parse and analyze JSON logs](troubleshooting/log_parsing.md).
|
||||
|
||||
## `production_json.log`
|
||||
|
||||
|
|
|
@ -148,3 +148,23 @@ Traceback (most recent call last):
|
|||
[traceback removed]
|
||||
/opt/gitlab/..../runner_command.rb:42:in `load': cannot load such file -- /tmp/helloworld.rb (LoadError)
|
||||
```
|
||||
|
||||
In case you encouter a similar error to this:
|
||||
|
||||
```plaintext
|
||||
[root ~]# sudo gitlab-rails runner helloworld.rb
|
||||
Please specify a valid ruby command or the path of a script to run.
|
||||
Run 'rails runner -h' for help.
|
||||
|
||||
undefined local variable or method `helloworld' for main:Object
|
||||
```
|
||||
|
||||
You can either move the file to the `/tmp` directory or create a new directory onwed by the user `git` and save the script in that directory as illustrated below:
|
||||
|
||||
```shell
|
||||
sudo mkdir /scripts
|
||||
sudo mv /script_path/helloworld.rb /scripts
|
||||
sudo chown -R git:git /scripts
|
||||
sudo chmod 700 /scripts
|
||||
sudo gitlab-rails runner /scripts/helloworld.rb
|
||||
```
|
||||
|
|
|
@ -41,6 +41,20 @@ jq -cR 'fromjson?' file.json | jq <COMMAND>
|
|||
By default `jq` will error out when it encounters a line that is not valid JSON.
|
||||
This skips over all invalid lines and parses the rest.
|
||||
|
||||
#### Print a JSON log's time range
|
||||
|
||||
```shell
|
||||
cat log.json | (head -1; tail -1) | jq '.time'
|
||||
```
|
||||
|
||||
Use `zcat` if the file has been rotated and compressed:
|
||||
|
||||
```shell
|
||||
zcat @400000006026b71d1a7af804.s | (head -1; tail -1) | jq '.time'
|
||||
|
||||
zcat some_json.log.25.gz | (head -1; tail -1) | jq '.time'
|
||||
```
|
||||
|
||||
### Parsing `production_json.log` and `api_json.log`
|
||||
|
||||
#### Find all requests with a 5XX status code
|
||||
|
|
|
@ -310,6 +310,27 @@ Example response:
|
|||
]
|
||||
```
|
||||
|
||||
## Remove a billable member from a group
|
||||
|
||||
Removes a billable member from a group and its subgroups and projects.
|
||||
|
||||
The user does not need to be a group member to qualify for removal.
|
||||
For example, if the user was added directly to a project within the group, you can
|
||||
still use this API to remove them.
|
||||
|
||||
```plaintext
|
||||
DELETE /groups/:id/billable_members/:user_id
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the member |
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members/:user_id"
|
||||
```
|
||||
|
||||
## Add a member to a group or project
|
||||
|
||||
Adds a member to a group or project.
|
||||
|
|
|
@ -46,9 +46,9 @@ To create a GitLab Pages website:
|
|||
|
||||
| Document | Description |
|
||||
| -------- | ----------- |
|
||||
| [Fork a sample project](getting_started/pages_forked_sample_project.md) | Create a new project with Pages already configured by forking a sample project. |
|
||||
| [Use a new project template](getting_started/pages_new_project_template.md) | Create a new project with Pages already configured by using a new project template. |
|
||||
| [Use a `.gitlab-ci.yml` template](getting_started/pages_ci_cd_template.md) | Add a Pages site to an existing project. Use a pre-populated CI template file. |
|
||||
| [Fork a sample project](getting_started/pages_forked_sample_project.md) | Create a new project with Pages already configured by forking a sample project. |
|
||||
| [Create a `gitlab-ci.yml` file from scratch](getting_started/pages_from_scratch.md) | Add a Pages site to an existing project. Learn how to create and configure your own CI file. |
|
||||
|
||||
To update a GitLab Pages website:
|
||||
|
|
|
@ -12,11 +12,11 @@ module API
|
|||
namespace 'queues' do
|
||||
desc 'Drop jobs matching the given metadata from the Sidekiq queue'
|
||||
params do
|
||||
Labkit::Context::KNOWN_KEYS.each do |key|
|
||||
Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
|
||||
optional key, type: String, allow_blank: false
|
||||
end
|
||||
|
||||
at_least_one_of(*Labkit::Context::KNOWN_KEYS)
|
||||
at_least_one_of(*Gitlab::ApplicationContext::KNOWN_KEYS)
|
||||
end
|
||||
delete ':queue_name' do
|
||||
result =
|
||||
|
|
|
@ -7,6 +7,9 @@ module Gitlab
|
|||
|
||||
Attribute = Struct.new(:name, :type)
|
||||
|
||||
LOG_KEY = Labkit::Context::LOG_KEY
|
||||
KNOWN_KEYS = Labkit::Context::KNOWN_KEYS
|
||||
|
||||
APPLICATION_ATTRIBUTES = [
|
||||
Attribute.new(:project, Project),
|
||||
Attribute.new(:namespace, Namespace),
|
||||
|
@ -22,6 +25,10 @@ module Gitlab
|
|||
application_context.use(&block)
|
||||
end
|
||||
|
||||
def self.with_raw_context(attributes = {}, &block)
|
||||
Labkit::Context.with_context(attributes, &block)
|
||||
end
|
||||
|
||||
def self.push(args)
|
||||
application_context = new(**args)
|
||||
Labkit::Context.push(application_context.to_lazy_hash)
|
||||
|
|
|
@ -9,7 +9,7 @@ module Gitlab
|
|||
include ::Gitlab::Database::MigrationHelpers
|
||||
include ::Gitlab::Database::Migrations::BackgroundMigrationHelpers
|
||||
|
||||
ALLOWED_TABLES = %w[audit_events].freeze
|
||||
ALLOWED_TABLES = %w[audit_events web_hook_logs].freeze
|
||||
ERROR_SCOPE = 'table partitioning'
|
||||
|
||||
MIGRATION_CLASS_NAME = "::#{module_parent_name}::BackfillPartitionedTable"
|
||||
|
|
|
@ -215,7 +215,7 @@ module Gitlab
|
|||
'client_name' => CLIENT_NAME
|
||||
}
|
||||
|
||||
context_data = Labkit::Context.current&.to_h
|
||||
context_data = Gitlab::ApplicationContext.current
|
||||
|
||||
feature_stack = Thread.current[:gitaly_feature_stack]
|
||||
feature = feature_stack && feature_stack[0]
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
module Loggers
|
||||
class ContextLogger < ::GrapeLogging::Loggers::Base
|
||||
def parameters(_, _)
|
||||
Labkit::Context.current.to_h
|
||||
Gitlab::ApplicationContext.current
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ module Gitlab
|
|||
job_search_metadata =
|
||||
search_metadata
|
||||
.stringify_keys
|
||||
.slice(*Labkit::Context::KNOWN_KEYS)
|
||||
.slice(*Gitlab::ApplicationContext::KNOWN_KEYS)
|
||||
.transform_keys { |key| "meta.#{key}" }
|
||||
.compact
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module PagerDuty
|
||||
class WebhookPayloadParser
|
||||
SCHEMA_PATH = File.join('lib', 'pager_duty', 'validator', 'schemas', 'message.json')
|
||||
SCHEMA_PATH = Rails.root.join('lib', 'pager_duty', 'validator', 'schemas', 'message.json')
|
||||
|
||||
def initialize(payload)
|
||||
@payload = payload
|
||||
|
@ -66,7 +66,7 @@ module PagerDuty
|
|||
end
|
||||
|
||||
def valid_message?(message)
|
||||
::JSONSchemer.schema(Pathname.new(SCHEMA_PATH)).valid?(message)
|
||||
::JSONSchemer.schema(SCHEMA_PATH).valid?(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34456,6 +34456,9 @@ msgstr ""
|
|||
msgid "You must set up incoming email before it becomes active."
|
||||
msgstr ""
|
||||
|
||||
msgid "You must solve the CAPTCHA in order to submit"
|
||||
msgstr ""
|
||||
|
||||
msgid "You must upload a file with the same file name when dropping onto an existing design."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -900,7 +900,7 @@ RSpec.describe ApplicationController do
|
|||
feature_category :issue_tracking
|
||||
|
||||
def index
|
||||
Labkit::Context.with_context do |context|
|
||||
Gitlab::ApplicationContext.with_raw_context do |context|
|
||||
render json: context.to_h
|
||||
end
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue