Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1d388ed855
commit
6305f1dc00
|
|
@ -24,9 +24,9 @@ const JumpToDiscussion = Vue.extend({
|
|||
computed: {
|
||||
buttonText() {
|
||||
if (this.discussionId) {
|
||||
return __('Jump to next unresolved discussion');
|
||||
return __('Jump to next unresolved thread');
|
||||
} else {
|
||||
return __('Jump to first unresolved discussion');
|
||||
return __('Jump to first unresolved thread');
|
||||
}
|
||||
},
|
||||
allResolved() {
|
||||
|
|
|
|||
|
|
@ -69,13 +69,11 @@ export default () => {
|
|||
},
|
||||
},
|
||||
render(createElement) {
|
||||
const isDiffView = this.activeTab === 'diffs';
|
||||
|
||||
// NOTE: Even though `discussionKeyboardNavigator` is added to the `notes-app`,
|
||||
// it adds a global key listener so it works on the diffs tab as well.
|
||||
// If we create a single Vue app for all of the MR tabs, we should move this
|
||||
// up the tree, to the root.
|
||||
return createElement(discussionKeyboardNavigator, { props: { isDiffView } }, [
|
||||
return createElement(discussionKeyboardNavigator, [
|
||||
createElement('notes-app', {
|
||||
props: {
|
||||
noteableData: this.noteableData,
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export default {
|
|||
v-if="discussion.resolvable && shouldShowJumpToNextDiscussion"
|
||||
class="btn-group discussion-actions ml-sm-2"
|
||||
>
|
||||
<jump-to-next-discussion-button @onClick="$emit('jumpToNextDiscussion')" />
|
||||
<jump-to-next-discussion-button />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import discussionNavigation from '../mixins/discussion_navigation';
|
||||
|
|
@ -17,9 +17,7 @@ export default {
|
|||
'getUserData',
|
||||
'getNoteableData',
|
||||
'resolvableDiscussionsCount',
|
||||
'firstUnresolvedDiscussionId',
|
||||
'unresolvedDiscussionsCount',
|
||||
'getDiscussion',
|
||||
]),
|
||||
isLoggedIn() {
|
||||
return this.getUserData.id;
|
||||
|
|
@ -37,16 +35,6 @@ export default {
|
|||
return this.resolvableDiscussionsCount - this.unresolvedDiscussionsCount;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['expandDiscussion']),
|
||||
jumpToFirstUnresolvedDiscussion() {
|
||||
const diffTab = window.mrTabs.currentAction === 'diffs';
|
||||
const discussionId =
|
||||
this.firstUnresolvedDiscussionId(diffTab) || this.firstUnresolvedDiscussionId();
|
||||
const firstDiscussion = this.getDiscussion(discussionId);
|
||||
this.jumpToDiscussion(firstDiscussion);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -83,9 +71,9 @@ export default {
|
|||
<div v-if="isLoggedIn && !allResolved" class="btn-group btn-group-sm" role="group">
|
||||
<button
|
||||
v-gl-tooltip
|
||||
title="Jump to first unresolved thread"
|
||||
title="Jump to next unresolved thread"
|
||||
class="btn btn-default discussion-next-btn"
|
||||
@click="jumpToFirstUnresolvedDiscussion"
|
||||
@click="jumpToNextDiscussion"
|
||||
>
|
||||
<icon name="comment-next" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import discussionNavigation from '../mixins/discussion_navigation';
|
||||
|
||||
export default {
|
||||
name: 'JumpToNextDiscussionButton',
|
||||
|
|
@ -10,6 +11,7 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [discussionNavigation],
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -19,8 +21,8 @@ export default {
|
|||
ref="button"
|
||||
v-gl-tooltip
|
||||
class="btn btn-default discussion-next-btn"
|
||||
:title="s__('MergeRequests|Jump to next unresolved discussion')"
|
||||
@click="$emit('onClick')"
|
||||
:title="s__('MergeRequests|Jump to next unresolved thread')"
|
||||
@click="jumpToNextDiscussion"
|
||||
>
|
||||
<icon name="comment-next" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,53 +1,18 @@
|
|||
<script>
|
||||
/* global Mousetrap */
|
||||
import 'mousetrap';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import discussionNavigation from '~/notes/mixins/discussion_navigation';
|
||||
|
||||
export default {
|
||||
mixins: [discussionNavigation],
|
||||
props: {
|
||||
isDiffView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentDiscussionId: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'nextUnresolvedDiscussionId',
|
||||
'previousUnresolvedDiscussionId',
|
||||
'getDiscussion',
|
||||
]),
|
||||
},
|
||||
mounted() {
|
||||
Mousetrap.bind('n', () => this.jumpToNextDiscussion());
|
||||
Mousetrap.bind('p', () => this.jumpToPreviousDiscussion());
|
||||
Mousetrap.bind('n', this.jumpToNextDiscussion);
|
||||
Mousetrap.bind('p', this.jumpToPreviousDiscussion);
|
||||
},
|
||||
beforeDestroy() {
|
||||
Mousetrap.unbind('n');
|
||||
Mousetrap.unbind('p');
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['expandDiscussion']),
|
||||
jumpToNextDiscussion() {
|
||||
const nextId = this.nextUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
|
||||
const nextDiscussion = this.getDiscussion(nextId);
|
||||
this.jumpToDiscussion(nextDiscussion);
|
||||
this.currentDiscussionId = nextId;
|
||||
},
|
||||
jumpToPreviousDiscussion() {
|
||||
const prevId = this.previousUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
|
||||
const prevDiscussion = this.getDiscussion(prevId);
|
||||
this.jumpToDiscussion(prevDiscussion);
|
||||
this.currentDiscussionId = prevId;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
return this.$slots.default;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import noteForm from './note_form.vue';
|
|||
import diffWithNote from './diff_with_note.vue';
|
||||
import noteable from '../mixins/noteable';
|
||||
import resolvable from '../mixins/resolvable';
|
||||
import discussionNavigation from '../mixins/discussion_navigation';
|
||||
import eventHub from '../event_hub';
|
||||
import DiscussionNotes from './discussion_notes.vue';
|
||||
import DiscussionActions from './discussion_actions.vue';
|
||||
|
|
@ -35,7 +34,7 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [noteable, resolvable, discussionNavigation, diffLineNoteFormMixin],
|
||||
mixins: [noteable, resolvable, diffLineNoteFormMixin],
|
||||
props: {
|
||||
discussion: {
|
||||
type: Object,
|
||||
|
|
@ -79,12 +78,8 @@ export default {
|
|||
'convertedDisscussionIds',
|
||||
'getNoteableData',
|
||||
'userCanReply',
|
||||
'nextUnresolvedDiscussionId',
|
||||
'unresolvedDiscussionsCount',
|
||||
'hasUnresolvedDiscussions',
|
||||
'showJumpToNextDiscussion',
|
||||
'getUserData',
|
||||
'getDiscussion',
|
||||
]),
|
||||
currentUser() {
|
||||
return this.getUserData;
|
||||
|
|
@ -152,7 +147,6 @@ export default {
|
|||
'saveNote',
|
||||
'removePlaceholderNotes',
|
||||
'toggleResolveNote',
|
||||
'expandDiscussion',
|
||||
'removeConvertedDiscussion',
|
||||
]),
|
||||
showReplyForm() {
|
||||
|
|
@ -219,15 +213,6 @@ export default {
|
|||
callback(err);
|
||||
});
|
||||
},
|
||||
jumpToNextDiscussion() {
|
||||
const nextId = this.nextUnresolvedDiscussionId(
|
||||
this.discussion.id,
|
||||
this.discussionsByDiffOrder,
|
||||
);
|
||||
const nextDiscussion = this.getDiscussion(nextId);
|
||||
|
||||
this.jumpToDiscussion(nextDiscussion);
|
||||
},
|
||||
deleteNoteHandler(note) {
|
||||
this.$emit('noteDeleted', this.discussion, note);
|
||||
},
|
||||
|
|
@ -294,7 +279,6 @@ export default {
|
|||
:should-show-jump-to-next-discussion="shouldShowJumpToNextDiscussion"
|
||||
@showReplyForm="showReplyForm"
|
||||
@resolve="resolveHandler"
|
||||
@jumpToNextDiscussion="jumpToNextDiscussion"
|
||||
/>
|
||||
<div v-if="isReplying" class="avatar-note-form-holder">
|
||||
<user-avatar-link
|
||||
|
|
|
|||
|
|
@ -1,8 +1,21 @@
|
|||
import { mapGetters, mapActions, mapState } from 'vuex';
|
||||
import { scrollToElement } from '~/lib/utils/common_utils';
|
||||
import eventHub from '../../notes/event_hub';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'nextUnresolvedDiscussionId',
|
||||
'previousUnresolvedDiscussionId',
|
||||
'getDiscussion',
|
||||
]),
|
||||
...mapState({
|
||||
currentDiscussionId: state => state.notes.currentDiscussionId,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['expandDiscussion', 'setCurrentDiscussionId']),
|
||||
|
||||
diffsJump(id) {
|
||||
const selector = `ul.notes[data-discussion-id="${id}"]`;
|
||||
|
||||
|
|
@ -58,5 +71,21 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
jumpToNextDiscussion() {
|
||||
this.handleDiscussionJump(this.nextUnresolvedDiscussionId);
|
||||
},
|
||||
|
||||
jumpToPreviousDiscussion() {
|
||||
this.handleDiscussionJump(this.previousUnresolvedDiscussionId);
|
||||
},
|
||||
|
||||
handleDiscussionJump(fn) {
|
||||
const isDiffView = window.mrTabs.currentAction === 'diffs';
|
||||
const targetId = fn(this.currentDiscussionId, isDiffView);
|
||||
const discussion = this.getDiscussion(targetId);
|
||||
this.jumpToDiscussion(discussion);
|
||||
this.setCurrentDiscussionId(targetId);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -506,5 +506,8 @@ export const fetchDescriptionVersion = (_, { endpoint, startingVersion }) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const setCurrentDiscussionId = ({ commit }, discussionId) =>
|
||||
commit(types.SET_CURRENT_DISCUSSION_ID, discussionId);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ export const getDiscussionLastNote = state => discussion =>
|
|||
|
||||
export const unresolvedDiscussionsCount = state => state.unresolvedDiscussionsCount;
|
||||
export const resolvableDiscussionsCount = state => state.resolvableDiscussionsCount;
|
||||
export const hasUnresolvedDiscussions = state => state.hasUnresolvedDiscussions;
|
||||
|
||||
export const showJumpToNextDiscussion = (state, getters) => (mode = 'discussion') => {
|
||||
const orderedDiffs =
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default () => ({
|
|||
convertedDisscussionIds: [],
|
||||
targetNoteHash: null,
|
||||
lastFetchedAt: null,
|
||||
currentDiscussionId: null,
|
||||
|
||||
// View layer
|
||||
isToggleStateButtonLoading: false,
|
||||
|
|
@ -26,7 +27,6 @@ export default () => ({
|
|||
commentsDisabled: false,
|
||||
resolvableDiscussionsCount: 0,
|
||||
unresolvedDiscussionsCount: 0,
|
||||
hasUnresolvedDiscussions: false,
|
||||
},
|
||||
actions,
|
||||
getters,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
|
|||
export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
|
||||
export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
|
||||
export const UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS = 'UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS';
|
||||
export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID';
|
||||
|
||||
// Issue
|
||||
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
|
||||
|
|
|
|||
|
|
@ -267,7 +267,6 @@ export default {
|
|||
discussion.resolvable &&
|
||||
discussion.notes.some(note => note.resolvable && !note.resolved),
|
||||
).length;
|
||||
state.hasUnresolvedDiscussions = state.unresolvedDiscussionsCount > 1;
|
||||
},
|
||||
|
||||
[types.CONVERT_TO_DISCUSSION](state, discussionId) {
|
||||
|
|
@ -281,4 +280,8 @@ export default {
|
|||
convertedDisscussionIds.splice(convertedDisscussionIds.indexOf(discussionId), 1);
|
||||
Object.assign(state, { convertedDisscussionIds });
|
||||
},
|
||||
|
||||
[types.SET_CURRENT_DISCUSSION_ID](state, discussionId) {
|
||||
state.currentDiscussionId = discussionId;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,6 +122,13 @@ module AvatarsHelper
|
|||
else
|
||||
source_identicon(source, options)
|
||||
end
|
||||
|
||||
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => e
|
||||
# Handle Gitaly connection issues gracefully
|
||||
Gitlab::ErrorTracking
|
||||
.track_exception(e, source_type: source.class.name, source_id: source.id)
|
||||
|
||||
source_identicon(source, options)
|
||||
end
|
||||
|
||||
def source_identicon(source, options = {})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix 503 errors caused by Gitaly failures during project_icon lookup
|
||||
merge_request: 23930
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Cycle unresolved threads
|
||||
merge_request: 23123
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -10,6 +10,16 @@ def log_pool_size(db, previous_pool_size, current_pool_size)
|
|||
Gitlab::AppLogger.debug(log_message.join(' '))
|
||||
end
|
||||
|
||||
Gitlab.ee do
|
||||
# We need to initialize the Geo database before
|
||||
# setting the Geo DB connection pool size.
|
||||
if File.exist?(Rails.root.join('config/database_geo.yml'))
|
||||
Rails.application.configure do
|
||||
config.geo_database = config_for(:database_geo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# When running on multi-threaded runtimes like Puma or Sidekiq,
|
||||
# set the number of threads per process as the minimum DB connection pool size.
|
||||
# This is to avoid connectivity issues as was documented here:
|
||||
|
|
|
|||
|
|
@ -1,17 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Gitlab.ee do
|
||||
if File.exist?(Rails.root.join('config/database_geo.yml'))
|
||||
Rails.application.configure do
|
||||
config.geo_database = config_for(:database_geo)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
if Gitlab::Geo.connected? && Gitlab::Geo.primary?
|
||||
Gitlab::Geo.current_node&.update_clone_url!
|
||||
end
|
||||
rescue => e
|
||||
warn "WARNING: Unable to check/update clone_url_prefix for Geo: #{e}"
|
||||
if Gitlab::Geo.connected? && Gitlab::Geo.primary?
|
||||
Gitlab::Geo.current_node&.update_clone_url!
|
||||
end
|
||||
rescue => e
|
||||
warn "WARNING: Unable to check/update clone_url_prefix for Geo: #{e}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ To do so we'll generate a dump of our current database. This dump will only
|
|||
contain the structure, not any data. To generate this dump run the following
|
||||
command on your active database server:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql -p 5432 -U gitlab-psql -s -f /tmp/structure.sql gitlabhq_production
|
||||
```
|
||||
|
||||
|
|
@ -39,14 +39,14 @@ Once the structure dump is generated we also need to generate a dump for the
|
|||
can't be replicated easily by Slony. To generate this dump run the following
|
||||
command on your active database server:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql/ -p 5432 -U gitlab-psql -a -t schema_migrations -f /tmp/migrations.sql gitlabhq_production
|
||||
```
|
||||
|
||||
Next we'll need to move these files somewhere accessible by the new database
|
||||
server. The easiest way is to simply download these files to your local system:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
scp your-user@production-database-host:/tmp/*.sql /tmp
|
||||
```
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ install Slony using said package manager.
|
|||
|
||||
When compiling Slony from source you *must* use the following commands to do so:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
./configure --prefix=/path/to/installation/directory --with-perltools --with-pgconfigdir=/path/to/directory/containing/pg_config/bin
|
||||
make
|
||||
make install
|
||||
|
|
@ -71,7 +71,7 @@ make install
|
|||
|
||||
Omnibus users can use the following commands:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
./configure --prefix=/opt/gitlab/embedded --with-perltools --with-pgconfigdir=/opt/gitlab/embedded/bin
|
||||
make
|
||||
make install
|
||||
|
|
@ -81,7 +81,7 @@ This assumes you have installed GitLab into `/opt/gitlab`.
|
|||
|
||||
To test if Slony is installed properly, run the following commands:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
test -f /opt/gitlab/embedded/bin/slonik && echo 'Slony installed' || echo 'Slony not installed'
|
||||
test -f /opt/gitlab/embedded/bin/slonik_init_cluster && echo 'Slony Perl tools are available' || echo 'Slony Perl tools are not available'
|
||||
/opt/gitlab/embedded/bin/slonik -v
|
||||
|
|
@ -91,7 +91,7 @@ This assumes Slony was installed to `/opt/gitlab/embedded`. If Slony was
|
|||
installed properly the output of these commands will be (the mentioned "slonik"
|
||||
version may be different):
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Slony installed
|
||||
Slony Perl tools are available
|
||||
slonik version 2.2.5
|
||||
|
|
@ -126,7 +126,7 @@ First we'll need to create some required directories and set the correct
|
|||
permissions. To do so, run the following commands on both the old and new
|
||||
database server:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo mkdir -p /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
|
||||
sudo chown gitlab-psql:root /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
|
||||
```
|
||||
|
|
@ -184,7 +184,7 @@ use it. The following placeholders should be replaced:
|
|||
The list of tables to replicate can be generated by running the following
|
||||
command on your old PostgreSQL database:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo gitlab-psql gitlabhq_production -c "select concat('\"', schemaname, '.', tablename, '\",') from pg_catalog.pg_tables where schemaname = 'public' and tableowner = 'gitlab' and tablename != 'schema_migrations' order by tablename asc;" -t
|
||||
```
|
||||
|
||||
|
|
@ -216,13 +216,13 @@ sure that the SQL files we generated earlier can be found in the `/tmp`
|
|||
directory of the new server. Once these files are in place start a `psql`
|
||||
session on this server:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo gitlab-psql gitlabhq_production
|
||||
```
|
||||
|
||||
Now run the following commands:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
\i /tmp/structure.sql
|
||||
\i /tmp/migrations.sql
|
||||
```
|
||||
|
|
@ -231,7 +231,7 @@ To verify if the structure is in place close the session, start it again, then
|
|||
run `\d`. If all went well you should see output along the lines of the
|
||||
following:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
List of relations
|
||||
Schema | Name | Type | Owner
|
||||
--------+---------------------------------------------+----------+-------------
|
||||
|
|
@ -248,13 +248,13 @@ following:
|
|||
Now we can initialize the required tables and what not that Slony will use for
|
||||
its replication process. To do so, run the following on the old database:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_init_cluster --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
|
||||
```
|
||||
|
||||
If all went well this will produce something along the lines of:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
<stdin>:10: Set up replication nodes
|
||||
<stdin>:13: Next: configure paths for each node/origin
|
||||
<stdin>:16: Replication nodes prepared
|
||||
|
|
@ -264,13 +264,13 @@ If all went well this will produce something along the lines of:
|
|||
Next we need to start a replication node on every server. To do so, run the
|
||||
following on the old database:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
|
||||
```
|
||||
|
||||
If all went well this will produce output such as:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2 slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 &
|
||||
Slon successfully started for cluster slony_replication, node node1
|
||||
PID [26740]
|
||||
|
|
@ -279,7 +279,7 @@ Start the watchdog process as well...
|
|||
|
||||
Next we need to run the following command on the _new_ database server:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
|
||||
```
|
||||
|
||||
|
|
@ -288,13 +288,13 @@ This will produce similar output if all went well.
|
|||
Next we need to tell the new database server what it should replicate. This can
|
||||
be done by running the following command on the _new_ database server:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_create_set 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
|
||||
```
|
||||
|
||||
This should produce output along the lines of the following:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
<stdin>:11: Subscription set 1 (set1) created
|
||||
<stdin>:12: Adding tables to the subscription set
|
||||
<stdin>:16: Add primary keyed table public.abuse_reports
|
||||
|
|
@ -308,13 +308,13 @@ This should produce output along the lines of the following:
|
|||
Finally we can start the replication process by running the following on the
|
||||
_new_ database server:
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_subscribe_set 1 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
|
||||
```
|
||||
|
||||
This should produce the following output:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
<stdin>:6: Subscribed nodes to set 1
|
||||
```
|
||||
|
||||
|
|
@ -324,7 +324,7 @@ not days. Unfortunately Slony itself doesn't really provide a way of knowing
|
|||
when the two databases are in sync. To get an estimate of the progress you can
|
||||
use the following shell script:
|
||||
|
||||
```
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
|
@ -365,7 +365,7 @@ GitLab so it can use the new database, etc.
|
|||
First, let's stop all of GitLab. Omnibus users can do so by running the
|
||||
following on their GitLab server(s):
|
||||
|
||||
```
|
||||
```shell
|
||||
sudo gitlab-ctl stop unicorn
|
||||
sudo gitlab-ctl stop sidekiq
|
||||
sudo gitlab-ctl stop mailroom
|
||||
|
|
@ -382,7 +382,7 @@ as this data will be lost.
|
|||
|
||||
To stop replication, run the following on both database servers:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_kill --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
|
||||
```
|
||||
|
||||
|
|
@ -394,7 +394,7 @@ The above setup does not replicate database sequences, as such these must be
|
|||
reset manually in the target database. You can use the following script for
|
||||
this:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
|
|
@ -459,7 +459,7 @@ main
|
|||
|
||||
Upload this script to the _target_ server and execute it as follows:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
bash path/to/the/script/above.sh
|
||||
```
|
||||
|
||||
|
|
@ -471,7 +471,7 @@ This will correct the ownership of sequences and reset the next value for the
|
|||
Next we need to remove all Slony related data. To do so, run the following
|
||||
command on the _target_ server:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo gitlab-psql gitlabhq_production -c "DROP SCHEMA _slony_replication CASCADE;"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ If you wanted to increase the max attachment size to 200m in a GitLab
|
|||
[Omnibus](https://docs.gitlab.com/omnibus/) install, for example, you might need to
|
||||
add the line below to `/etc/gitlab/gitlab.rb` before increasing the max attachment size:
|
||||
|
||||
```
|
||||
```ruby
|
||||
nginx['client_max_body_size'] = "200m"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ type: reference
|
|||
|
||||
GitLab protects the following paths with Rack Attack by default:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
'/users/password',
|
||||
'/users/sign_in',
|
||||
'/api/#{API::API.version}/session.json',
|
||||
|
|
@ -23,7 +23,7 @@ that exceed 10 requests per minute per IP address.
|
|||
|
||||
This header is included in responses to blocked requests:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Retry-After: 60
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ An approval will be optional when a license report:
|
|||
When including a security job template like [`SAST`](sast/index.md#configuration),
|
||||
the following error can be raised, depending on your GitLab CI/CD configuration:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Found errors in your .gitlab-ci.yml:
|
||||
|
||||
* sast job: stage parameter should be unit-tests
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ that the IP address of the pods are routable within the GCP network.
|
|||
|
||||
First, we need to declare some environment variables with configuration that will be used throughout this guide:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
export PROJECT_ID=crossplane-playground # the GCP project where all resources reside.
|
||||
export NETWORK_NAME=default # the GCP network where your GKE is provisioned.
|
||||
export REGION=us-central1 # the GCP region where the GKE cluster is provisioned.
|
||||
|
|
@ -43,7 +43,7 @@ NOTE: **Note:**
|
|||
For a non-GitLab managed cluster, ensure that the service account for the token provided can manage resources in the `database.crossplane.io` API group.
|
||||
1. Save the following YAML as `crossplane-database-role.yaml`:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
cat > crossplane-database-role.yaml <<EOF
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
|
|
@ -69,7 +69,7 @@ EOF
|
|||
|
||||
Once the file is created, apply it with the following command in order to create the necessary role:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl apply -f crossplane-database-role.yaml
|
||||
```
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ This can done by either:
|
|||
[configuring private services access](https://cloud.google.com/vpc/docs/configure-private-services-access).
|
||||
Create a GlobalAddress and Connection resources:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
cat > network.yaml <<EOF
|
||||
---
|
||||
# gitlab-ad-globaladdress defines the IP range that will be allocated for cloud services connecting to the instances in the given Network.
|
||||
|
|
@ -133,14 +133,14 @@ EOF
|
|||
|
||||
Apply the settings specified in the file with the following command:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl apply -f network.yaml
|
||||
```
|
||||
|
||||
You can verify creation of the network resources with the following commands.
|
||||
Verify that the status of both of these resources is ready and is synced.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl describe connection.servicenetworking.gcp.crossplane.io gitlab-ad-connection
|
||||
kubectl describe globaladdress.compute.gcp.crossplane.io gitlab-ad-globaladdress
|
||||
```
|
||||
|
|
@ -154,7 +154,7 @@ Resource classes are a way of defining a configuration for the required managed
|
|||
1. A default CloudSQLInstanceClass.
|
||||
1. A CloudSQLInstanceClass with labels.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
cat > gcp-postgres-standard.yaml <<EOF
|
||||
apiVersion: database.gcp.crossplane.io/v1beta1
|
||||
kind: CloudSQLInstanceClass
|
||||
|
|
@ -204,13 +204,13 @@ EOF
|
|||
|
||||
Apply the resource class configuration with the following command:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl apply -f gcp-postgres-standard.yaml
|
||||
```
|
||||
|
||||
Verify creation of the Resource class with the following command:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl get cloudsqlinstanceclasses
|
||||
```
|
||||
|
||||
|
|
@ -239,13 +239,13 @@ The Auto DevOps pipeline should provision a PostgresqlInstance when it runs succ
|
|||
|
||||
Verify creation of the PostgreSQL Instance.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl get postgresqlinstance
|
||||
```
|
||||
|
||||
Sample Output: The `STATUS` field of the PostgresqlInstance transitions to `BOUND` when it is successfully provisioned.
|
||||
|
||||
```
|
||||
```plaintext
|
||||
NAME STATUS CLASS-KIND CLASS-NAME RESOURCE-KIND RESOURCE-NAME AGE
|
||||
staging-test8 Bound CloudSQLInstanceClass cloudsqlinstancepostgresql-standard CloudSQLInstance xp-ad-demo-24-staging-staging-test8-jj55c 9m
|
||||
```
|
||||
|
|
@ -254,13 +254,13 @@ The endpoint of the PostgreSQL instance, and the user credentials, are present i
|
|||
|
||||
Verify the secret with the database information is created with the following command:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
kubectl describe secret app-postgres
|
||||
```
|
||||
|
||||
Sample Output:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Name: app-postgres
|
||||
Namespace: xp-ad-demo-24-staging
|
||||
Labels: <none>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ When a merge request has a large number of comments it can be difficult to track
|
|||
what remains unresolved. You can jump between unresolved threads with the
|
||||
Jump button next to the Reply field on a thread.
|
||||
|
||||
You can also jump to the first unresolved thread from the button next to the
|
||||
You can also jump to the next unresolved thread from the button next to the
|
||||
resolved threads tracker.
|
||||
|
||||
You can also use keyboard shortcuts to navigate among threads:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Below are the fingerprints for GitLab.com's SSH host keys.
|
|||
Add the following to `.ssh/known_hosts` to skip manual fingerprint
|
||||
confirmation in SSH:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
|
||||
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
|
||||
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
|
||||
|
|
@ -41,7 +41,7 @@ GitLab.com can be reached via a [different SSH port][altssh] for `git+ssh`.
|
|||
|
||||
An example `~/.ssh/config` is the following:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Host gitlab.com
|
||||
Hostname altssh.gitlab.com
|
||||
User git
|
||||
|
|
@ -455,7 +455,7 @@ per second per IP address.
|
|||
|
||||
The following example headers are included for all API requests:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
RateLimit-Limit: 600
|
||||
RateLimit-Observed: 6
|
||||
RateLimit-Remaining: 594
|
||||
|
|
@ -481,7 +481,7 @@ user confirmation, user sign in, and password reset.
|
|||
|
||||
This header is included in responses to blocked requests:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Retry-After: 60
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ Learn how to set up GitLab CI/CD for specific use cases.
|
|||
|
||||
Supposed your repository contained the following files:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
├── index.html
|
||||
├── css
|
||||
│ └── main.css
|
||||
|
|
@ -144,7 +144,7 @@ orphan branch (let's name it `pages`) that will host your static generator site.
|
|||
|
||||
You can create a new empty branch like this:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
git checkout --orphan pages
|
||||
```
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ commits. Push the source files of your static generator in the `pages` branch.
|
|||
Below is a copy of `.gitlab-ci.yml` where the most significant line is the last
|
||||
one, specifying to execute everything in the `pages` branch:
|
||||
|
||||
```
|
||||
```yaml
|
||||
image: ruby:2.6
|
||||
|
||||
pages:
|
||||
|
|
@ -188,7 +188,7 @@ compressed files, it will serve that version instead of the uncompressed one.
|
|||
To take advantage of this feature, the artifact you upload to the Pages should
|
||||
have this structure:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
public/
|
||||
├─┬ index.html
|
||||
│ └ index.html.gz
|
||||
|
|
@ -226,7 +226,7 @@ request for a URL that does not include an extension.
|
|||
|
||||
Consider a Pages site deployed with the following files:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
public/
|
||||
├─┬ index.html
|
||||
│ ├ data.html
|
||||
|
|
|
|||
|
|
@ -113,13 +113,13 @@ Artifacts for other pipelines can be accessed with direct access to them.
|
|||
|
||||
The structure of the URL to download the whole artifacts archive is the following:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/download?job=<job_name>
|
||||
```
|
||||
|
||||
To download a single file from the artifacts use the following URL:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
|
||||
```
|
||||
|
||||
|
|
@ -127,40 +127,40 @@ For example, to download the latest artifacts of the job named `coverage` of
|
|||
the `master` branch of the `gitlab` project that belongs to the `gitlab-org`
|
||||
namespace, the URL would be:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/download?job=coverage
|
||||
```
|
||||
|
||||
To download the file `coverage/index.html` from the same
|
||||
artifacts use the following URL:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/raw/coverage/index.html?job=coverage
|
||||
```
|
||||
|
||||
There is also a URL to browse the latest job artifacts:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/browse?job=<job_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/browse?job=coverage
|
||||
```
|
||||
|
||||
There is also a URL to specific files, including html files that
|
||||
are shown in [GitLab Pages](../../../administration/pages/index.md):
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/file/<path>?job=<job_name>
|
||||
```
|
||||
|
||||
For example, when a job `coverage` creates the artifact `htmlcov/index.html`,
|
||||
you can access it at:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/file/htmlcov/index.html?job=coverage
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ started:
|
|||
1. Generate the private/public key pair with the following command, which will
|
||||
spawn a series of questions:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
gpg --full-gen-key
|
||||
```
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ started:
|
|||
1. The first question is which algorithm can be used. Select the kind you want
|
||||
or press <kbd>Enter</kbd> to choose the default (RSA and RSA):
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Please select what kind of key you want:
|
||||
(1) RSA and RSA (default)
|
||||
(2) DSA and Elgamal
|
||||
|
|
@ -68,7 +68,7 @@ started:
|
|||
|
||||
1. The next question is key length. We recommend you choose `4096`:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
RSA keys may be between 1024 and 4096 bits long.
|
||||
What keysize do you want? (2048) 4096
|
||||
Requested keysize is 4096 bits
|
||||
|
|
@ -77,7 +77,7 @@ started:
|
|||
1. Specify the validity period of your key. This is something
|
||||
subjective, and you can use the default value, which is to never expire:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Please specify how long the key should be valid.
|
||||
0 = key does not expire
|
||||
<n> = key expires in n days
|
||||
|
|
@ -90,7 +90,7 @@ started:
|
|||
|
||||
1. Confirm that the answers you gave were correct by typing `y`:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
Is this correct? (y/N) y
|
||||
```
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ started:
|
|||
(should match a verified email address you use in GitLab) and an optional
|
||||
comment (press <kbd>Enter</kbd> to skip):
|
||||
|
||||
```
|
||||
```plaintext
|
||||
GnuPG needs to construct a user ID to identify your key.
|
||||
|
||||
Real name: Mr. Robot
|
||||
|
|
@ -113,7 +113,7 @@ started:
|
|||
1. Pick a strong password when asked and type it twice to confirm.
|
||||
1. Use the following command to list the private GPG key you just created:
|
||||
|
||||
```
|
||||
```shell
|
||||
gpg --list-secret-keys --keyid-format LONG <your_email>
|
||||
```
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ started:
|
|||
1. Copy the GPG key ID that starts with `sec`. In the following example, that's
|
||||
`30F2B65B9246B6CA`:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC]
|
||||
D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA
|
||||
uid [ultimate] Mr. Robot <your_email>
|
||||
|
|
@ -131,7 +131,7 @@ started:
|
|||
|
||||
1. Export the public key of that ID (replace your key ID from the previous step):
|
||||
|
||||
```
|
||||
```shell
|
||||
gpg --armor --export 30F2B65B9246B6CA
|
||||
```
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ key to use.
|
|||
|
||||
1. Use the following command to list the private GPG key you just created:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
gpg --list-secret-keys --keyid-format LONG <your_email>
|
||||
```
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ key to use.
|
|||
1. Copy the GPG key ID that starts with `sec`. In the following example, that's
|
||||
`30F2B65B9246B6CA`:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC]
|
||||
D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA
|
||||
uid [ultimate] Mr. Robot <your_email>
|
||||
|
|
@ -185,7 +185,7 @@ key to use.
|
|||
|
||||
1. Tell Git to use that key to sign the commits:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
git config --global user.signingkey 30F2B65B9246B6CA
|
||||
```
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ key to use.
|
|||
or `gpg: signing failed: secret key not available`, run the following command to
|
||||
change to `gpg2`:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
git config --global gpg.program gpg2
|
||||
```
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ commits:
|
|||
|
||||
1. Commit like you used to, the only difference is the addition of the `-S` flag:
|
||||
|
||||
```
|
||||
```shell
|
||||
git commit -S -m "My commit msg"
|
||||
```
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ commits:
|
|||
If you don't want to type the `-S` flag every time you commit, you can tell Git
|
||||
to sign your commits automatically:
|
||||
|
||||
```
|
||||
```shell
|
||||
git config --global commit.gpgsign true
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -60,26 +60,26 @@ removed from the repository.
|
|||
|
||||
1. Navigate to your repository:
|
||||
|
||||
```
|
||||
```shell
|
||||
cd my_repository/
|
||||
```
|
||||
|
||||
1. Change to the branch you want to remove the big file from:
|
||||
|
||||
```
|
||||
```shell
|
||||
git checkout master
|
||||
```
|
||||
|
||||
1. Create a commit removing the large file from the branch, if it still exists:
|
||||
|
||||
```
|
||||
```shell
|
||||
git rm path/to/big_file.mpg
|
||||
git commit -m 'Remove unneeded large file'
|
||||
```
|
||||
|
||||
1. Rewrite history:
|
||||
|
||||
```
|
||||
```shell
|
||||
bfg --delete-files path/to/big_file.mpg
|
||||
```
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ removed from the repository.
|
|||
|
||||
1. Force-push the changes to GitLab:
|
||||
|
||||
```
|
||||
```shell
|
||||
git push --force-with-lease origin master
|
||||
```
|
||||
|
||||
|
|
@ -116,31 +116,31 @@ purposes!
|
|||
|
||||
1. Navigate to your repository:
|
||||
|
||||
```
|
||||
```shell
|
||||
cd my_repository/
|
||||
```
|
||||
|
||||
1. Change to the branch you want to remove the big file from:
|
||||
|
||||
```
|
||||
```shell
|
||||
git checkout master
|
||||
```
|
||||
|
||||
1. Use `filter-branch` to remove the big file:
|
||||
|
||||
```
|
||||
```shell
|
||||
git filter-branch --force --tree-filter 'rm -f path/to/big_file.mpg' HEAD
|
||||
```
|
||||
|
||||
1. Instruct Git to purge the unwanted data:
|
||||
|
||||
```
|
||||
```shell
|
||||
git reflog expire --expire=now --all && git gc --prune=now --aggressive
|
||||
```
|
||||
|
||||
1. Lastly, force push to the repository:
|
||||
|
||||
```
|
||||
```shell
|
||||
git push --force origin master
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -10770,10 +10770,10 @@ msgstr ""
|
|||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jump to first unresolved discussion"
|
||||
msgid "Jump to first unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jump to next unresolved discussion"
|
||||
msgid "Jump to next unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jun"
|
||||
|
|
@ -11787,7 +11787,7 @@ msgstr ""
|
|||
msgid "MergeRequests|Failed to squash. Should be done manually."
|
||||
msgstr ""
|
||||
|
||||
msgid "MergeRequests|Jump to next unresolved discussion"
|
||||
msgid "MergeRequests|Jump to next unresolved thread"
|
||||
msgstr ""
|
||||
|
||||
msgid "MergeRequests|Reply..."
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
module QA
|
||||
module Resource
|
||||
class ProjectMilestone < Base
|
||||
attribute :id
|
||||
attribute :title
|
||||
|
||||
attribute :project do
|
||||
Project.fabricate!
|
||||
Project.fabricate_via_api! do |resource|
|
||||
resource.name = 'project-with-milestone'
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
|
|
|
|||
|
|
@ -4,93 +4,49 @@ require 'pathname'
|
|||
|
||||
module QA
|
||||
context 'Configure' do
|
||||
def disable_optional_jobs(project)
|
||||
# Disable code_quality check in Auto DevOps pipeline as it takes
|
||||
# too long and times out the test
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'CODE_QUALITY_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |p|
|
||||
p.name = Runtime::Env.auto_devops_project_name || 'autodevops-project'
|
||||
p.auto_devops_enabled = true
|
||||
end
|
||||
end
|
||||
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'LICENSE_MANAGEMENT_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'SAST_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'DEPENDENCY_SCANNING_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'CONTAINER_SCANNING_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'DAST_DISABLED'
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
before do
|
||||
disable_optional_jobs(project)
|
||||
end
|
||||
|
||||
describe 'Auto DevOps support', :orchestrated, :kubernetes, quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/118481' do
|
||||
context 'when rbac is enabled' do
|
||||
before(:all) do
|
||||
@cluster = Service::KubernetesCluster.new.create!
|
||||
end
|
||||
let(:cluster) { Service::KubernetesCluster.new.create! }
|
||||
|
||||
after(:all) do
|
||||
@cluster&.remove!
|
||||
after do
|
||||
cluster&.remove!
|
||||
end
|
||||
|
||||
it 'runs auto devops' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
@project = Resource::Project.fabricate! do |p|
|
||||
p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
|
||||
p.description = 'Project with Auto DevOps'
|
||||
end
|
||||
|
||||
disable_optional_jobs(@project)
|
||||
|
||||
# Set an application secret CI variable (prefixed with K8S_SECRET_)
|
||||
Resource::CiVariable.fabricate! do |resource|
|
||||
resource.project = @project
|
||||
resource.project = project
|
||||
resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
|
||||
resource.value = 'you_can_see_this_variable'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
# Connect K8s cluster
|
||||
Resource::KubernetesCluster.fabricate! do |cluster|
|
||||
cluster.project = @project
|
||||
cluster.cluster = @cluster
|
||||
cluster.install_helm_tiller = true
|
||||
cluster.install_ingress = true
|
||||
cluster.install_prometheus = true
|
||||
cluster.install_runner = true
|
||||
Resource::KubernetesCluster.fabricate! do |k8s_cluster|
|
||||
k8s_cluster.project = project
|
||||
k8s_cluster.cluster = cluster
|
||||
k8s_cluster.install_helm_tiller = true
|
||||
k8s_cluster.install_ingress = true
|
||||
k8s_cluster.install_prometheus = true
|
||||
k8s_cluster.install_runner = true
|
||||
end
|
||||
|
||||
# Create Auto DevOps compatible repo
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = @project
|
||||
push.project = project
|
||||
push.directory = Pathname
|
||||
.new(__dir__)
|
||||
.join('../../../../../fixtures/auto_devops_rack')
|
||||
|
|
@ -145,20 +101,15 @@ module QA
|
|||
before do
|
||||
Flow::Login.sign_in
|
||||
|
||||
@project = Resource::Project.fabricate_via_browser_ui! do |p|
|
||||
p.name = "project-with-autodevops-#{SecureRandom.hex(8)}"
|
||||
p.description = 'Project with AutoDevOps'
|
||||
end
|
||||
project.visit!
|
||||
|
||||
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
|
||||
Page::Project::Settings::CICD.perform(&:expand_auto_devops)
|
||||
Page::Project::Settings::AutoDevops.perform(&:enable_autodevops)
|
||||
|
||||
@project.visit!
|
||||
|
||||
# Create AutoDevOps repo
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = @project
|
||||
push.project = project
|
||||
push.directory = Pathname
|
||||
.new(__dir__)
|
||||
.join('../../../../../fixtures/auto_devops_rack')
|
||||
|
|
@ -175,5 +126,22 @@ module QA
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def disable_optional_jobs(project)
|
||||
%w[
|
||||
CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
|
||||
SAST_DISABLED DAST_DISABLED DEPENDENCY_SCANNING_DISABLED
|
||||
CONTAINER_SCANNING_DISABLED
|
||||
].each do |key|
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
resource.key = key
|
||||
resource.value = '1'
|
||||
resource.masked = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -368,16 +368,6 @@ describe 'Merge request > User resolves diff notes and threads', :js do
|
|||
end
|
||||
end
|
||||
|
||||
it 'shows jump to next discussion button on all discussions' do
|
||||
wait_for_requests
|
||||
|
||||
all_discussion_replies = page.all('.discussion-reply-holder')
|
||||
|
||||
expect(all_discussion_replies.count).to eq(2)
|
||||
expect(all_discussion_replies.first.all('.discussion-next-btn').count).to eq(1)
|
||||
expect(all_discussion_replies.last.all('.discussion-next-btn').count).to eq(1)
|
||||
end
|
||||
|
||||
it 'displays next thread even if hidden' do
|
||||
page.all('.note-discussion', count: 2).each do |discussion|
|
||||
page.within discussion do
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
|
|||
>
|
||||
<button
|
||||
class="btn btn-default discussion-next-btn"
|
||||
title="Jump to next unresolved discussion"
|
||||
title="Jump to next unresolved thread"
|
||||
>
|
||||
<icon-stub
|
||||
name="comment-next"
|
||||
|
|
|
|||
|
|
@ -120,14 +120,5 @@ describe('DiscussionActions', () => {
|
|||
.trigger('click');
|
||||
expect(wrapper.vm.$emit).toHaveBeenCalledWith('resolve');
|
||||
});
|
||||
|
||||
it('emits jumpToNextDiscussion event when clicking on jump to next discussion button', () => {
|
||||
jest.spyOn(wrapper.vm, '$emit');
|
||||
wrapper
|
||||
.find(JumpToNextDiscussionButton)
|
||||
.find('button')
|
||||
.trigger('click');
|
||||
expect(wrapper.vm.$emit).toHaveBeenCalledWith('jumpToNextDiscussion');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,15 +15,4 @@ describe('JumpToNextDiscussionButton', () => {
|
|||
it('matches the snapshot', () => {
|
||||
expect(wrapper.vm.$el).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('emits onClick event on button click', () => {
|
||||
const button = wrapper.find({ ref: 'button' });
|
||||
|
||||
button.trigger('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.emitted().onClick).toBeTruthy();
|
||||
expect(wrapper.emitted().onClick.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,13 +53,15 @@ describe('notes/components/discussion_keyboard_navigator', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
isDiffView | expectedNextId | expectedPrevId
|
||||
${true} | ${NEXT_DIFF_ID} | ${PREV_DIFF_ID}
|
||||
${false} | ${NEXT_ID} | ${PREV_ID}
|
||||
`('when isDiffView is $isDiffView', ({ isDiffView, expectedNextId, expectedPrevId }) => {
|
||||
currentAction | expectedNextId | expectedPrevId
|
||||
${'diffs'} | ${NEXT_DIFF_ID} | ${PREV_DIFF_ID}
|
||||
${'show'} | ${NEXT_ID} | ${PREV_ID}
|
||||
`('when isDiffView is $isDiffView', ({ currentAction, expectedNextId, expectedPrevId }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ propsData: { isDiffView } });
|
||||
window.mrTabs = { currentAction };
|
||||
createComponent();
|
||||
});
|
||||
afterEach(() => delete window.mrTabs);
|
||||
|
||||
it('calls jumpToNextDiscussion when pressing `n`', () => {
|
||||
Mousetrap.trigger('n');
|
||||
|
|
|
|||
|
|
@ -501,7 +501,6 @@ describe('Notes Store mutations', () => {
|
|||
expect.objectContaining({
|
||||
resolvableDiscussionsCount: 1,
|
||||
unresolvedDiscussionsCount: 1,
|
||||
hasUnresolvedDiscussions: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
@ -538,7 +537,6 @@ describe('Notes Store mutations', () => {
|
|||
expect.objectContaining({
|
||||
resolvableDiscussionsCount: 4,
|
||||
unresolvedDiscussionsCount: 2,
|
||||
hasUnresolvedDiscussions: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,15 +22,41 @@ describe AvatarsHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when providing a project' do
|
||||
it_behaves_like 'resource with a default avatar', 'project' do
|
||||
let(:resource) { create(:project, name: 'foo') }
|
||||
let(:helper_args) { [resource] }
|
||||
shared_examples 'Gitaly exception handling' do
|
||||
before do
|
||||
allow(resource).to receive(:avatar_url).and_raise(error_class)
|
||||
end
|
||||
|
||||
it 'handles Gitaly exception gracefully' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
|
||||
an_instance_of(error_class), source_type: 'Project', source_id: resource.id
|
||||
)
|
||||
expect { project_icon(resource) }.not_to raise_error
|
||||
end
|
||||
|
||||
it_behaves_like 'resource with a default avatar', 'project'
|
||||
end
|
||||
|
||||
context 'when providing a project' do
|
||||
let(:helper_args) { [resource] }
|
||||
let(:resource) { create(:project, name: 'foo') }
|
||||
|
||||
it_behaves_like 'resource with a default avatar', 'project'
|
||||
|
||||
it_behaves_like 'resource with a custom avatar', 'project' do
|
||||
let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
|
||||
let(:helper_args) { [resource] }
|
||||
end
|
||||
|
||||
context 'when Gitaly is unavailable' do
|
||||
let(:error_class) { GRPC::Unavailable }
|
||||
|
||||
include_examples 'Gitaly exception handling'
|
||||
end
|
||||
|
||||
context 'when Gitaly request is taking too long' do
|
||||
let(:error_class) { GRPC::DeadlineExceeded }
|
||||
|
||||
include_examples 'Gitaly exception handling'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
|
|||
describe('DiscussionCounter component', () => {
|
||||
let store;
|
||||
let vm;
|
||||
const notes = { currentDiscussionId: null };
|
||||
|
||||
beforeEach(() => {
|
||||
window.mrTabs = {};
|
||||
|
|
@ -25,7 +26,7 @@ describe('DiscussionCounter component', () => {
|
|||
});
|
||||
|
||||
describe('methods', () => {
|
||||
describe('jumpToFirstUnresolvedDiscussion', () => {
|
||||
describe('jumpToNextDiscussion', () => {
|
||||
it('expands unresolved discussion', () => {
|
||||
window.mrTabs.currentAction = 'show';
|
||||
|
||||
|
|
@ -48,13 +49,14 @@ describe('DiscussionCounter component', () => {
|
|||
store.replaceState({
|
||||
...store.state,
|
||||
discussions,
|
||||
notes,
|
||||
});
|
||||
vm.jumpToFirstUnresolvedDiscussion();
|
||||
vm.jumpToNextDiscussion();
|
||||
|
||||
expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: firstDiscussionId });
|
||||
});
|
||||
|
||||
it('jumps to first unresolved discussion from diff tab if all diff discussions are resolved', () => {
|
||||
it('jumps to next unresolved discussion from diff tab if all diff discussions are resolved', () => {
|
||||
window.mrTabs.currentAction = 'diff';
|
||||
spyOn(vm, 'switchToDiscussionsTabAndJumpTo').and.stub();
|
||||
|
||||
|
|
@ -77,8 +79,9 @@ describe('DiscussionCounter component', () => {
|
|||
store.replaceState({
|
||||
...store.state,
|
||||
discussions,
|
||||
notes,
|
||||
});
|
||||
vm.jumpToFirstUnresolvedDiscussion();
|
||||
vm.jumpToNextDiscussion();
|
||||
|
||||
expect(vm.switchToDiscussionsTabAndJumpTo).toHaveBeenCalledWith(unresolvedId);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -101,37 +101,6 @@ describe('noteable_discussion component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
describe('jumpToNextDiscussion', () => {
|
||||
it('expands next unresolved thread', done => {
|
||||
const discussion2 = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
|
||||
discussion2.resolved = false;
|
||||
discussion2.active = true;
|
||||
discussion2.id = 'next'; // prepare this for being identified as next one (to be jumped to)
|
||||
store.dispatch('setInitialNotes', [discussionMock, discussion2]);
|
||||
window.mrTabs.currentAction = 'show';
|
||||
|
||||
wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
spyOn(wrapper.vm, 'expandDiscussion').and.stub();
|
||||
|
||||
const nextDiscussionId = discussion2.id;
|
||||
|
||||
setFixtures(`<div class="discussion" data-discussion-id="${nextDiscussionId}"></div>`);
|
||||
|
||||
wrapper.vm.jumpToNextDiscussion();
|
||||
|
||||
expect(wrapper.vm.expandDiscussion).toHaveBeenCalledWith({
|
||||
discussionId: nextDiscussionId,
|
||||
});
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('for resolved thread', () => {
|
||||
beforeEach(() => {
|
||||
const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ RSpec.shared_examples 'mentions in notes' do |mentionable_type|
|
|||
|
||||
it 'returns all mentionable mentions' do
|
||||
expect(mentionable.user_mentions.count).to eq 1
|
||||
expect(mentionable.referenced_users).to eq [user, user2]
|
||||
expect(mentionable.referenced_users).to match_array([user, user2])
|
||||
expect(mentionable.referenced_projects(user)).to eq [mentionable.project].compact # epic.project is nil, and we want empty []
|
||||
expect(mentionable.referenced_groups(user)).to eq [group]
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue