summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md15
-rw-r--r--app/assets/javascripts/api.js14
-rw-r--r--app/assets/javascripts/ide/components/ide.vue4
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue37
-rw-r--r--app/assets/javascripts/ide/services/index.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js96
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutations/branch.js9
-rw-r--r--app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js3
-rw-r--r--app/assets/javascripts/pipelines/components/empty_state.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue3
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue12
-rw-r--r--app/assets/stylesheets/framework/forms.scss8
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss12
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/pages/members.scss10
-rw-r--r--app/assets/stylesheets/pages/projects.scss6
-rw-r--r--app/assets/stylesheets/pages/repo.scss12
-rw-r--r--app/controllers/admin/application_settings_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/projects/clusters/applications_controller.rb7
-rw-r--r--app/controllers/projects/prometheus/metrics_controller.rb2
-rw-r--r--app/helpers/commits_helper.rb4
-rw-r--r--app/mailers/emails/merge_requests.rb8
-rw-r--r--app/models/appearance.rb15
-rw-r--r--app/models/application_setting.rb36
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/concerns/cacheable_attributes.rb54
-rw-r--r--app/models/concerns/resolvable_note.rb2
-rw-r--r--app/models/merge_request.rb29
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/presenters/merge_request_presenter.rb11
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb17
-rw-r--r--app/services/merge_requests/merge_when_pipeline_succeeds_service.rb6
-rw-r--r--app/services/merge_requests/refresh_service.rb8
-rw-r--r--app/services/notification_recipient_service.rb25
-rw-r--r--app/services/notification_service.rb18
-rw-r--r--app/services/projects/open_issues_count_service.rb38
-rw-r--r--app/services/todo_service.rb30
-rw-r--r--app/uploaders/object_storage.rb1
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml2
-rw-r--r--app/views/admin/dashboard/index.html.haml2
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml4
-rw-r--r--app/views/notify/merge_request_unmergeable_email.html.haml6
-rw-r--r--app/views/notify/merge_request_unmergeable_email.text.haml11
-rw-r--r--app/views/projects/commit/branches.html.haml3
-rw-r--r--app/views/projects/empty.html.haml2
-rw-r--r--app/views/projects/merge_requests/_how_to_merge.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/shared/_auto_devops_callout.html.haml2
-rw-r--r--changelogs/unreleased/39710-search-placeholder-cut-off.yml5
-rw-r--r--changelogs/unreleased/44579-ide-add-pipeline-to-status-bar.yml5
-rw-r--r--changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml5
-rw-r--r--changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml5
-rw-r--r--changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml5
-rw-r--r--changelogs/unreleased/46600-fix-gitlab-revision-when-not-in-git-repo.yml6
-rw-r--r--changelogs/unreleased/commit-branch-tag-icon-update.yml5
-rw-r--r--changelogs/unreleased/fix-devops-remove-beta.yml5
-rw-r--r--changelogs/unreleased/fix-kube_client-proxy_url-exception.yml5
-rw-r--r--changelogs/unreleased/issue_38418.yml5
-rw-r--r--changelogs/unreleased/jprovazn-fix-resolvable.yml5
-rw-r--r--changelogs/unreleased/migrate-restore-repo-to-gitaly.yml5
-rw-r--r--changelogs/unreleased/mr-conflict-notification.yml5
-rw-r--r--changelogs/unreleased/sh-bump-prometheus-client-mmap.yml5
-rw-r--r--changelogs/unreleased/sh-fix-backup-specific-rake-task.yml5
-rw-r--r--changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml5
-rw-r--r--config/initializers/1_settings.rb3
-rw-r--r--config/initializers/7_prometheus_metrics.rb2
-rw-r--r--config/initializers/console_message.rb2
-rw-r--r--config/initializers/kubeclient.rb16
-rw-r--r--config/initializers/sentry.rb2
-rw-r--r--config/webpack.config.js5
-rw-r--r--db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb15
-rw-r--r--doc/api/issues.md4
-rw-r--r--doc/api/merge_requests.md4
-rw-r--r--doc/ci/examples/code_climate.md19
-rw-r--r--doc/ci/examples/container_scanning.md21
-rw-r--r--doc/install/requirements.md4
-rw-r--r--doc/raketasks/backup_restore.md12
-rw-r--r--doc/topics/autodevops/index.md30
-rw-r--r--doc/topics/autodevops/quick_start_guide.md6
-rw-r--r--doc/user/snippets.md33
-rw-r--r--doc/workflow/merge_requests.md2
-rw-r--r--doc/workflow/notifications.md4
-rw-r--r--doc/workflow/shortcuts.md6
-rw-r--r--doc/workflow/todos.md3
-rw-r--r--lib/api/internal.rb2
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/v3/settings.rb2
-rw-r--r--lib/api/version.rb2
-rw-r--r--lib/backup/artifacts.rb6
-rw-r--r--lib/backup/builds.rb6
-rw-r--r--lib/backup/database.rb16
-rw-r--r--lib/backup/lfs.rb6
-rw-r--r--lib/backup/manager.rb55
-rw-r--r--lib/backup/pages.rb6
-rw-r--r--lib/backup/registry.rb6
-rw-r--r--lib/backup/repository.rb98
-rw-r--r--lib/backup/uploads.rb6
-rw-r--r--lib/banzai/filter/plantuml_filter.rb2
-rw-r--r--lib/gitlab.rb21
-rw-r--r--lib/gitlab/current_settings.rb44
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/tasks/gitlab/backup.rake132
-rw-r--r--lib/tasks/gitlab/info.rake2
-rw-r--r--package.json2
-rw-r--r--spec/controllers/projects/merge_requests/conflicts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/prometheus/metrics_controller_spec.rb73
-rw-r--r--spec/features/admin/admin_settings_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb24
-rw-r--r--spec/javascripts/api_spec.js21
-rw-r--r--spec/javascripts/ide/mock_data.js34
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js159
-rw-r--r--spec/javascripts/ide/stores/mutations/branch_spec.js36
-rw-r--r--spec/javascripts/pipelines/empty_state_spec.js2
-rw-r--r--spec/javascripts/pipelines/mock_data.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js11
-rw-r--r--spec/lib/backup/manager_spec.rb2
-rw-r--r--spec/lib/backup/repository_spec.rb40
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb101
-rw-r--r--spec/lib/gitlab_spec.rb60
-rw-r--r--spec/mailers/notify_spec.rb40
-rw-r--r--spec/models/appearance_spec.rb27
-rw-r--r--spec/models/application_setting_spec.rb30
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb2
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb153
-rw-r--r--spec/models/concerns/resolvable_note_spec.rb8
-rw-r--r--spec/models/merge_request_spec.rb138
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb35
-rw-r--r--spec/requests/api/version_spec.rb2
-rw-r--r--spec/services/clusters/applications/schedule_installation_service_spec.rb33
-rw-r--r--spec/services/merge_requests/conflicts/list_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb26
-rw-r--r--spec/services/notification_service_spec.rb26
-rw-r--r--spec/services/projects/open_issues_count_service_spec.rb51
-rw-r--r--spec/services/todo_service_spec.rb25
-rw-r--r--spec/support/helpers/login_helpers.rb8
-rw-r--r--spec/support/helpers/routes_helpers.rb7
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb10
-rw-r--r--spec/views/admin/dashboard/index.html.haml_spec.rb2
-rw-r--r--spec/views/help/index.html.haml_spec.rb2
-rw-r--r--spec/workers/update_merge_requests_worker_spec.rb9
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml18
-rw-r--r--yarn.lock6
150 files changed, 1823 insertions, 721 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c90ab1e8bd..99cf96035d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.8.1 (2018-05-23)
+
+### Fixed (9 changes)
+
+- Allow CommitStatus class to use presentable methods. !18979
+- Fix corrupted environment pages with unathorized proxy url. !18989
+- Fixes deploy token variables on Ci::Build. !19047
+- Fix project mirror database inconsistencies when upgrading from EE to CE. !19109
+- Render 404 when prometheus adapter is disabled in Prometheus metrics controller. !19110
+- Fix error when deleting an empty list of refs.
+- Fixed U2F login when used with LDAP.
+- Bump prometheus-client-mmap to 0.9.3 to fix nil exception error.
+- Fix system hook not firing for blocked users when LDAP sign-in is used.
+
+
## 10.8.0 (2018-05-22)
### Security (3 changes, 1 of them is from the community)
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index eb919241318..ce1069276ab 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -21,6 +21,7 @@ const Api = {
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
usersPath: '/api/:version/users.json',
commitPath: '/api/:version/projects/:id/repository/commits',
+ commitPipelinesPath: '/:project_id/commit/:sha/pipelines',
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
createBranchPath: '/api/:version/projects/:id/repository/branches',
pipelinesPath: '/api/:version/projects/:id/pipelines',
@@ -166,6 +167,19 @@ const Api = {
});
},
+ commitPipelines(projectId, sha) {
+ const encodedProjectId = projectId
+ .split('/')
+ .map(fragment => encodeURIComponent(fragment))
+ .join('/');
+
+ const url = Api.buildUrl(Api.commitPipelinesPath)
+ .replace(':project_id', encodedProjectId)
+ .replace(':sha', encodeURIComponent(sha));
+
+ return axios.get(url);
+ },
+
branchSingle(id, branch) {
const url = Api.buildUrl(Api.branchSinglePath)
.replace(':id', encodeURIComponent(id))
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 1ec69adce09..0aaf5a112cb 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -123,8 +123,6 @@ export default {
</template>
</div>
</div>
- <ide-status-bar
- :file="activeFile"
- />
+ <ide-status-bar :file="activeFile"/>
</article>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index 70c6d53c3ab..6f60cfbf184 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -1,14 +1,16 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapActions, mapState, mapGetters } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
+import CiIcon from '../../vue_shared/components/ci_icon.vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
export default {
components: {
icon,
userAvatarImage,
+ CiIcon,
},
directives: {
tooltip,
@@ -27,8 +29,16 @@ export default {
};
},
computed: {
+ ...mapState(['currentBranchId', 'currentProjectId']),
...mapGetters(['currentProject', 'lastCommit']),
},
+ watch: {
+ lastCommit() {
+ if (!this.isPollingInitialized) {
+ this.initPipelinePolling();
+ }
+ },
+ },
mounted() {
this.startTimer();
},
@@ -36,13 +46,21 @@ export default {
if (this.intervalId) {
clearInterval(this.intervalId);
}
+ if (this.isPollingInitialized) {
+ this.stopPipelinePolling();
+ }
},
methods: {
+ ...mapActions(['pipelinePoll', 'stopPipelinePolling']),
startTimer() {
this.intervalId = setInterval(() => {
this.commitAgeUpdate();
}, 1000);
},
+ initPipelinePolling() {
+ this.pipelinePoll();
+ this.isPollingInitialized = true;
+ },
commitAgeUpdate() {
if (this.lastCommit) {
this.lastCommitFormatedAge = this.timeFormated(this.lastCommit.committed_date);
@@ -61,6 +79,23 @@ export default {
class="ide-status-branch"
v-if="lastCommit && lastCommitFormatedAge"
>
+ <span
+ class="ide-status-pipeline"
+ v-if="lastCommit.pipeline && lastCommit.pipeline.details"
+ >
+ <ci-icon
+ :status="lastCommit.pipeline.details.status"
+ v-tooltip
+ :title="lastCommit.pipeline.details.status.text"
+ />
+ Pipeline
+ <a
+ class="monospace"
+ :href="lastCommit.pipeline.details.status.details_path">#{{ lastCommit.pipeline.id }}</a>
+ {{ lastCommit.pipeline.details.status.text }}
+ for
+ </span>
+
<icon
name="commit"
/>
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index a12e637616a..e8b51f2b516 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -75,4 +75,8 @@ export default {
},
});
},
+ lastCommitPipelines({ getters }) {
+ const commitSha = getters.lastCommit.id;
+ return Api.commitPipelines(getters.currentProject.path_with_namespace, commitSha);
+ },
};
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index eff9bc03651..cece9154c82 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -1,6 +1,11 @@
+import Visibility from 'visibilityjs';
import flash from '~/flash';
+import { __ } from '~/locale';
import service from '../../services';
import * as types from '../mutation_types';
+import Poll from '../../../lib/utils/poll';
+
+let eTagPoll;
export const getProjectData = (
{ commit, state, dispatch },
@@ -21,7 +26,7 @@ export const getProjectData = (
})
.catch(() => {
flash(
- 'Error loading project data. Please try again.',
+ __('Error loading project data. Please try again.'),
'alert',
document,
null,
@@ -59,7 +64,7 @@ export const getBranchData = (
})
.catch(() => {
flash(
- 'Error loading branch data. Please try again.',
+ __('Error loading branch data. Please try again.'),
'alert',
document,
null,
@@ -73,25 +78,74 @@ export const getBranchData = (
}
});
-export const refreshLastCommitData = (
- { commit, state, dispatch },
- { projectId, branchId } = {},
-) => service
- .getBranchData(projectId, branchId)
- .then(({ data }) => {
- commit(types.SET_BRANCH_COMMIT, {
- projectId,
- branchId,
- commit: data.commit,
+export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId, branchId } = {}) =>
+ service
+ .getBranchData(projectId, branchId)
+ .then(({ data }) => {
+ commit(types.SET_BRANCH_COMMIT, {
+ projectId,
+ branchId,
+ commit: data.commit,
+ });
+ })
+ .catch(() => {
+ flash(__('Error loading last commit.'), 'alert', document, null, false, true);
});
- })
- .catch(() => {
- flash(
- 'Error loading last commit.',
- 'alert',
- document,
- null,
- false,
- true,
+
+export const pollSuccessCallBack = ({ commit, state, dispatch }, { data }) => {
+ if (data.pipelines && data.pipelines.length) {
+ const lastCommitHash =
+ state.projects[state.currentProjectId].branches[state.currentBranchId].commit.id;
+ const lastCommitPipeline = data.pipelines.find(
+ pipeline => pipeline.commit.id === lastCommitHash,
);
+ commit(types.SET_LAST_COMMIT_PIPELINE, {
+ projectId: state.currentProjectId,
+ branchId: state.currentBranchId,
+ pipeline: lastCommitPipeline || {},
+ });
+ }
+
+ return data;
+};
+
+export const pipelinePoll = ({ getters, dispatch }) => {
+ eTagPoll = new Poll({
+ resource: service,
+ method: 'lastCommitPipelines',
+ data: {
+ getters,
+ },
+ successCallback: ({ data }) => dispatch('pollSuccessCallBack', { data }),
+ errorCallback: () => {
+ flash(
+ __('Something went wrong while fetching the latest pipeline status.'),
+ 'alert',
+ document,
+ null,
+ false,
+ true,
+ );
+ },
});
+
+ if (!Visibility.hidden()) {
+ eTagPoll.makeRequest();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ eTagPoll.restart();
+ } else {
+ eTagPoll.stop();
+ }
+ });
+};
+
+export const stopPipelinePolling = () => {
+ eTagPoll.stop();
+};
+
+export const restartPipelinePolling = () => {
+ eTagPoll.restart();
+};
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index a3fb3232f1d..0a3f8d031c4 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -23,6 +23,7 @@ export const SET_BRANCH = 'SET_BRANCH';
export const SET_BRANCH_COMMIT = 'SET_BRANCH_COMMIT';
export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE';
export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN';
+export const SET_LAST_COMMIT_PIPELINE = 'SET_LAST_COMMIT_PIPELINE';
// Tree mutation types
export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA';
diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js
index e09f88878f4..f17ec4da308 100644
--- a/app/assets/javascripts/ide/stores/mutations/branch.js
+++ b/app/assets/javascripts/ide/stores/mutations/branch.js
@@ -14,6 +14,10 @@ export default {
treeId: `${projectPath}/${branchName}`,
active: true,
workingReference: '',
+ commit: {
+ ...branch.commit,
+ pipeline: {},
+ },
},
},
});
@@ -28,4 +32,9 @@ export default {
commit,
});
},
+ [types.SET_LAST_COMMIT_PIPELINE](state, { projectId, branchId, pipeline }) {
+ Object.assign(state.projects[projectId].branches[branchId].commit, {
+ pipeline,
+ });
+ },
};
diff --git a/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js b/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js
new file mode 100644
index 00000000000..edd7e38471b
--- /dev/null
+++ b/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js
@@ -0,0 +1,3 @@
+import initU2F from '../../../shared/sessions/u2f';
+
+document.addEventListener('DOMContentLoaded', initU2F);
diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue
index 10ac8c08bed..6083421073e 100644
--- a/app/assets/javascripts/pipelines/components/empty_state.vue
+++ b/app/assets/javascripts/pipelines/components/empty_state.vue
@@ -34,7 +34,7 @@
</h4>
<p>
- {{ s__(`Pipelines|Continous Integration can help
+ {{ s__(`Pipelines|Continuous Integration can help
catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver
code to your product environment.`) }}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 41986b827cd..4318abe97e0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -37,6 +37,7 @@
return {
pipelineId: '',
endpoint: '',
+ cancelingPipeline: null,
};
},
computed: {
@@ -64,6 +65,7 @@
},
onSubmit() {
eventHub.$emit('postAction', this.endpoint);
+ this.cancelingPipeline = this.pipelineId;
},
},
};
@@ -106,6 +108,7 @@
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
+ :canceling-pipeline="cancelingPipeline"
/>
<modal
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index 0f671ceea21..b99246a92d0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -46,12 +46,16 @@
type: String,
required: true,
},
+ cancelingPipeline: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
pipelinesTable: PIPELINES_TABLE,
data() {
return {
isRetrying: false,
- isCancelling: false,
};
},
computed: {
@@ -227,12 +231,14 @@
isChildView() {
return this.viewType === 'child';
},
+
+ isCancelling() {
+ return this.cancelingPipeline === this.pipeline.id;
+ },
},
methods: {
handleCancelClick() {
- this.isCancelling = true;
-
eventHub.$emit('openConfirmationModal', {
pipelineId: this.pipeline.id,
endpoint: this.pipeline.cancel_path,
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 2c30311b1c1..1fb4e3ec761 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -120,6 +120,14 @@ label {
@include box-shadow(none);
border-radius: 2px;
padding: $gl-vert-padding $gl-input-padding;
+
+ &.input-short {
+ width: $input-short-width;
+
+ @media (min-width: $screen-md-min) {
+ width: $input-short-md-width;
+ }
+ }
}
.select-wrapper {
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 66dbe403385..937638d50e8 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -128,14 +128,6 @@
/* Large devices (large desktops, 1200px and up) */
@media (min-width: $screen-lg-min) { width: 250px; }
-
- &.input-short {
- /* Medium devices (desktops, 992px and up) */
- @media (min-width: $screen-md-min) { width: 170px; }
-
- /* Large devices (large desktops, 1200px and up) */
- @media (min-width: $screen-lg-min) { width: 210px; }
- }
}
@media (max-width: $screen-xs-max) {
@@ -164,10 +156,6 @@
}
}
- .input-short {
- width: 100%;
- }
-
.icon-label {
display: inline-block;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index b5505538541..86f82ec14ce 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -230,7 +230,7 @@ $row-hover: $blue-50;
$row-hover-border: $blue-200;
$progress-color: #c0392b;
$header-height: 40px;
-$ide-statusbar-height: 27px;
+$ide-statusbar-height: 25px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$limited-layout-width-sm: 790px;
@@ -554,6 +554,8 @@ $input-danger-border: $red-400;
$input-group-addon-bg: #f7f8fa;
$gl-field-focus-shadow: rgba(0, 0, 0, 0.075);
$gl-field-focus-shadow-error: rgba($red-500, 0.6);
+$input-short-width: 200px;
+$input-short-md-width: 280px;
/*
* Help
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 3422829de58..d5ae141dd7e 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -99,16 +99,6 @@
@media (min-width: $screen-sm-min) {
width: 250px;
}
-
- &.input-short {
- @media (min-width: $screen-md-min) {
- width: 170px;
- }
-
- @media (min-width: $screen-lg-min) {
- width: 210px;
- }
- }
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index dd0cb2c2613..53d021086bf 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -871,12 +871,6 @@ pre.light-well {
margin: 0;
}
-.commits-search-form {
- .input-short {
- min-width: 200px;
- }
-}
-
.git-clone-holder {
width: 380px;
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 175d2779bb7..35985d3c8f3 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -22,7 +22,6 @@
height: calc(100vh - #{$header-height});
margin-top: 0;
border-top: 1px solid $white-dark;
- border-bottom: 1px solid $white-dark;
padding-bottom: $ide-statusbar-height;
&.is-collapsed {
@@ -380,7 +379,7 @@
.ide-status-bar {
border-top: 1px solid $white-dark;
- padding: $gl-bar-padding $gl-padding;
+ padding: 2px $gl-padding-8 0;
background: $white-light;
display: flex;
justify-content: space-between;
@@ -391,12 +390,19 @@
left: 0;
width: 100%;
+ font-size: 12px;
+ line-height: 22px;
+
+ * {
+ font-size: inherit;
+ }
+
> div + div {
padding-left: $gl-padding;
}
svg {
- vertical-align: middle;
+ vertical-align: sub;
}
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 8958eab0423..cdfe3d6ab1e 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -52,7 +52,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
private
def set_application_setting
- @application_setting = ApplicationSetting.current
+ @application_setting = ApplicationSetting.current_without_cache
end
def application_setting_params
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index ed89bed029b..27fd5f7ba37 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -26,11 +26,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Extend the standard message generation to accept our custom exception
def failure_message
- exception = env["omniauth.error"]
+ exception = request.env["omniauth.error"]
error = exception.error_reason if exception.respond_to?(:error_reason)
error ||= exception.error if exception.respond_to?(:error)
error ||= exception.message if exception.respond_to?(:message)
- error ||= env["omniauth.error.type"].to_s
+ error ||= request.env["omniauth.error.type"].to_s
error.to_s.humanize if error
end
diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb
index 90c7fa62216..35885543622 100644
--- a/app/controllers/projects/clusters/applications_controller.rb
+++ b/app/controllers/projects/clusters/applications_controller.rb
@@ -5,9 +5,10 @@ class Projects::Clusters::ApplicationsController < Projects::ApplicationControll
before_action :authorize_create_cluster!, only: [:create]
def create
- Clusters::Applications::ScheduleInstallationService.new(project, current_user,
- application_class: @application_class,
- cluster: @cluster).execute
+ application = @application_class.find_or_create_by!(cluster: @cluster)
+
+ Clusters::Applications::ScheduleInstallationService.new(project, current_user).execute(application)
+
head :no_content
rescue StandardError
head :bad_request
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
index 1dd886409a5..c6b6243b553 100644
--- a/app/controllers/projects/prometheus/metrics_controller.rb
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -25,7 +25,7 @@ module Projects
end
def require_prometheus_metrics!
- render_404 unless prometheus_adapter.can_query?
+ render_404 unless prometheus_adapter&.can_query?
end
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e594a1d0ba3..f5a7871a6e0 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -63,7 +63,7 @@ module CommitsHelper
# Returns a link formatted as a commit branch link
def commit_branch_link(url, text)
link_to(url, class: 'label label-gray ref-name branch-link') do
- sprite_icon('fork', size: 12, css_class: 'fork-svg') + "#{text}"
+ sprite_icon('branch', size: 12, css_class: 'fork-svg') + "#{text}"
end
end
@@ -77,7 +77,7 @@ module CommitsHelper
# Returns a link formatted as a commit tag link
def commit_tag_link(url, text)
link_to(url, class: 'label label-gray ref-name') do
- icon('tag', class: 'append-right-5') + "#{text}"
+ sprite_icon('tag', size: 12, css_class: 'append-right-5 vertical-align-middle') + "#{text}"
end
end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index b3f2aeb08ca..5ba3a4a322c 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -56,6 +56,14 @@ module Emails
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
+ def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil)
+ setup_merge_request_mail(merge_request_id, recipient_id)
+
+ @reasons = MergeRequestPresenter.new(@merge_request, current_user: current_user).unmergeable_reasons
+
+ mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
+ end
+
def resolved_all_discussions_email(recipient_id, merge_request_id, resolved_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index f8713138a93..67cc84a9140 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -1,6 +1,6 @@
class Appearance < ActiveRecord::Base
+ include CacheableAttributes
include CacheMarkdownField
- include AfterCommitQueue
include ObjectStorage::BackgroundMove
include WithUploads
@@ -15,16 +15,9 @@ class Appearance < ActiveRecord::Base
mount_uploader :logo, AttachmentUploader
mount_uploader :header_logo, AttachmentUploader
- CACHE_KEY = "current_appearance:#{Gitlab::VERSION}".freeze
-
- after_commit :flush_redis_cache
-
- def self.current
- Rails.cache.fetch(CACHE_KEY) { first }
- end
-
- def flush_redis_cache
- Rails.cache.delete(CACHE_KEY)
+ # Overrides CacheableAttributes.current_without_cache
+ def self.current_without_cache
+ first
end
def single_appearance_row
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 451e512aef7..e8ccb320fae 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1,11 +1,11 @@
class ApplicationSetting < ActiveRecord::Base
+ include CacheableAttributes
include CacheMarkdownField
include TokenAuthenticatable
add_authentication_token_field :runners_registration_token
add_authentication_token_field :health_check_access_token
- CACHE_KEY = 'application_setting.last'.freeze
DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or
\s # any whitespace character
@@ -229,40 +229,6 @@ class ApplicationSetting < ActiveRecord::Base
after_commit do
reset_memoized_terms
- Rails.cache.write(CACHE_KEY, self)
- end
-
- def self.current
- ensure_cache_setup
-
- Rails.cache.fetch(CACHE_KEY) do
- ApplicationSetting.last.tap do |settings|
- # do not cache nils
- raise 'missing settings' unless settings
- end
- end
- rescue
- # Fall back to an uncached value if there are any problems (e.g. redis down)
- ApplicationSetting.last
- end
-
- def self.expire
- Rails.cache.delete(CACHE_KEY)
- rescue
- # Gracefully handle when Redis is not available. For example,
- # omnibus may fail here during gitlab:assets:compile.
- end
-
- def self.cached
- value = Rails.cache.read(CACHE_KEY)
- ensure_cache_setup if value.present?
- value
- end
-
- def self.ensure_cache_setup
- # This is a workaround for a Rails bug that causes attribute methods not
- # to be loaded when read from cache: https://github.com/rails/rails/issues/27348
- ApplicationSetting.define_attribute_methods
end
def self.defaults
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 495430931aa..75fd55a8f7b 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -618,7 +618,7 @@ module Ci
variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
- variables.append(key: 'CI_SERVER_REVISION', value: Gitlab::REVISION)
+ variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
variables.append(key: 'CI_COMMIT_SHA', value: sha)
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
new file mode 100644
index 00000000000..b32459fdabf
--- /dev/null
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -0,0 +1,54 @@
+module CacheableAttributes
+ extend ActiveSupport::Concern
+
+ included do
+ after_commit { self.class.expire }
+ end
+
+ class_methods do
+ # Can be overriden
+ def current_without_cache
+ last
+ end
+
+ def cache_key
+ "#{name}:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:json".freeze
+ end
+
+ def defaults
+ {}
+ end
+
+ def build_from_defaults(attributes = {})
+ new(defaults.merge(attributes))
+ end
+
+ def cached
+ json_attributes = Rails.cache.read(cache_key)
+ return nil unless json_attributes.present?
+
+ build_from_defaults(JSON.parse(json_attributes))
+ end
+
+ def current
+ cached_record = cached
+ return cached_record if cached_record.present?
+
+ current_without_cache.tap { |current_record| current_record&.cache! }
+ rescue
+ # Fall back to an uncached value if there are any problems (e.g. Redis down)
+ current_without_cache
+ end
+
+ def expire
+ Rails.cache.delete(cache_key)
+ rescue
+ # Gracefully handle when Redis is not available. For example,
+ # omnibus may fail here during gitlab:assets:compile.
+ end
+ end
+
+ def cache!
+ Rails.cache.write(self.class.cache_key, attributes.to_json)
+ end
+end
diff --git a/app/models/concerns/resolvable_note.rb b/app/models/concerns/resolvable_note.rb
index 668c5a079e3..4a0f8b92b3a 100644
--- a/app/models/concerns/resolvable_note.rb
+++ b/app/models/concerns/resolvable_note.rb
@@ -32,7 +32,7 @@ module ResolvableNote
# Keep this method in sync with the `potentially_resolvable` scope
def potentially_resolvable?
- RESOLVABLE_TYPES.include?(self.class.name) && noteable.supports_resolvable_notes?
+ RESOLVABLE_TYPES.include?(self.class.name) && noteable&.supports_resolvable_notes?
end
# Keep this method in sync with the `resolvable` scope
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 628c61d5d69..9c4384a6e42 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -104,24 +104,35 @@ class MergeRequest < ActiveRecord::Base
state_machine :merge_status, initial: :unchecked do
event :mark_as_unchecked do
- transition [:can_be_merged, :cannot_be_merged] => :unchecked
+ transition [:can_be_merged, :unchecked] => :unchecked
+ transition [:cannot_be_merged, :cannot_be_merged_recheck] => :cannot_be_merged_recheck
end
event :mark_as_mergeable do
- transition [:unchecked, :cannot_be_merged] => :can_be_merged
+ transition [:unchecked, :cannot_be_merged_recheck] => :can_be_merged
end
event :mark_as_unmergeable do
- transition [:unchecked, :can_be_merged] => :cannot_be_merged
+ transition [:unchecked, :cannot_be_merged_recheck] => :cannot_be_merged
end
state :unchecked
+ state :cannot_be_merged_recheck
state :can_be_merged
state :cannot_be_merged
around_transition do |merge_request, transition, block|
Gitlab::Timeless.timeless(merge_request, &block)
end
+
+ after_transition unchecked: :cannot_be_merged do |merge_request, transition|
+ NotificationService.new.merge_request_unmergeable(merge_request)
+ TodoService.new.merge_request_became_unmergeable(merge_request)
+ end
+
+ def check_state?(merge_status)
+ [:unchecked, :cannot_be_merged_recheck].include?(merge_status.to_sym)
+ end
end
validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
@@ -327,6 +338,16 @@ class MergeRequest < ActiveRecord::Base
update_column(:merge_jid, jid)
end
+ def merge_participants
+ participants = [author]
+
+ if merge_when_pipeline_succeeds? && !participants.include?(merge_user)
+ participants << merge_user
+ end
+
+ participants
+ end
+
def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
@@ -602,7 +623,7 @@ class MergeRequest < ActiveRecord::Base
end
def check_if_can_be_merged
- return unless unchecked? && Gitlab::Database.read_write?
+ return unless self.class.state_machines[:merge_status].check_state?(merge_status) && Gitlab::Database.read_write?
can_be_merged =
!broken? && project.repository.can_be_merged?(diff_head_sha, target_branch)
diff --git a/app/models/project.rb b/app/models/project.rb
index 0e727664d39..0fe9f8880b4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1431,8 +1431,8 @@ class Project < ActiveRecord::Base
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
- def open_issues_count
- Projects::OpenIssuesCountService.new(self).count
+ def open_issues_count(current_user = nil)
+ Projects::OpenIssuesCountService.new(self, current_user).count
end
def open_merge_requests_count
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 71b10fc6bc1..a4bf427ac0b 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -115,6 +115,6 @@ class DroneCiService < CiService
def merge_request_valid?(data)
data[:object_attributes][:state] == 'opened' &&
- data[:object_attributes][:merge_status] == 'unchecked'
+ MergeRequest.state_machines[:merge_status].check_state?(data[:object_attributes][:merge_status])
end
end
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index 4b4132af2d0..ad839d9840a 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -20,6 +20,17 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
end
+ def unmergeable_reasons
+ strong_memoize(:unmergeable_reasons) do
+ reasons = []
+ reasons << "no commits" if merge_request.has_no_commits?
+ reasons << "source branch is missing" unless merge_request.source_branch_exists?
+ reasons << "target branch is missing" unless merge_request.target_branch_exists?
+ reasons << "has merge conflicts" unless merge_request.project.repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch)
+ reasons
+ end
+ end
+
def cancel_merge_when_pipeline_succeeds_path
if can_cancel_merge_when_pipeline_succeeds?(current_user)
cancel_merge_when_pipeline_succeeds_project_merge_request_path(project, merge_request)
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
index eb8caa68ef7..9c5461e85e1 100644
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ b/app/services/clusters/applications/schedule_installation_service.rb
@@ -1,21 +1,10 @@
module Clusters
module Applications
class ScheduleInstallationService < ::BaseService
- def execute
- application_class.find_or_create_by!(cluster: cluster).try do |application|
- application.make_scheduled!
- ClusterInstallAppWorker.perform_async(application.name, application.id)
- end
- end
-
- private
-
- def application_class
- params[:application_class]
- end
+ def execute(application)
+ application.make_scheduled!
- def cluster
- params[:cluster]
+ ClusterInstallAppWorker.perform_async(application.name, application.id)
end
end
end
diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
index 850deb0ac7a..9a4e6eb2e88 100644
--- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
@@ -24,11 +24,7 @@ module MergeRequests
pipeline_merge_requests(pipeline) do |merge_request|
next unless merge_request.merge_when_pipeline_succeeds?
-
- unless merge_request.mergeable?
- todo_service.merge_request_became_unmergeable(merge_request)
- next
- end
+ next unless merge_request.mergeable?
merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params)
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 1fb1796b56c..0127d781686 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -3,6 +3,12 @@ module MergeRequests
def execute(oldrev, newrev, ref)
return true unless Gitlab::Git.branch_ref?(ref)
+ do_execute(oldrev, newrev, ref)
+ end
+
+ private
+
+ def do_execute(oldrev, newrev, ref)
@oldrev, @newrev = oldrev, newrev
@branch_name = Gitlab::Git.ref_name(ref)
@@ -28,8 +34,6 @@ module MergeRequests
true
end
- private
-
def close_upon_missing_source_branch_ref
# MergeRequest#reload_diff ignores not opened MRs. This means it won't
# create an `empty` diff for `closed` MRs without a source branch, keeping
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 5658699664d..4fa38665abc 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -18,6 +18,10 @@ module NotificationRecipientService
Builder::NewNote.new(*a).notification_recipients
end
+ def self.build_merge_request_unmergeable_recipients(*a)
+ Builder::MergeRequestUnmergeable.new(*a).notification_recipients
+ end
+
module Builder
class Base
def initialize(*)
@@ -330,5 +334,26 @@ module NotificationRecipientService
note.author
end
end
+
+ class MergeRequestUnmergeable < Base
+ attr_reader :target
+ def initialize(merge_request)
+ @target = merge_request
+ end
+
+ def build!
+ target.merge_participants.each do |user|
+ add_recipients(user, :participating, nil)
+ end
+ end
+
+ def custom_action
+ :unmergeable_merge_request
+ end
+
+ def acting_user
+ nil
+ end
+ end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 55a1735e54b..636cfbf5b45 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -129,6 +129,7 @@ class NotificationService
# When create a merge request we should send an email to:
#
+ # * mr author
# * mr assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
# * watchers of the mr's labels
@@ -148,6 +149,15 @@ class NotificationService
end
end
+ # When a merge request is found to be unmergeable, we should send an email to:
+ #
+ # * mr author
+ # * mr merge user if set
+ #
+ def merge_request_unmergeable(merge_request)
+ merge_request_unmergeable_email(merge_request)
+ end
+
# When merge request text is updated, we should send an email to:
#
# * newly mentioned project team members with notification level higher than Participating
@@ -484,6 +494,14 @@ class NotificationService
end
end
+ def merge_request_unmergeable_email(merge_request)
+ recipients = NotificationRecipientService.build_merge_request_unmergeable_recipients(merge_request)
+
+ recipients.each do |recipient|
+ mailer.merge_request_unmergeable_email(recipient.user.id, merge_request.id).deliver_later
+ end
+ end
+
def mailer
Notify
end
diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb
index a975a06a05c..0a004677417 100644
--- a/app/services/projects/open_issues_count_service.rb
+++ b/app/services/projects/open_issues_count_service.rb
@@ -2,14 +2,42 @@ module Projects
# Service class for counting and caching the number of open issues of a
# project.
class OpenIssuesCountService < Projects::CountService
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(project, user = nil)
+ @user = user
+
+ super(project)
+ end
+
def cache_key_name
- 'open_issues_count'
+ public_only? ? 'public_open_issues_count' : 'total_open_issues_count'
+ end
+
+ def public_only?
+ !user_is_at_least_reporter?
+ end
+
+ def relation_for_count
+ self.class.query(@project, public_only: public_only?)
+ end
+
+ def user_is_at_least_reporter?
+ strong_memoize(:user_is_at_least_reporter) do
+ @user && @project.team.member?(@user, Gitlab::Access::REPORTER)
+ end
end
- def self.query(project_ids)
- # We don't include confidential issues in this number since this would
- # expose the number of confidential issues to non project members.
- Issue.opened.public_only.where(project: project_ids)
+ # We only show total issues count for reporters
+ # which are allowed to view confidential issues
+ # This will still show a discrepancy on issues number but should be less than before.
+ # Check https://gitlab.com/gitlab-org/gitlab-ce/issues/38418 description.
+ def self.query(projects, public_only: true)
+ if public_only
+ Issue.opened.public_only.where(project: projects)
+ else
+ Issue.opened.where(project: projects)
+ end
end
end
end
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index ffd48e842c2..f91cd03bf5c 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -98,12 +98,12 @@ class TodoService
# When a build fails on the HEAD of a merge request we should:
#
- # * create a todo for author of MR to fix it
- # * create a todo for merge_user to keep an eye on it
+ # * create a todo for each merge participant
#
def merge_request_build_failed(merge_request)
- create_build_failed_todo(merge_request, merge_request.author)
- create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
+ merge_request.merge_participants.each do |user|
+ create_build_failed_todo(merge_request, user)
+ end
end
# When a new commit is pushed to a merge request we should:
@@ -116,20 +116,22 @@ class TodoService
# When a build is retried to a merge request we should:
#
- # * mark all pending todos related to the merge request for the author as done
- # * mark all pending todos related to the merge request for the merge_user as done
+ # * mark all pending todos related to the merge request as done for each merge participant
#
def merge_request_build_retried(merge_request)
- mark_pending_todos_as_done(merge_request, merge_request.author)
- mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
+ merge_request.merge_participants.each do |user|
+ mark_pending_todos_as_done(merge_request, user)
+ end
end
- # When a merge request could not be automatically merged due to its unmergeable state we should:
+ # When a merge request could not be merged due to its unmergeable state we should:
#
- # * create a todo for a merge_user
+ # * create a todo for each merge participant
#
def merge_request_became_unmergeable(merge_request)
- create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
+ merge_request.merge_participants.each do |user|
+ create_unmergeable_todo(merge_request, user)
+ end
end
# When create a note we should:
@@ -275,9 +277,9 @@ class TodoService
create_todos(todo_author, attributes)
end
- def create_unmergeable_todo(merge_request, merge_user)
- attributes = attributes_for_todo(merge_request.project, merge_request, merge_user, Todo::UNMERGEABLE)
- create_todos(merge_user, attributes)
+ def create_unmergeable_todo(merge_request, todo_author)
+ attributes = attributes_for_todo(merge_request.project, merge_request, todo_author, Todo::UNMERGEABLE)
+ create_todos(todo_author, attributes)
end
def attributes_for_target(target)
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index a3549cada95..f2a8afccdeb 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -103,6 +103,7 @@ module ObjectStorage
end
included do
+ include AfterCommitQueue
after_save on: [:create, :update] do
background_upload(changed_mounts)
end
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index b4d2a789df0..079f371e0ba 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -7,7 +7,7 @@
.checkbox
= f.label :auto_devops_enabled do
= f.check_box :auto_devops_enabled
- Enabled Auto DevOps (Beta) for projects by default
+ Enabled Auto DevOps for projects by default
.help-block
It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md')
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index cfa1d9d0f0c..dd1800eff01 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -132,7 +132,7 @@
GitLab
%span.pull-right
= Gitlab::VERSION
- = "(#{Gitlab::REVISION})"
+ = "(#{Gitlab.revision})"
%p
GitLab Shell
%span.pull-right
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 1a3b5e58ed5..e49bf1cffc8 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -45,7 +45,7 @@
.pull-left
= form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
.form-group
- = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token', spellcheck: false
+ = search_field_tag :search, params[:search], class: 'form-control input-short', placeholder: 'Runner description or token', spellcheck: false
= submit_tag 'Search', class: 'btn'
.pull-right.light
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index bf2725dc328..c7a64d77a6a 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -8,7 +8,7 @@
Community Edition
- if user_signed_in?
%span= Gitlab::VERSION
- %small= link_to Gitlab::REVISION, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab::REVISION)
+ %small= link_to Gitlab.revision, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab.revision)
= version_status_badge
%hr
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index f3af15d771d..99e4113bd5a 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -89,7 +89,7 @@
= _('Issues')
- if @project.issues_enabled?
%span.badge.count.issue_counter
- = number_with_delimiter(@project.open_issues_count)
+ = number_with_delimiter(@project.open_issues_count(current_user))
%ul.sidebar-sub-level-items
= nav_link(controller: :issues, html_options: { class: "fly-out-top-item" } ) do
@@ -98,7 +98,7 @@
= _('Issues')
- if @project.issues_enabled?
%span.badge.count.issue_counter.fly-out-badge
- = number_with_delimiter(@project.open_issues_count)
+ = number_with_delimiter(@project.open_issues_count(current_user))
%li.divider.fly-out-top-item
= nav_link(controller: :issues, action: :index) do
= link_to project_issues_path(@project), title: 'Issues' do
diff --git a/app/views/notify/merge_request_unmergeable_email.html.haml b/app/views/notify/merge_request_unmergeable_email.html.haml
new file mode 100644
index 00000000000..e84905a5fad
--- /dev/null
+++ b/app/views/notify/merge_request_unmergeable_email.html.haml
@@ -0,0 +1,6 @@
+%p
+ Merge Request #{link_to @merge_request.to_reference, project_merge_request_url(@merge_request.target_project, @merge_request)} can no longer be merged due to the following #{pluralize(@reasons, 'reason')}:
+
+ %ul
+ - @reasons.each do |reason|
+ %li= reason
diff --git a/app/views/notify/merge_request_unmergeable_email.text.haml b/app/views/notify/merge_request_unmergeable_email.text.haml
new file mode 100644
index 00000000000..4f0901c761a
--- /dev/null
+++ b/app/views/notify/merge_request_unmergeable_email.text.haml
@@ -0,0 +1,11 @@
+Merge Request #{@merge_request.to_reference} can no longer be merged due to the following #{pluralize(@reasons, 'reason')}:
+
+- @reasons.each do |reason|
+ * #{reason}
+
+Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
+
+= merge_path_description(@merge_request, 'to')
+
+Author: #{@merge_request.author_name}
+Assignee: #{@merge_request.assignee_name}
diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml
index 8611129b356..a91e31afc2b 100644
--- a/app/views/projects/commit/branches.html.haml
+++ b/app/views/projects/commit/branches.html.haml
@@ -6,7 +6,8 @@
- if @branches.any? || @tags.any? || @tags_limit_exceeded
%span
- = link_to "…", "#", class: "js-details-expand label label-gray"
+ = link_to "#", class: "js-details-expand label label-gray ref-name" do
+ = sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
%span.js-details-content.hide
= commit_branches_links(@project, @branches)
- if @tags_limit_exceeded
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index a066f9f4cca..63fc5c6d05a 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -22,7 +22,7 @@
%hr
%p
- - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'))
+ - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'))
- link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), new_project_cluster_path(@project))
= s_('AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml
index 54a661040ea..779034fa173 100644
--- a/app/views/projects/merge_requests/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/_how_to_merge.html.haml
@@ -1,4 +1,4 @@
-#modal_merge_info.modal
+#modal_merge_info.modal{ tabindex: '-1' }
.modal-dialog
.modal-content
.modal-header
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 5f596a019f7..7d8dd58e7e0 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -19,7 +19,7 @@
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
- = s_('CICD|Auto DevOps (Beta)')
+ = s_('CICD|Auto DevOps')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
diff --git a/app/views/shared/_auto_devops_callout.html.haml b/app/views/shared/_auto_devops_callout.html.haml
index d3fa324e460..084d295f2c1 100644
--- a/app/views/shared/_auto_devops_callout.html.haml
+++ b/app/views/shared/_auto_devops_callout.html.haml
@@ -3,7 +3,7 @@
= custom_icon('icon_autodevops')
.banner-body.prepend-left-10.append-bottom-10
- %h5.banner-title= s_('AutoDevOps|Auto DevOps (Beta)')
+ %h5.banner-title= s_('AutoDevOps|Auto DevOps')
%p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
%p
- link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
diff --git a/changelogs/unreleased/39710-search-placeholder-cut-off.yml b/changelogs/unreleased/39710-search-placeholder-cut-off.yml
new file mode 100644
index 00000000000..59290768c6a
--- /dev/null
+++ b/changelogs/unreleased/39710-search-placeholder-cut-off.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fixes: Runners search input placeholder is cut off'
+merge_request: 19015
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/44579-ide-add-pipeline-to-status-bar.yml b/changelogs/unreleased/44579-ide-add-pipeline-to-status-bar.yml
new file mode 100644
index 00000000000..21e7c795815
--- /dev/null
+++ b/changelogs/unreleased/44579-ide-add-pipeline-to-status-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Add pipeline status to the status bar of the Web IDE
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml b/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml
new file mode 100644
index 00000000000..c3955c9d8b3
--- /dev/null
+++ b/changelogs/unreleased/45850-close-mr-checkout-modal-on-escape.yml
@@ -0,0 +1,5 @@
+---
+title: Closes MR check out branch modal with escape
+merge_request: Jacopo Beschi @jacopo-beschi
+author: 19050
+type: added
diff --git a/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml b/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml
deleted file mode 100644
index 2f885c5c927..00000000000
--- a/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow CommitStatus class to use presentable methods
-merge_request: 18979
-author:
-type: fixed
diff --git a/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml b/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml
deleted file mode 100644
index e610e53f71c..00000000000
--- a/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes deploy token variables on Ci::Build
-merge_request: 19047
-author:
-type: fixed
diff --git a/changelogs/unreleased/46600-fix-gitlab-revision-when-not-in-git-repo.yml b/changelogs/unreleased/46600-fix-gitlab-revision-when-not-in-git-repo.yml
new file mode 100644
index 00000000000..1d0b11cfd2a
--- /dev/null
+++ b/changelogs/unreleased/46600-fix-gitlab-revision-when-not-in-git-repo.yml
@@ -0,0 +1,6 @@
+---
+title: Replace Gitlab::REVISION with Gitlab.revision and handle installations without
+ a .git directory
+merge_request: 19125
+author:
+type: fixed
diff --git a/changelogs/unreleased/commit-branch-tag-icon-update.yml b/changelogs/unreleased/commit-branch-tag-icon-update.yml
new file mode 100644
index 00000000000..136b7cbf0f4
--- /dev/null
+++ b/changelogs/unreleased/commit-branch-tag-icon-update.yml
@@ -0,0 +1,5 @@
+---
+title: Updated icons for branch and tag names in commit details
+merge_request: 18953
+author: Constance Okoghenun
+type: changed
diff --git a/changelogs/unreleased/fix-devops-remove-beta.yml b/changelogs/unreleased/fix-devops-remove-beta.yml
new file mode 100644
index 00000000000..326003eb956
--- /dev/null
+++ b/changelogs/unreleased/fix-devops-remove-beta.yml
@@ -0,0 +1,5 @@
+---
+title: Removed "(Beta)" from "Auto DevOps" messages
+merge_request: 18759
+author:
+type: changed
diff --git a/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml b/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml
deleted file mode 100644
index 1f64ab9f30f..00000000000
--- a/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix corrupted environment pages with unathorized proxy url
-merge_request: 18989
-author:
-type: fixed
diff --git a/changelogs/unreleased/issue_38418.yml b/changelogs/unreleased/issue_38418.yml
new file mode 100644
index 00000000000..79452b27e4b
--- /dev/null
+++ b/changelogs/unreleased/issue_38418.yml
@@ -0,0 +1,5 @@
+---
+title: Fix issue count on sidebar
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/jprovazn-fix-resolvable.yml b/changelogs/unreleased/jprovazn-fix-resolvable.yml
new file mode 100644
index 00000000000..e17c409e290
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-fix-resolvable.yml
@@ -0,0 +1,5 @@
+---
+title: Fix resolvable check if note's commit could not be found.
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml b/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml
new file mode 100644
index 00000000000..59f375de20e
--- /dev/null
+++ b/changelogs/unreleased/migrate-restore-repo-to-gitaly.yml
@@ -0,0 +1,5 @@
+---
+title: Support restoring repositories into gitaly
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/mr-conflict-notification.yml b/changelogs/unreleased/mr-conflict-notification.yml
new file mode 100644
index 00000000000..d3d5f1fc373
--- /dev/null
+++ b/changelogs/unreleased/mr-conflict-notification.yml
@@ -0,0 +1,5 @@
+---
+title: When MR becomes unmergeable, notify and create todo for author and merge user
+merge_request: 18042
+author:
+type: added
diff --git a/changelogs/unreleased/sh-bump-prometheus-client-mmap.yml b/changelogs/unreleased/sh-bump-prometheus-client-mmap.yml
deleted file mode 100644
index ea8bb0b365b..00000000000
--- a/changelogs/unreleased/sh-bump-prometheus-client-mmap.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump prometheus-client-mmap to 0.9.3 to fix nil exception error
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml b/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml
new file mode 100644
index 00000000000..71b121710ee
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-backup-specific-rake-task.yml
@@ -0,0 +1,5 @@
+---
+title: Fix backup creation and restore for specific Rake tasks
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml b/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml
deleted file mode 100644
index f7abe763ea8..00000000000
--- a/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix system hook not firing for blocked users when LDAP sign-in is used
-merge_request:
-author:
-type: fixed
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 5248bd858a0..dd36700964a 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -470,6 +470,3 @@ if Rails.env.test?
Settings.gitlab['default_can_create_group'] = true
Settings.gitlab['default_can_create_team'] = false
end
-
-# Force a refresh of application settings at startup
-ApplicationSetting.expire
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index eb7959e4da6..146c4b1e024 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -25,7 +25,7 @@ Sidekiq.configure_server do |config|
end
end
-if Gitlab::Metrics.prometheus_metrics_enabled?
+if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
unless Sidekiq.server?
Gitlab::Metrics::Samplers::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
end
diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb
index 536ab337d85..2c46a25f365 100644
--- a/config/initializers/console_message.rb
+++ b/config/initializers/console_message.rb
@@ -3,7 +3,7 @@ if defined?(Rails::Console)
# note that this will not print out when using `spring`
justify = 15
puts "-------------------------------------------------------------------------------------"
- puts " Gitlab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab::REVISION})"
+ puts " Gitlab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab.revision})"
puts " Gitlab Shell:".ljust(justify) + Gitlab::Shell.new.version
puts " #{Gitlab::Database.adapter_name}:".ljust(justify) + Gitlab::Database.version
puts "-------------------------------------------------------------------------------------"
diff --git a/config/initializers/kubeclient.rb b/config/initializers/kubeclient.rb
new file mode 100644
index 00000000000..7f115268b37
--- /dev/null
+++ b/config/initializers/kubeclient.rb
@@ -0,0 +1,16 @@
+class Kubeclient::Client
+ # We need to monkey patch this method until
+ # https://github.com/abonas/kubeclient/pull/323 is merged
+ def proxy_url(kind, name, port, namespace = '')
+ discover unless @discovered
+ entity_name_plural =
+ if %w[services pods nodes].include?(kind.to_s)
+ kind.to_s
+ else
+ @entities[kind.to_s].resource_name
+ end
+
+ ns_prefix = build_namespace_prefix(namespace)
+ rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url
+ end
+end
diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb
index b2da3b3dc19..17d09293205 100644
--- a/config/initializers/sentry.rb
+++ b/config/initializers/sentry.rb
@@ -13,7 +13,7 @@ def configure_sentry
if sentry_enabled
Raven.configure do |config|
config.dsn = Gitlab::CurrentSettings.current_application_settings.sentry_dsn
- config.release = Gitlab::REVISION
+ config.release = Gitlab.revision
# Sanitize fields based on those sanitized from Rails.
config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s)
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 27050e7069d..d6ab32972fb 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -306,7 +306,10 @@ module.exports = {
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
disableHostCheck: true,
- headers: { 'Access-Control-Allow-Origin': '*' },
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Headers': '*',
+ },
stats: 'errors-only',
hot: DEV_SERVER_LIVERELOAD,
inline: DEV_SERVER_LIVERELOAD,
diff --git a/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb b/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb
new file mode 100644
index 00000000000..970a53d68d0
--- /dev/null
+++ b/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb
@@ -0,0 +1,15 @@
+class EnsureMissingColumnsToProjectMirrorData < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ add_column :project_mirror_data, :status, :string unless column_exists?(:project_mirror_data, :status)
+ add_column :project_mirror_data, :jid, :string unless column_exists?(:project_mirror_data, :jid)
+ add_column :project_mirror_data, :last_error, :text unless column_exists?(:project_mirror_data, :last_error)
+ end
+
+ def down
+ # db/migrate/20180502122856_create_project_mirror_data.rb will remove the table
+ end
+end
diff --git a/doc/api/issues.md b/doc/api/issues.md
index d0063e0b8a2..5613cb6d915 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -467,7 +467,7 @@ POST /projects/:id/issues
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Set an issue to be confidential. Default is `false`. |
| `assignee_ids` | Array[integer] | no | The ID of the users to assign issue |
-| `milestone_id` | integer | no | The ID of a milestone to assign issue |
+| `milestone_id` | integer | no | The global ID of a milestone to assign issue |
| `labels` | string | no | Comma-separated label names for an issue |
| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
@@ -548,7 +548,7 @@ PUT /projects/:id/issues/:issue_iid
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Updates an issue to be confidential |
| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
-| `milestone_id` | integer | no | The ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
+| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index cbd51c9870c..4e34831422a 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -539,7 +539,7 @@ POST /projects/:id/merge_requests
| `description` | string | no | Description of MR |
| `target_project_id` | integer | no | The target project (numeric id) |
| `labels` | string | no | Labels for MR as a comma-separated list |
-| `milestone_id` | integer | no | The ID of a milestone |
+| `milestone_id` | integer | no | The global ID of a milestone |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
| `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch |
@@ -622,7 +622,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| `target_branch` | string | no | The target branch |
| `title` | string | no | Title of MR |
| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
-| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
+| `milestone_id` | integer | no | The global ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. |
| `description` | string | no | Description of MR |
| `state_event` | string | no | New state (close/reopen) |
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index d1aa783cc9c..cc19e090964 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -5,10 +5,10 @@ GitLab CI and Docker.
First, you need GitLab Runner with [docker-in-docker executor][dind].
-Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `codequality`:
+Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
```yaml
-codequality:
+code_quality:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -23,20 +23,27 @@ codequality:
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
- paths: [codeclimate.json]
+ paths: [gl-code-quality-report.json]
```
-The above example will create a `codequality` job in your CI/CD pipeline which
+The above example will create a `code_quality` job in your CI/CD pipeline which
will scan your source code for code quality issues. The report will be saved
as an artifact that you can later download and analyze.
TIP: **Tip:**
Starting with [GitLab Starter][ee] 9.3, this information will
be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `codequality` and the artifact path must be
-`codeclimate.json`.
+so, the CI/CD job must be named `code_quality` and the artifact path must be
+`gl-code-quality-report.json`.
[Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
+CAUTION: **Caution:**
+Code Quality was previously using `codeclimate` and `codequality` for job name and
+`codeclimate.json` for the artifact name. While these old names
+are still maintained they have been deprecated with GitLab 11.0 and may be removed
+in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
+configuration to reflect that change.
+
[cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
[ee]: https://about.gitlab.com/products/
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index a9501f6c577..92ff90507ee 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -7,10 +7,10 @@ for Vulnerability Static Analysis for containers.
All you need is a GitLab Runner with the Docker executor (the shared Runners on
GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
-called `sast:container`:
+called `container_scanning`:
```yaml
-sast:container:
+container_scanning:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -34,12 +34,12 @@ sast:container:
- retries=0
- echo "Waiting for clair daemon to start"
- while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
- - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
+ - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
artifacts:
- paths: [gl-sast-container-report.json]
+ paths: [gl-container-scanning-report.json]
```
-The above example will create a `sast:container` job in your CI/CD pipeline, pull
+The above example will create a `container_scanning` job in your CI/CD pipeline, pull
the image from the [Container Registry](../../user/project/container_registry.md)
(whose name is defined from the two `CI_APPLICATION_` variables) and scan it
for possible vulnerabilities. The report will be saved as an artifact that you
@@ -52,8 +52,15 @@ in our case its named `clair-whitelist.yml`.
TIP: **Tip:**
Starting with [GitLab Ultimate][ee] 10.4, this information will
be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `sast:container` and the artifact path must be
-`gl-sast-container-report.json`.
+so, the CI/CD job must be named `container_scanning` and the artifact path must be
+`gl-container-scanning-report.json`.
[Learn more on container scanning results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html).
+CAUTION: **Caution:**
+Container Scanning was previously using `sast:container` for job name and
+`gl-sast-container-report.json` for the artifact name. While these old names
+are still maintained they have been deprecated with GitLab 11.0 and may be removed
+in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
+configuration to reflect that change.
+
[ee]: https://about.gitlab.com/products/
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 1f2b4d9d3d9..1f399a8a3f7 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -27,8 +27,8 @@ Please see the [installation from source guide](installation.md) and the [instal
### Non-Unix operating systems such as Windows
-GitLab is developed for Unix operating systems.
-GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
+GitLab is developed for Unix operating systems.
+It does **not** run on Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567).
Please consider using a virtual machine to run GitLab.
## Ruby versions
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 77139c50d07..5faae7ca2d6 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -31,8 +31,8 @@ sudo apt-get install -y rsync
>**Note:**
In GitLab 9.2 the timestamp format was changed from `EPOCH_YYYY_MM_DD` to
-`EPOCH_YYYY_MM_DD_GitLab version`, for example `1493107454_2017_04_25`
-would become `1493107454_2017_04_25_9.1.0`.
+`EPOCH_YYYY_MM_DD_GitLab_version`, for example `1493107454_2018_04_25`
+would become `1493107454_2018_04_25_10.6.4-ce`.
The backup archive will be saved in `backup_path`, which is specified in the
`config/gitlab.yml` file.
@@ -41,8 +41,8 @@ identifies the time at which each backup was created, plus the GitLab version.
The timestamp is needed if you need to restore GitLab and multiple backups are
available.
-For example, if the backup name is `1493107454_2017_04_25_9.1.0_gitlab_backup.tar`,
-then the timestamp is `1493107454_2017_04_25_9.1.0`.
+For example, if the backup name is `1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar`,
+then the timestamp is `1493107454_2018_04_25_10.6.4-ce`.
### Creating a backup of the GitLab system
@@ -574,7 +574,7 @@ First make sure your backup tar file is in the backup directory described in the
`/var/opt/gitlab/backups`.
```shell
-sudo cp 1493107454_2017_04_25_9.1.0_gitlab_backup.tar /var/opt/gitlab/backups/
+sudo cp 11493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar /var/opt/gitlab/backups/
```
Stop the processes that are connected to the database. Leave the rest of GitLab
@@ -592,7 +592,7 @@ restore:
```shell
# This command will overwrite the contents of your GitLab database!
-sudo gitlab-rake gitlab:backup:restore BACKUP=1493107454_2017_04_25_9.1.0
+sudo gitlab-rake gitlab:backup:restore BACKUP=1493107454_2018_04_25_10.6.4-ce
```
Next, restore `/etc/gitlab/gitlab-secrets.json` if necessary as mentioned above.
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 33e2d710410..efec365042a 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -1,7 +1,5 @@
# Auto DevOps
-DANGER: Auto DevOps is currently in **Beta** and _not recommended for production use_.
-
> [Introduced][ce-37115] in GitLab 10.0.
Auto DevOps automatically detects, builds, tests, deploys, and monitors your
@@ -222,8 +220,8 @@ tests, it's up to you to add them.
### Auto Code Quality
-Auto Code Quality uses the open source
-[`codeclimate` image](https://hub.docker.com/r/codeclimate/codeclimate/) to run
+Auto Code Quality uses the
+[Code Quality image](https://gitlab.com/gitlab-org/security-products/codequality) to run
static analysis and other code checks on the current code. The report is
created, and is uploaded as an artifact which you can later download and check
out.
@@ -496,7 +494,16 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-variables-environment-variables). Set it to use a custom database name. |
| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
+| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
+| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
+| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
+| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
+| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
+| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
+| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
+| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
+| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
TIP: **Tip:**
Set up the replica variables using a
@@ -579,6 +586,21 @@ If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to
to a `staging` environment, and a `production_manual` job will be created for
you when you're ready to manually deploy to production.
+#### Deploy policy for canary environments **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/171)
+in GitLab 11.0.
+
+A [canary environment](https://docs.gitlab.com/ee/user/project/canary_deployments.html) can be used
+before any changes are deployed to production.
+
+If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
+`1` as a secret variable) then two manual jobs will be created:
+
+- `canary` which will deploy the application to the canary environment
+- `production_manual` which is to be used by you when you're ready to manually
+ deploy to production.
+
#### Incremental rollout to production **[PREMIUM]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8.
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 0b16af2953b..61c04f3d9bb 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -1,7 +1,5 @@
# Auto DevOps: quick start guide
-DANGER: Auto DevOps is currently in **Beta** and _not recommended for production use_.
-
> [Introduced][ce-37115] in GitLab 10.0.
This is a step-by-step guide to deploying a project hosted on GitLab.com to
@@ -128,10 +126,10 @@ Next, a pipeline needs to be triggered. Since the test project doesn't have a
manually visit `https://gitlab.com/<username>/minimal-ruby-app/pipelines/new`,
where `<username>` is your username.
-This will create a new pipeline with several jobs: `build`, `test`, `codequality`,
+This will create a new pipeline with several jobs: `build`, `test`, `code_quality`,
and `production`. The `build` job will create a Docker image with your new
change and push it to the Container Registry. The `test` job will test your
-changes, whereas the `codequality` job will run static analysis on your changes.
+changes, whereas the `code_quality` job will run static analysis on your changes.
Finally, the `production` job will deploy your changes to a production application.
Once the deploy job succeeds you should be able to see your application by
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 2170b079f62..8397c0b00ef 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -27,3 +27,36 @@ Personal snippets are not related to any project and can be created completely i
You can download the raw content of a snippet.
By default snippets will be downloaded with Linux-style line endings (`LF`). If you want to preserve the original line endings you need to add a parameter `line_ending=raw` (eg. `https://gitlab.com/snippets/SNIPPET_ID/raw?line_ending=raw`). In case a snippet was created using the GitLab web interface the original line ending is Windows-like (`CRLF`).
+
+## Embedded Snippets
+
+> Introduced in GitLab 10.8.
+
+Public snippets can not only be shared, but also embedded on any website. This
+allows to reuse a GitLab snippet in multiple places and any change to the source
+is automatically reflected in the embedded snippet.
+
+To embed a snippet, first make sure that:
+
+- The project is public (if it's a project snippet)
+- The snippet is public
+- In **Project > Settings > Permissions**, the snippets permissions are
+ set to **Everyone with access**
+
+Once the above conditions are met, the "Embed" section will appear in your snippet
+where you can simply click on the "Copy to clipboard" button. This copies a one-line
+script that you can add to any website or blog post.
+
+Here's how an example code looks like:
+
+```html
+<script src="https://gitlab.com/namespace/project/snippets/SNIPPET_ID.js"></script>
+```
+
+Here's how an embedded snippet looks like:
+
+<script src="https://gitlab.com/gitlab-org/gitlab-ce/snippets/1717978.js"></script>
+
+Embedded snippets are displayed with a header that shows the file name if defined,
+the snippet size, a link to GitLab, and the actual snippet content. Actions in
+the header allow users to see the snippet in raw format and download it.
diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md
index a68bb8b27ca..dc6da1938f3 100644
--- a/doc/workflow/merge_requests.md
+++ b/doc/workflow/merge_requests.md
@@ -1 +1 @@
-This document was moved to [user/project/merge_requests](../user/project/merge_requests.md).
+This document was moved to [user/project/merge_requests/index.md](../user/project/merge_requests/index.md).
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index f1501c81b27..fc592b99860 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -106,6 +106,10 @@ by yourself (except when an issue is due). You will only receive automatic
notifications when somebody else comments or adds changes to the ones that
you've created or mentions you.
+If a merge request becomes unmergeable, its author will be notified about the cause.
+If a user has also set the merge request to automatically merge once pipeline succeeds,
+then that user will also be notified.
+
### Email Headers
Notification emails include headers that provide extra content about the notification received:
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index c99505e6bdf..b2f1cbec204 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -11,7 +11,7 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| <kbd>f</kbd> | Focus filter |
| <kbd>p</kbd> + <kbd>b</kbd> | Show/hide the Performance Bar |
| <kbd>?</kbd> | Show/hide this dialog |
-| <kbd>⌘</kbd> + <kbd>shift</kbd> + <kbd>p</kbd> | Toggle markdown preview |
+| <kbd>Cmd</kbd>/<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>p</kbd> | Toggle markdown preview |
| <kbd>↑</kbd> | Edit last comment (when focused on an empty textarea) |
## Project Files Browsing
@@ -70,8 +70,8 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| <kbd>→</kbd> or <kbd>l</kbd> | Scroll right |
| <kbd>↑</kbd> or <kbd>k</kbd> | Scroll up |
| <kbd>↓</kbd> or <kbd>j</kbd> | Scroll down |
-| <kbd>shift</kbd> + <kbd>↑</kbd> or <kbd>shift</kbd> + <kbd>k</kbd> | Scroll to top |
-| <kbd>shift</kbd> + <kbd>↓</kbd> or <kbd>shift</kbd> + <kbd>j</kbd> | Scroll to bottom |
+| <kbd>Shift</kbd> + <kbd>↑</kbd> or <kbd>Shift</kbd> + <kbd>k</kbd> | Scroll to top |
+| <kbd>Shift</kbd> + <kbd>↓</kbd> or <kbd>Shift</kbd> + <kbd>j</kbd> | Scroll to bottom |
## Issues and Merge Requests
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index f13d29884d4..762bf616268 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -31,6 +31,9 @@ A Todo appears in your Todos dashboard when:
- you are `@mentioned` in a comment on a commit,
- a job in the CI pipeline running for your merge request failed, but this
job is not allowed to fail.
+- a merge request becomes unmergeable, and you are either:
+ - the author, or
+ - have set it to automatically merge once pipeline succeeds.
### Directly addressed Todos
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 6b72caea8fd..a3dac36b8b6 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -113,7 +113,7 @@ module API
{
api_version: API.version,
gitlab_version: Gitlab::VERSION,
- gitlab_rev: Gitlab::REVISION,
+ gitlab_rev: Gitlab.revision,
redis: redis_ping
}
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 152df23a327..e31c332b6e4 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -5,7 +5,7 @@ module API
helpers do
def current_settings
@current_setting ||=
- (ApplicationSetting.current || ApplicationSetting.create_from_defaults)
+ (ApplicationSetting.current_without_cache || ApplicationSetting.create_from_defaults)
end
end
diff --git a/lib/api/v3/settings.rb b/lib/api/v3/settings.rb
index 9b4ab7630fb..fc56495c8b1 100644
--- a/lib/api/v3/settings.rb
+++ b/lib/api/v3/settings.rb
@@ -6,7 +6,7 @@ module API
helpers do
def current_settings
@current_setting ||=
- (ApplicationSetting.current || ApplicationSetting.create_from_defaults)
+ (ApplicationSetting.current_without_cache || ApplicationSetting.create_from_defaults)
end
end
diff --git a/lib/api/version.rb b/lib/api/version.rb
index 9ba576bd828..3b10bfa6a7d 100644
--- a/lib/api/version.rb
+++ b/lib/api/version.rb
@@ -6,7 +6,7 @@ module API
detail 'This feature was introduced in GitLab 8.13.'
end
get '/version' do
- { version: Gitlab::VERSION, revision: Gitlab::REVISION }
+ { version: Gitlab::VERSION, revision: Gitlab.revision }
end
end
end
diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb
index 6a5a223a614..45a935ab352 100644
--- a/lib/backup/artifacts.rb
+++ b/lib/backup/artifacts.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Artifacts < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('artifacts', JobArtifactUploader.root)
end
end
diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb
index f869916e199..adf85ca4719 100644
--- a/lib/backup/builds.rb
+++ b/lib/backup/builds.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Builds < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('builds', Settings.gitlab_ci.builds_path)
end
end
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index 5e6828de597..1608f7ad02d 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -2,9 +2,11 @@ require 'yaml'
module Backup
class Database
+ attr_reader :progress
attr_reader :config, :db_file_name
- def initialize
+ def initialize(progress)
+ @progress = progress
@config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env]
@db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
end
@@ -19,12 +21,12 @@ module Backup
dump_pid =
case config["adapter"]
when /^mysql/ then
- $progress.print "Dumping MySQL database #{config['database']} ... "
+ progress.print "Dumping MySQL database #{config['database']} ... "
# Workaround warnings from MySQL 5.6 about passwords on cmd line
ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
spawn('mysqldump', *mysql_args, config['database'], out: compress_wr)
when "postgresql" then
- $progress.print "Dumping PostgreSQL database #{config['database']} ... "
+ progress.print "Dumping PostgreSQL database #{config['database']} ... "
pg_env
pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump.
if Gitlab.config.backup.pg_schema
@@ -53,12 +55,12 @@ module Backup
restore_pid =
case config["adapter"]
when /^mysql/ then
- $progress.print "Restoring MySQL database #{config['database']} ... "
+ progress.print "Restoring MySQL database #{config['database']} ... "
# Workaround warnings from MySQL 5.6 about passwords on cmd line
ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
spawn('mysql', *mysql_args, config['database'], in: decompress_rd)
when "postgresql" then
- $progress.print "Restoring PostgreSQL database #{config['database']} ... "
+ progress.print "Restoring PostgreSQL database #{config['database']} ... "
pg_env
spawn('psql', config['database'], in: decompress_rd)
end
@@ -111,9 +113,9 @@ module Backup
def report_success(success)
if success
- $progress.puts '[DONE]'.color(:green)
+ progress.puts '[DONE]'.color(:green)
else
- $progress.puts '[FAILED]'.color(:red)
+ progress.puts '[FAILED]'.color(:red)
end
end
end
diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb
index 4e234e50a7a..185ff8ae6bd 100644
--- a/lib/backup/lfs.rb
+++ b/lib/backup/lfs.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Lfs < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('lfs', Settings.lfs.storage_path)
end
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index f27ce4d2b2b..a8da0c7edef 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -4,6 +4,12 @@ module Backup
FOLDERS_TO_BACKUP = %w[repositories db].freeze
FILE_NAME_SUFFIX = '_gitlab_backup.tar'.freeze
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+ end
+
def pack
# Make sure there is a connection
ActiveRecord::Base.connection.reconnect!
@@ -14,11 +20,11 @@ module Backup
end
# create archive
- $progress.print "Creating backup archive: #{tar_file} ... "
+ progress.print "Creating backup archive: #{tar_file} ... "
# Set file permissions on open to prevent chmod races.
tar_system_options = { out: [tar_file, 'w', Gitlab.config.backup.archive_permissions] }
if Kernel.system('tar', '-cf', '-', *backup_contents, tar_system_options)
- $progress.puts "done".color(:green)
+ progress.puts "done".color(:green)
else
puts "creating archive #{tar_file} failed".color(:red)
abort 'Backup failed'
@@ -29,11 +35,11 @@ module Backup
end
def upload
- $progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
+ progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
connection_settings = Gitlab.config.backup.upload.connection
if connection_settings.blank?
- $progress.puts "skipped".color(:yellow)
+ progress.puts "skipped".color(:yellow)
return
end
@@ -43,7 +49,7 @@ module Backup
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
encryption: Gitlab.config.backup.upload.encryption,
storage_class: Gitlab.config.backup.upload.storage_class)
- $progress.puts "done".color(:green)
+ progress.puts "done".color(:green)
else
puts "uploading backup to #{remote_directory} failed".color(:red)
abort 'Backup failed'
@@ -51,13 +57,13 @@ module Backup
end
def cleanup
- $progress.print "Deleting tmp directories ... "
+ progress.print "Deleting tmp directories ... "
backup_contents.each do |dir|
next unless File.exist?(File.join(backup_path, dir))
if FileUtils.rm_rf(File.join(backup_path, dir))
- $progress.puts "done".color(:green)
+ progress.puts "done".color(:green)
else
puts "deleting tmp directory '#{dir}' failed".color(:red)
abort 'Backup failed'
@@ -67,7 +73,7 @@ module Backup
def remove_old
# delete backups
- $progress.print "Deleting old backups ... "
+ progress.print "Deleting old backups ... "
keep_time = Gitlab.config.backup.keep_time.to_i
if keep_time > 0
@@ -88,31 +94,32 @@ module Backup
FileUtils.rm(file)
removed += 1
rescue => e
- $progress.puts "Deleting #{file} failed: #{e.message}".color(:red)
+ progress.puts "Deleting #{file} failed: #{e.message}".color(:red)
end
end
end
end
- $progress.puts "done. (#{removed} removed)".color(:green)
+ progress.puts "done. (#{removed} removed)".color(:green)
else
- $progress.puts "skipping".color(:yellow)
+ progress.puts "skipping".color(:yellow)
end
end
+ # rubocop: disable Metrics/AbcSize
def unpack
Dir.chdir(backup_path) do
# check for existing backups in the backup dir
if backup_file_list.empty?
- $progress.puts "No backups found in #{backup_path}"
- $progress.puts "Please make sure that file name ends with #{FILE_NAME_SUFFIX}"
+ progress.puts "No backups found in #{backup_path}"
+ progress.puts "Please make sure that file name ends with #{FILE_NAME_SUFFIX}"
exit 1
elsif backup_file_list.many? && ENV["BACKUP"].nil?
- $progress.puts 'Found more than one backup:'
+ progress.puts 'Found more than one backup:'
# print list of available backups
- $progress.puts " " + available_timestamps.join("\n ")
- $progress.puts 'Please specify which one you want to restore:'
- $progress.puts 'rake gitlab:backup:restore BACKUP=timestamp_of_backup'
+ progress.puts " " + available_timestamps.join("\n ")
+ progress.puts 'Please specify which one you want to restore:'
+ progress.puts 'rake gitlab:backup:restore BACKUP=timestamp_of_backup'
exit 1
end
@@ -123,31 +130,31 @@ module Backup
end
unless File.exist?(tar_file)
- $progress.puts "The backup file #{tar_file} does not exist!"
+ progress.puts "The backup file #{tar_file} does not exist!"
exit 1
end
- $progress.print 'Unpacking backup ... '
+ progress.print 'Unpacking backup ... '
unless Kernel.system(*%W(tar -xf #{tar_file}))
- $progress.puts 'unpacking backup failed'.color(:red)
+ progress.puts 'unpacking backup failed'.color(:red)
exit 1
else
- $progress.puts 'done'.color(:green)
+ progress.puts 'done'.color(:green)
end
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
# restoring mismatching backups can lead to unexpected problems
if settings[:gitlab_version] != Gitlab::VERSION
- $progress.puts(<<~HEREDOC.color(:red))
+ progress.puts(<<~HEREDOC.color(:red))
GitLab version mismatch:
Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!
Please switch to the following version and try again:
version: #{settings[:gitlab_version]}
HEREDOC
- $progress.puts
- $progress.puts "Hint: git checkout v#{settings[:gitlab_version]}"
+ progress.puts
+ progress.puts "Hint: git checkout v#{settings[:gitlab_version]}"
exit 1
end
end
diff --git a/lib/backup/pages.rb b/lib/backup/pages.rb
index 5830b209d6e..542e35a7c7c 100644
--- a/lib/backup/pages.rb
+++ b/lib/backup/pages.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Pages < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('pages', Gitlab.config.pages.path)
end
end
diff --git a/lib/backup/registry.rb b/lib/backup/registry.rb
index 91698669402..35821805797 100644
--- a/lib/backup/registry.rb
+++ b/lib/backup/registry.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Registry < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('registry', Settings.registry.path)
end
end
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 65e06fd78c0..c3360c391af 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -6,6 +6,12 @@ module Backup
include Backup::Helper
# rubocop:disable Metrics/AbcSize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+ end
+
def dump
prepare
@@ -67,6 +73,9 @@ module Backup
end
def prepare_directories
+ # TODO: Need to find a way to do this for gitaly
+ # Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194
+
Gitlab.config.repositories.storages.each do |name, repository_storage|
path = repository_storage.legacy_disk_path
next unless File.exist?(path)
@@ -87,70 +96,65 @@ module Backup
end
end
+ def restore_custom_hooks(project)
+ # TODO: Need to find a way to do this for gitaly
+ # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195
+ in_path(path_to_tars(project)) do |dir|
+ path_to_project_repo = path_to_repo(project)
+ cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
+
+ output, status = Gitlab::Popen.popen(cmd)
+ unless status.zero?
+ progress_warn(project, cmd.join(' '), output)
+ end
+ end
+ end
+
def restore
prepare_directories
+ gitlab_shell = Gitlab::Shell.new
Project.find_each(batch_size: 1000) do |project|
- progress.print " * #{display_repo_path(project)} ... "
- path_to_project_repo = path_to_repo(project)
+ progress.print " * #{project.full_path} ... "
path_to_project_bundle = path_to_bundle(project)
-
project.ensure_storage_path_exists
- cmd = if File.exist?(path_to_project_bundle)
- %W(#{Gitlab.config.git.bin_path} clone --bare --mirror #{path_to_project_bundle} #{path_to_project_repo})
- else
- %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo})
- end
+ restore_repo_success = nil
+ if File.exist?(path_to_project_bundle)
+ begin
+ gitlab_shell.remove_repository(project.repository_storage, project.disk_path) if project.repository_exists?
+ project.repository.create_from_bundle path_to_project_bundle
+ restore_repo_success = true
+ rescue => e
+ restore_repo_success = false
+ progress.puts "Error: #{e}".color(:red)
+ end
+ else
+ restore_repo_success = gitlab_shell.create_repository(project.repository_storage, project.disk_path)
+ end
- output, status = Gitlab::Popen.popen(cmd)
- if status.zero?
+ if restore_repo_success
progress.puts "[DONE]".color(:green)
else
- progress_warn(project, cmd.join(' '), output)
+ progress.puts "[Failed] restoring #{project.full_path} repository".color(:red)
end
- in_path(path_to_tars(project)) do |dir|
- cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
-
- output, status = Gitlab::Popen.popen(cmd)
- unless status.zero?
- progress_warn(project, cmd.join(' '), output)
- end
- end
+ restore_custom_hooks(project)
wiki = ProjectWiki.new(project)
- path_to_wiki_repo = path_to_repo(wiki)
path_to_wiki_bundle = path_to_bundle(wiki)
if File.exist?(path_to_wiki_bundle)
- progress.print " * #{display_repo_path(wiki)} ... "
-
- # If a wiki bundle exists, first remove the empty repo
- # that was initialized with ProjectWiki.new() and then
- # try to restore with 'git clone --bare'.
- FileUtils.rm_rf(path_to_wiki_repo)
- cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_wiki_bundle} #{path_to_wiki_repo})
-
- output, status = Gitlab::Popen.popen(cmd)
- if status.zero?
- progress.puts " [DONE]".color(:green)
- else
- progress_warn(project, cmd.join(' '), output)
+ progress.print " * #{wiki.full_path} ... "
+ begin
+ gitlab_shell.remove_repository(wiki.repository_storage, wiki.disk_path) if wiki.repository_exists?
+ wiki.repository.create_from_bundle(path_to_wiki_bundle)
+ progress.puts "[DONE]".color(:green)
+ rescue => e
+ progress.puts "[Failed] restoring #{wiki.full_path} wiki".color(:red)
+ progress.puts "Error #{e}".color(:red)
end
end
end
-
- progress.print 'Put GitLab hooks in repositories dirs'.color(:yellow)
- cmd = %W(#{Gitlab.config.gitlab_shell.path}/bin/create-hooks) + repository_storage_paths_args
-
- output, status = Gitlab::Popen.popen(cmd)
- if status.zero?
- progress.puts " [DONE]".color(:green)
- else
- puts " [FAILED]".color(:red)
- puts "failed: #{cmd}"
- puts output
- end
end
# rubocop:enable Metrics/AbcSize
@@ -217,10 +221,6 @@ module Backup
Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path }
end
- def progress
- $progress
- end
-
def display_repo_path(project)
project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path
end
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index d46e2cd869d..49b117a7ee3 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -2,7 +2,11 @@ require 'backup/files'
module Backup
class Uploads < Files
- def initialize
+ attr_reader :progress
+
+ def initialize(progress)
+ @progress = progress
+
super('uploads', Rails.root.join('public/uploads'))
end
end
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 5325819d828..28933c78966 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -23,7 +23,7 @@ module Banzai
private
def settings
- ApplicationSetting.current || ApplicationSetting.create_from_defaults
+ Gitlab::CurrentSettings.current_application_settings
end
def plantuml_setup
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index c5498d0da1a..a129746e2c6 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -9,11 +9,30 @@ module Gitlab
Settings
end
+ def self.migrations_hash
+ @_migrations_hash ||= Digest::MD5.hexdigest(ActiveRecord::Migrator.get_all_versions.to_s)
+ end
+
+ def self.revision
+ @_revision ||= begin
+ if File.exist?(root.join("REVISION"))
+ File.read(root.join("REVISION")).strip.freeze
+ else
+ result = Gitlab::Popen.popen_with_detail(%W[#{config.git.bin_path} log --pretty=format:%h -n 1])
+
+ if result.status.success?
+ result.stdout.chomp.freeze
+ else
+ "Unknown".freeze
+ end
+ end
+ end
+ end
+
COM_URL = 'https://gitlab.com'.freeze
APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
VERSION = File.read(root.join("VERSION")).strip.freeze
- REVISION = Gitlab::Popen.popen(%W(#{config.git.bin_path} log --pretty=format:%h -n 1)).first.chomp.freeze
def self.com?
# Check `gl_subdomain?` as well to keep parity with gitlab.com
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index e392a015b91..6cf7aa1bf0d 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -9,8 +9,8 @@ module Gitlab
end
end
- def fake_application_settings(defaults = ::ApplicationSetting.defaults)
- Gitlab::FakeApplicationSettings.new(defaults)
+ def fake_application_settings(attributes = {})
+ Gitlab::FakeApplicationSettings.new(::ApplicationSetting.defaults.merge(attributes || {}))
end
def method_missing(name, *args, &block)
@@ -25,43 +25,35 @@ module Gitlab
def ensure_application_settings!
return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
-
- cached_application_settings || uncached_application_settings
- end
-
- def cached_application_settings
- begin
- ::ApplicationSetting.cached
- rescue ::Redis::BaseError, ::Errno::ENOENT, ::Errno::EADDRNOTAVAIL
- # In case Redis isn't running or the Redis UNIX socket file is not available
- end
- end
-
- def uncached_application_settings
return fake_application_settings unless connect_to_db?
- db_settings = ::ApplicationSetting.current
-
+ current_settings = ::ApplicationSetting.current
# If there are pending migrations, it's possible there are columns that
# need to be added to the application settings. To prevent Rake tasks
# and other callers from failing, use any loaded settings and return
# defaults for missing columns.
if ActiveRecord::Migrator.needs_migration?
- defaults = ::ApplicationSetting.defaults
- defaults.merge!(db_settings.attributes.symbolize_keys) if db_settings.present?
- return fake_application_settings(defaults)
+ return fake_application_settings(current_settings&.attributes)
end
- return db_settings if db_settings.present?
+ return current_settings if current_settings.present?
- ::ApplicationSetting.create_from_defaults || in_memory_application_settings
+ with_fallback_to_fake_application_settings do
+ ::ApplicationSetting.create_from_defaults || in_memory_application_settings
+ end
end
def in_memory_application_settings
- @in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults) # rubocop:disable Gitlab/ModuleWithInstanceVariables
- rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
- # In case migrations the application_settings table is not created yet,
- # we fallback to a simple OpenStruct
+ with_fallback_to_fake_application_settings do
+ @in_memory_application_settings ||= ::ApplicationSetting.build_from_defaults # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+
+ def with_fallback_to_fake_application_settings(&block)
+ yield
+ rescue
+ # In case the application_settings table is not created yet, or if a new
+ # ApplicationSetting column is not yet migrated we fallback to a simple OpenStruct
fake_application_settings
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index c741dabe168..0d31934347f 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -15,7 +15,7 @@ module Gitlab
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.sentry_dsn = Gitlab::CurrentSettings.clientside_sentry_dsn if Gitlab::CurrentSettings.clientside_sentry_enabled
gon.gitlab_url = Gitlab.config.gitlab.url
- gon.revision = Gitlab::REVISION
+ gon.revision = Gitlab.revision
gon.gitlab_logo = ActionController::Base.helpers.asset_path('gitlab_logo.png')
gon.sprite_icons = IconsHelper.sprite_icon_path
gon.sprite_file_icons = IconsHelper.sprite_file_icons_path
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 24e37f6c6cc..e96fbb64372 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -6,7 +6,6 @@ namespace :gitlab do
desc "GitLab | Create a backup of the GitLab system"
task create: :gitlab_environment do
warn_user_is_not_gitlab
- configure_cron_mode
Rake::Task["gitlab:backup:db:create"].invoke
Rake::Task["gitlab:backup:repo:create"].invoke
@@ -17,7 +16,7 @@ namespace :gitlab do
Rake::Task["gitlab:backup:lfs:create"].invoke
Rake::Task["gitlab:backup:registry:create"].invoke
- backup = Backup::Manager.new
+ backup = Backup::Manager.new(progress)
backup.pack
backup.cleanup
backup.remove_old
@@ -27,9 +26,8 @@ namespace :gitlab do
desc 'GitLab | Restore a previously created backup'
task restore: :gitlab_environment do
warn_user_is_not_gitlab
- configure_cron_mode
- backup = Backup::Manager.new
+ backup = Backup::Manager.new(progress)
backup.unpack
unless backup.skipped?('db')
@@ -49,9 +47,9 @@ namespace :gitlab do
# Drop all tables Load the schema to ensure we don't have any newer tables
# hanging out from a failed upgrade
- $progress.puts 'Cleaning the database ... '.color(:blue)
+ progress.puts 'Cleaning the database ... '.color(:blue)
Rake::Task['gitlab:db:drop_tables'].invoke
- $progress.puts 'done'.color(:green)
+ progress.puts 'done'.color(:green)
Rake::Task['gitlab:backup:db:restore'].invoke
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".color(:red)
@@ -74,173 +72,173 @@ namespace :gitlab do
namespace :repo do
task create: :gitlab_environment do
- $progress.puts "Dumping repositories ...".color(:blue)
+ progress.puts "Dumping repositories ...".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("repositories")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Repository.new.dump
- $progress.puts "done".color(:green)
+ Backup::Repository.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring repositories ...".color(:blue)
- Backup::Repository.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring repositories ...".color(:blue)
+ Backup::Repository.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :db do
task create: :gitlab_environment do
- $progress.puts "Dumping database ... ".color(:blue)
+ progress.puts "Dumping database ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("db")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Database.new.dump
- $progress.puts "done".color(:green)
+ Backup::Database.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring database ... ".color(:blue)
- Backup::Database.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring database ... ".color(:blue)
+ Backup::Database.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :builds do
task create: :gitlab_environment do
- $progress.puts "Dumping builds ... ".color(:blue)
+ progress.puts "Dumping builds ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("builds")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Builds.new.dump
- $progress.puts "done".color(:green)
+ Backup::Builds.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring builds ... ".color(:blue)
- Backup::Builds.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring builds ... ".color(:blue)
+ Backup::Builds.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :uploads do
task create: :gitlab_environment do
- $progress.puts "Dumping uploads ... ".color(:blue)
+ progress.puts "Dumping uploads ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("uploads")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Uploads.new.dump
- $progress.puts "done".color(:green)
+ Backup::Uploads.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring uploads ... ".color(:blue)
- Backup::Uploads.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring uploads ... ".color(:blue)
+ Backup::Uploads.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :artifacts do
task create: :gitlab_environment do
- $progress.puts "Dumping artifacts ... ".color(:blue)
+ progress.puts "Dumping artifacts ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("artifacts")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Artifacts.new.dump
- $progress.puts "done".color(:green)
+ Backup::Artifacts.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring artifacts ... ".color(:blue)
- Backup::Artifacts.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring artifacts ... ".color(:blue)
+ Backup::Artifacts.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :pages do
task create: :gitlab_environment do
- $progress.puts "Dumping pages ... ".color(:blue)
+ progress.puts "Dumping pages ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("pages")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Pages.new.dump
- $progress.puts "done".color(:green)
+ Backup::Pages.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring pages ... ".color(:blue)
- Backup::Pages.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring pages ... ".color(:blue)
+ Backup::Pages.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :lfs do
task create: :gitlab_environment do
- $progress.puts "Dumping lfs objects ... ".color(:blue)
+ progress.puts "Dumping lfs objects ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("lfs")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Lfs.new.dump
- $progress.puts "done".color(:green)
+ Backup::Lfs.new(progress).dump
+ progress.puts "done".color(:green)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring lfs objects ... ".color(:blue)
- Backup::Lfs.new.restore
- $progress.puts "done".color(:green)
+ progress.puts "Restoring lfs objects ... ".color(:blue)
+ Backup::Lfs.new(progress).restore
+ progress.puts "done".color(:green)
end
end
namespace :registry do
task create: :gitlab_environment do
- $progress.puts "Dumping container registry images ... ".color(:blue)
+ progress.puts "Dumping container registry images ... ".color(:blue)
if Gitlab.config.registry.enabled
if ENV["SKIP"] && ENV["SKIP"].include?("registry")
- $progress.puts "[SKIPPED]".color(:cyan)
+ progress.puts "[SKIPPED]".color(:cyan)
else
- Backup::Registry.new.dump
- $progress.puts "done".color(:green)
+ Backup::Registry.new(progress).dump
+ progress.puts "done".color(:green)
end
else
- $progress.puts "[DISABLED]".color(:cyan)
+ progress.puts "[DISABLED]".color(:cyan)
end
end
task restore: :gitlab_environment do
- $progress.puts "Restoring container registry images ... ".color(:blue)
+ progress.puts "Restoring container registry images ... ".color(:blue)
if Gitlab.config.registry.enabled
- Backup::Registry.new.restore
- $progress.puts "done".color(:green)
+ Backup::Registry.new(progress).restore
+ progress.puts "done".color(:green)
else
- $progress.puts "[DISABLED]".color(:cyan)
+ progress.puts "[DISABLED]".color(:cyan)
end
end
end
- def configure_cron_mode
+ def progress
if ENV['CRON']
# We need an object we can say 'puts' and 'print' to; let's use a
# StringIO.
require 'stringio'
- $progress = StringIO.new
+ StringIO.new
else
- $progress = $stdout
+ $stdout
end
end
end # namespace end: backup
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 47ed522aec3..289aa5d9060 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -47,7 +47,7 @@ namespace :gitlab do
puts ""
puts "GitLab information".color(:yellow)
puts "Version:\t#{Gitlab::VERSION}"
- puts "Revision:\t#{Gitlab::REVISION}"
+ puts "Revision:\t#{Gitlab.revision}"
puts "Directory:\t#{Rails.root}"
puts "DB Adapter:\t#{database_adapter}"
puts "URL:\t\t#{Gitlab.config.gitlab.url}"
diff --git a/package.json b/package.json
index 1382afd41d6..68e5e0a9b6a 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.22.0",
+ "@gitlab-org/gitlab-svgs": "^1.23.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.3",
diff --git a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
index 2d7647a6e12..397cc79bde4 100644
--- a/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/conflicts_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::MergeRequests::ConflictsController do
let(:user) { project.owner }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
let(:merge_request_with_conflicts) do
- create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) do |mr|
+ create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c8cc6b374f6..d3042be9e8b 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::MergeRequestsController do
let(:user) { project.owner }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
let(:merge_request_with_conflicts) do
- create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) do |mr|
+ create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
index b2b245dba90..871dcf5c796 100644
--- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
@@ -12,44 +12,67 @@ describe Projects::Prometheus::MetricsController do
end
describe 'GET #active_common' do
- before do
- allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
- end
+ context 'when prometheus_adapter can query' do
+ before do
+ allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+ end
- context 'when prometheus metrics are enabled' do
- context 'when data is not present' do
- before do
- allow(prometheus_adapter).to receive(:query).with(:matched_metrics).and_return({})
- end
+ context 'when prometheus metrics are enabled' do
+ context 'when data is not present' do
+ before do
+ allow(prometheus_adapter).to receive(:query).with(:matched_metrics).and_return({})
+ end
- it 'returns no content response' do
- get :active_common, project_params(format: :json)
+ it 'returns no content response' do
+ get :active_common, project_params(format: :json)
- expect(response).to have_gitlab_http_status(204)
+ expect(response).to have_gitlab_http_status(204)
+ end
end
- end
- context 'when data is available' do
- let(:sample_response) { { some_data: 1 } }
+ context 'when data is available' do
+ let(:sample_response) { { some_data: 1 } }
+
+ before do
+ allow(prometheus_adapter).to receive(:query).with(:matched_metrics).and_return(sample_response)
+ end
- before do
- allow(prometheus_adapter).to receive(:query).with(:matched_metrics).and_return(sample_response)
+ it 'returns no content response' do
+ get :active_common, project_params(format: :json)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq(sample_response.deep_stringify_keys)
+ end
end
- it 'returns no content response' do
- get :active_common, project_params(format: :json)
+ context 'when requesting non json response' do
+ it 'returns not found response' do
+ get :active_common, project_params
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to eq(sample_response.deep_stringify_keys)
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
+ end
- context 'when requesting non json response' do
- it 'returns not found response' do
- get :active_common, project_params
+ context 'when prometheus_adapter cannot query' do
+ it 'renders 404' do
+ prometheus_adapter = double('prometheus_adapter', can_query?: false)
- expect(response).to have_gitlab_http_status(404)
- end
+ allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+ allow(prometheus_adapter).to receive(:query).with(:matched_metrics).and_return({})
+
+ get :active_common, project_params(format: :json)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when prometheus_adapter is disabled' do
+ it 'renders 404' do
+ get :active_common, project_params(format: :json)
+
+ expect(response).to have_gitlab_http_status(404)
end
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index f2f9b734c39..dc025d82937 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -152,7 +152,7 @@ feature 'Admin updates settings' do
scenario 'Change CI/CD settings' do
page.within('.as-ci-cd') do
- check 'Enabled Auto DevOps (Beta) for projects by default'
+ check 'Enabled Auto DevOps for projects by default'
fill_in 'Auto devops domain', with: 'domain.com'
click_button 'Save changes'
end
diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index 25a57f7d379..59aa90fc86f 100644
--- a/spec/features/merge_request/user_resolves_conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -10,7 +10,7 @@ describe 'Merge request > User resolves conflicts', :js do
end
def create_merge_request(source_branch)
- create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project) do |mr|
+ create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
new file mode 100644
index 00000000000..c40c720d168
--- /dev/null
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees Check out branch modal', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+ click_button('Check out branch')
+ end
+
+ it 'shows the check out branch modal' do
+ expect(page).to have_content('Check out, review, and merge locally')
+ end
+
+ it 'closes the check out branch model with Escape keypress' do
+ find('#modal_merge_info').send_keys(:escape)
+
+ expect(page).not_to have_content('Check out, review, and merge locally')
+ end
+end
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index 3d7ccf432be..e8435116221 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -341,4 +341,25 @@ describe('Api', () => {
.catch(done.fail);
});
});
+
+ describe('commitPipelines', () => {
+ it('fetches pipelines for a given commit', done => {
+ const projectId = 'example/foobar';
+ const commitSha = 'abc123def';
+ const expectedUrl = `${dummyUrlRoot}/${projectId}/commit/${commitSha}/pipelines`;
+ mock.onGet(expectedUrl).reply(200, [
+ {
+ name: 'test',
+ },
+ ]);
+
+ Api.commitPipelines(projectId, commitSha)
+ .then(({ data }) => {
+ expect(data.length).toBe(1);
+ expect(data[0].name).toBe('test');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
index 7e641c7984b..c68ae050641 100644
--- a/spec/javascripts/ide/mock_data.js
+++ b/spec/javascripts/ide/mock_data.js
@@ -59,3 +59,37 @@ export const jobs = [
duration: 1,
},
];
+
+export const fullPipelinesResponse = {
+ data: {
+ count: {
+ all: 2,
+ },
+ pipelines: [
+ {
+ id: '51',
+ commit: {
+ id: 'xxxxxxxxxxxxxxxxxxxx',
+ },
+ details: {
+ status: {
+ icon: 'status_failed',
+ text: 'failed',
+ },
+ },
+ },
+ {
+ id: '50',
+ commit: {
+ id: 'abc123def456ghi789jkl',
+ },
+ details: {
+ status: {
+ icon: 'status_passed',
+ text: 'passed',
+ },
+ },
+ },
+ ],
+ },
+};
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index ebd08d95810..8e078ae7138 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -1,14 +1,33 @@
-import {
- refreshLastCommitData,
-} from '~/ide/stores/actions';
+import Visibility from 'visibilityjs';
+import MockAdapter from 'axios-mock-adapter';
+import { refreshLastCommitData, pollSuccessCallBack } from '~/ide/stores/actions';
import store from '~/ide/stores';
import service from '~/ide/services';
+import axios from '~/lib/utils/axios_utils';
+import { fullPipelinesResponse } from '../../mock_data';
import { resetStore } from '../../helpers';
import testAction from '../../../helpers/vuex_action_helper';
describe('IDE store project actions', () => {
+ const setProjectState = () => {
+ store.state.currentProjectId = 'abc/def';
+ store.state.currentBranchId = 'master';
+ store.state.projects['abc/def'] = {
+ id: 4,
+ path_with_namespace: 'abc/def',
+ branches: {
+ master: {
+ commit: {
+ id: 'abc123def456ghi789jkl',
+ title: 'example',
+ },
+ },
+ },
+ };
+ };
+
beforeEach(() => {
- store.state.projects.abcproject = {};
+ store.state.projects['abc/def'] = {};
});
afterEach(() => {
@@ -17,18 +36,16 @@ describe('IDE store project actions', () => {
describe('refreshLastCommitData', () => {
beforeEach(() => {
- store.state.currentProjectId = 'abcproject';
+ store.state.currentProjectId = 'abc/def';
store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
+ store.state.projects['abc/def'] = {
+ id: 4,
branches: {
master: {
commit: null,
},
},
};
- });
-
- it('calls the service', done => {
spyOn(service, 'getBranchData').and.returnValue(
Promise.resolve({
data: {
@@ -36,14 +53,16 @@ describe('IDE store project actions', () => {
},
}),
);
+ });
+ it('calls the service', done => {
store
.dispatch('refreshLastCommitData', {
projectId: store.state.currentProjectId,
branchId: store.state.currentBranchId,
})
.then(() => {
- expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
+ expect(service.getBranchData).toHaveBeenCalledWith('abc/def', 'master');
done();
})
@@ -53,16 +72,118 @@ describe('IDE store project actions', () => {
it('commits getBranchData', done => {
testAction(
refreshLastCommitData,
- {},
- {},
- [{
- type: 'SET_BRANCH_COMMIT',
- payload: {
- projectId: 'abcproject',
- branchId: 'master',
- commit: { id: '123' },
+ {
+ projectId: store.state.currentProjectId,
+ branchId: store.state.currentBranchId,
+ },
+ store.state,
+ [
+ {
+ type: 'SET_BRANCH_COMMIT',
+ payload: {
+ projectId: 'abc/def',
+ branchId: 'master',
+ commit: { id: '123' },
+ },
+ },
+ ], // mutations
+ [
+ {
+ type: 'getLastCommitPipeline',
+ payload: {
+ projectId: 'abc/def',
+ projectIdNumber: store.state.projects['abc/def'].id,
+ branchId: 'master',
+ },
+ },
+ ], // action
+ done,
+ );
+ });
+ });
+
+ describe('pipelinePoll', () => {
+ let mock;
+
+ beforeEach(() => {
+ setProjectState();
+ jasmine.clock().install();
+ mock = new MockAdapter(axios);
+ mock
+ .onGet('/abc/def/commit/abc123def456ghi789jkl/pipelines')
+ .reply(200, { data: { foo: 'bar' } }, { 'poll-interval': '10000' });
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ mock.restore();
+ store.dispatch('stopPipelinePolling');
+ });
+
+ it('calls service periodically', done => {
+ spyOn(axios, 'get').and.callThrough();
+ spyOn(Visibility, 'hidden').and.returnValue(false);
+
+ store
+ .dispatch('pipelinePoll')
+ .then(() => {
+ jasmine.clock().tick(1000);
+
+ expect(axios.get).toHaveBeenCalled();
+ expect(axios.get.calls.count()).toBe(1);
+ })
+ .then(() => new Promise(resolve => requestAnimationFrame(resolve)))
+ .then(() => {
+ jasmine.clock().tick(10000);
+ expect(axios.get.calls.count()).toBe(2);
+ })
+ .then(() => new Promise(resolve => requestAnimationFrame(resolve)))
+ .then(() => {
+ jasmine.clock().tick(10000);
+ expect(axios.get.calls.count()).toBe(3);
+ })
+ .then(() => new Promise(resolve => requestAnimationFrame(resolve)))
+ .then(() => {
+ jasmine.clock().tick(10000);
+ expect(axios.get.calls.count()).toBe(4);
+ })
+
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('pollSuccessCallBack', () => {
+ beforeEach(() => {
+ setProjectState();
+ });
+
+ it('commits correct pipeline', done => {
+ testAction(
+ pollSuccessCallBack,
+ fullPipelinesResponse,
+ store.state,
+ [
+ {
+ type: 'SET_LAST_COMMIT_PIPELINE',
+ payload: {
+ projectId: 'abc/def',
+ branchId: 'master',
+ pipeline: {
+ id: '50',
+ commit: {
+ id: 'abc123def456ghi789jkl',
+ },
+ details: {
+ status: {
+ icon: 'status_passed',
+ text: 'passed',
+ },
+ },
+ },
+ },
},
- }], // mutations
+ ], // mutations
[], // action
done,
);
diff --git a/spec/javascripts/ide/stores/mutations/branch_spec.js b/spec/javascripts/ide/stores/mutations/branch_spec.js
index 29eb859ddaf..f2f1f2a9a2e 100644
--- a/spec/javascripts/ide/stores/mutations/branch_spec.js
+++ b/spec/javascripts/ide/stores/mutations/branch_spec.js
@@ -37,4 +37,40 @@ describe('Multi-file store branch mutations', () => {
expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit');
});
});
+
+ describe('SET_LAST_COMMIT_PIPELINE', () => {
+ it('sets the pipeline for the last commit on current project', () => {
+ localState.projects = {
+ Example: {
+ branches: {
+ master: {
+ commit: {},
+ },
+ },
+ },
+ };
+
+ mutations.SET_LAST_COMMIT_PIPELINE(localState, {
+ projectId: 'Example',
+ branchId: 'master',
+ pipeline: {
+ id: '50',
+ details: {
+ status: {
+ icon: 'status_passed',
+ text: 'passed',
+ },
+ },
+ },
+ });
+
+ expect(localState.projects.Example.branches.master.commit.pipeline.id).toBe('50');
+ expect(localState.projects.Example.branches.master.commit.pipeline.details.status.text).toBe(
+ 'passed',
+ );
+ expect(localState.projects.Example.branches.master.commit.pipeline.details.status.icon).toBe(
+ 'status_passed',
+ );
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/javascripts/pipelines/empty_state_spec.js
index 71f77e5f42e..1e41a7bfe7c 100644
--- a/spec/javascripts/pipelines/empty_state_spec.js
+++ b/spec/javascripts/pipelines/empty_state_spec.js
@@ -29,7 +29,7 @@ describe('Pipelines Empty State', () => {
expect(
component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
- ).toContain('Continous Integration can help catch bugs by running your tests automatically,');
+ ).toContain('Continuous Integration can help catch bugs by running your tests automatically,');
expect(
component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js
index a5a200973d7..03ead6cd8ba 100644
--- a/spec/javascripts/pipelines/mock_data.js
+++ b/spec/javascripts/pipelines/mock_data.js
@@ -217,7 +217,7 @@ export const pipelineWithStages = {
browse_path: '/gitlab-org/gitlab-ee/-/jobs/62411442/artifacts/browse',
},
{
- name: 'codequality',
+ name: 'code_quality',
expired: false,
expire_at: '2018-04-18T14:16:24.484Z',
path: '/gitlab-org/gitlab-ee/-/jobs/62411441/artifacts/download',
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 05ca4cb9044..68043b91bd0 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -182,7 +182,16 @@ describe('Pipelines Table Row', () => {
});
component.$el.querySelector('.js-pipelines-cancel-button').click();
- expect(component.isCancelling).toEqual(true);
+ });
+
+ it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => {
+ component.cancelingPipeline = pipeline.id;
+ component.$nextTick()
+ .then(() => {
+ expect(component.isCancelling).toEqual(true);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 84688845fa5..23c04a1a101 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -5,6 +5,8 @@ describe Backup::Manager do
let(:progress) { StringIO.new }
+ subject { described_class.new(progress) }
+
before do
allow(progress).to receive(:puts)
allow(progress).to receive(:print)
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index b1ea9c0b622..a44243ac82d 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe Backup::Repository do
let(:progress) { StringIO.new }
let!(:project) { create(:project, :wiki_repo) }
+ subject { described_class.new(progress) }
before do
allow(progress).to receive(:puts)
@@ -24,14 +25,12 @@ describe Backup::Repository do
end
it 'does not raise error' do
- expect { described_class.new.dump }.not_to raise_error
+ expect { subject.dump }.not_to raise_error
end
end
end
describe '#restore' do
- subject { described_class.new }
-
let(:timestamp) { Time.utc(2017, 3, 22) }
let(:temp_dirs) do
Gitlab.config.repositories.storages.map do |name, storage|
@@ -49,14 +48,14 @@ describe Backup::Repository do
describe 'command failure' do
before do
- allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1])
+ allow_any_instance_of(Gitlab::Shell).to receive(:create_repository).and_return(false)
end
context 'hashed storage' do
it 'shows the appropriate error' do
subject.restore
- expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} (#{project.disk_path}) - error")
+ expect(progress).to have_received(:puts).with("[Failed] restoring #{project.full_path} repository")
end
end
@@ -66,33 +65,10 @@ describe Backup::Repository do
it 'shows the appropriate error' do
subject.restore
- expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
+ expect(progress).to have_received(:puts).with("[Failed] restoring #{project.full_path} repository")
end
end
end
-
- describe 'folders without permissions' do
- before do
- allow(FileUtils).to receive(:mv).and_raise(Errno::EACCES)
- end
-
- it 'shows error message' do
- expect(subject).to receive(:access_denied_error)
- subject.restore
- end
- end
-
- describe 'folder that is a mountpoint' do
- before do
- allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY)
- end
-
- it 'shows error message' do
- expect(subject).to receive(:resource_busy_error).and_call_original
-
- expect { subject.restore }.to raise_error(/is a mountpoint/)
- end
- end
end
describe '#empty_repo?' do
@@ -102,20 +78,20 @@ describe Backup::Repository do
it 'invalidates the emptiness cache' do
expect(wiki.repository).to receive(:expire_emptiness_caches).once
- described_class.new.send(:empty_repo?, wiki)
+ subject.send(:empty_repo?, wiki)
end
context 'wiki repo has content' do
let!(:wiki_page) { create(:wiki_page, wiki: wiki) }
it 'returns true, regardless of bad cache value' do
- expect(described_class.new.send(:empty_repo?, wiki)).to be(false)
+ expect(subject.send(:empty_repo?, wiki)).to be(false)
end
end
context 'wiki repo does not have content' do
it 'returns true, regardless of bad cache value' do
- expect(described_class.new.send(:empty_repo?, wiki)).to be_truthy
+ expect(subject.send(:empty_repo?, wiki)).to be_truthy
end
end
end
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 4ddcbd7eb66..19028495f52 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -1,17 +1,15 @@
require 'spec_helper'
describe Gitlab::CurrentSettings do
- include StubENV
-
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
- describe '#current_application_settings' do
+ describe '#current_application_settings', :use_clean_rails_memory_store_caching do
it 'allows keys to be called directly' do
db_settings = create(:application_setting,
- home_page_url: 'http://mydomain.com',
- signup_enabled: false)
+ home_page_url: 'http://mydomain.com',
+ signup_enabled: false)
expect(described_class.home_page_url).to eq(db_settings.home_page_url)
expect(described_class.signup_enabled?).to be_falsey
@@ -19,46 +17,54 @@ describe Gitlab::CurrentSettings do
expect(described_class.metrics_sample_interval).to be(15)
end
- context 'with DB available' do
+ context 'when ENV["IN_MEMORY_APPLICATION_SETTINGS"] is true' do
before do
- # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues
- # during the initialization phase of the test suite, so instead let's mock the internals of it
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true)
- allow(ActiveRecord::Base.connection).to receive(:table_exists?).and_call_original
- allow(ActiveRecord::Base.connection).to receive(:table_exists?).with('application_settings').and_return(true)
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
end
- it 'attempts to use cached values first' do
- expect(ApplicationSetting).to receive(:cached)
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).not_to receive(:current)
expect(described_class.current_application_settings).to be_a(ApplicationSetting)
+ expect(described_class.current_application_settings).not_to be_persisted
end
+ end
- it 'falls back to DB if Redis returns an empty value' do
- expect(ApplicationSetting).to receive(:cached).and_return(nil)
- expect(ApplicationSetting).to receive(:last).and_call_original.twice
-
- expect(described_class.current_application_settings).to be_a(ApplicationSetting)
+ context 'with DB unavailable' do
+ before do
+ # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues
+ # during the initialization phase of the test suite, so instead let's mock the internals of it
+ allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false)
end
- it 'falls back to DB if Redis fails' do
- db_settings = ApplicationSetting.create!(ApplicationSetting.defaults)
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).not_to receive(:current)
- expect(ApplicationSetting).to receive(:cached).and_raise(::Redis::BaseError)
- expect(Rails.cache).to receive(:fetch).with(ApplicationSetting::CACHE_KEY).and_raise(Redis::BaseError)
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
+ end
- expect(described_class.current_application_settings).to eq(db_settings)
+ context 'with DB available' do
+ # This method returns the ::ApplicationSetting.defaults hash
+ # but with respect of custom attribute accessors of ApplicationSetting model
+ def settings_from_defaults
+ ar_wrapped_defaults = ::ApplicationSetting.build_from_defaults.attributes
+ ar_wrapped_defaults.slice(*::ApplicationSetting.defaults.keys)
end
- it 'creates default ApplicationSettings if none are present' do
- expect(ApplicationSetting).to receive(:cached).and_raise(::Redis::BaseError)
- expect(Rails.cache).to receive(:fetch).with(ApplicationSetting::CACHE_KEY).and_raise(Redis::BaseError)
+ before do
+ # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues
+ # during the initialization phase of the test suite, so instead let's mock the internals of it
+ allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true)
+ allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
+ end
+ it 'creates default ApplicationSettings if none are present' do
settings = described_class.current_application_settings
expect(settings).to be_a(ApplicationSetting)
expect(settings).to be_persisted
- expect(settings).to have_attributes(ApplicationSetting.defaults)
+ expect(settings).to have_attributes(settings_from_defaults)
end
context 'with migrations pending' do
@@ -69,7 +75,7 @@ describe Gitlab::CurrentSettings do
it 'returns an in-memory ApplicationSetting object' do
settings = described_class.current_application_settings
- expect(settings).to be_a(OpenStruct)
+ expect(settings).to be_a(Gitlab::FakeApplicationSettings)
expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
end
@@ -81,7 +87,7 @@ describe Gitlab::CurrentSettings do
settings = described_class.current_application_settings
app_defaults = ApplicationSetting.last
- expect(settings).to be_a(OpenStruct)
+ expect(settings).to be_a(Gitlab::FakeApplicationSettings)
expect(settings.home_page_url).to eq(db_settings.home_page_url)
expect(settings.signup_enabled?).to be_falsey
expect(settings.signup_enabled).to be_falsey
@@ -91,34 +97,29 @@ describe Gitlab::CurrentSettings do
settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
end
end
- end
-
- context 'with DB unavailable' do
- before do
- # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues
- # during the initialization phase of the test suite, so instead let's mock the internals of it
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false)
- end
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).not_to receive(:current)
- expect(ApplicationSetting).not_to receive(:last)
+ context 'when ApplicationSettings.current is present' do
+ it 'returns the existing application settings' do
+ expect(ApplicationSetting).to receive(:current).and_return(:current_settings)
- expect(described_class.current_application_settings).to be_a(OpenStruct)
+ expect(described_class.current_application_settings).to eq(:current_settings)
+ end
end
- end
- context 'when ENV["IN_MEMORY_APPLICATION_SETTINGS"] is true' do
- before do
- stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
+ context 'when the application_settings table does not exists' do
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid)
+
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
end
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).not_to receive(:current)
- expect(ApplicationSetting).not_to receive(:last)
+ context 'when the application_settings table is not fully migrated' do
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError)
- expect(described_class.current_application_settings).to be_a(ApplicationSetting)
- expect(described_class.current_application_settings).not_to be_persisted
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
end
end
end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index da146e24893..d63f448883b 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -8,6 +8,66 @@ describe Gitlab do
expect(described_class.root).to eq(Pathname.new(File.expand_path('../..', __dir__)))
end
end
+ describe '.revision' do
+ let(:cmd) { %W[#{described_class.config.git.bin_path} log --pretty=format:%h -n 1] }
+
+ around do |example|
+ described_class.instance_variable_set(:@_revision, nil)
+ example.run
+ described_class.instance_variable_set(:@_revision, nil)
+ end
+
+ context 'when a REVISION file exists' do
+ before do
+ expect(File).to receive(:exist?)
+ .with(described_class.root.join('REVISION'))
+ .and_return(true)
+ end
+
+ it 'returns the actual Git revision' do
+ expect(File).to receive(:read)
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
+
+ expect(described_class.revision).to eq('abc123')
+ end
+
+ it 'memoizes the revision' do
+ expect(File).to receive(:read)
+ .once
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
+
+ 2.times { described_class.revision }
+ end
+ end
+
+ context 'when no REVISION file exist' do
+ context 'when the Git command succeeds' do
+ before do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true)))
+ end
+
+ it 'returns the actual Git revision' do
+ expect(described_class.revision).to eq('abc123')
+ end
+ end
+
+ context 'when the Git command fails' do
+ before do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false)))
+ end
+
+ it 'returns "Unknown"' do
+ expect(described_class.revision).to eq('Unknown')
+ end
+ end
+ end
+ end
describe '.com?' do
it 'is true when on GitLab.com' do
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 8a52c151cc4..f310a6854d5 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -390,6 +390,46 @@ describe Notify do
end
end
+ describe 'that are unmergeable' do
+ set(:merge_request) do
+ create(:merge_request, :conflict,
+ source_project: project,
+ target_project: project,
+ author: current_user,
+ assignee: assignee,
+ description: 'Awesome description')
+ end
+
+ subject { described_class.merge_request_unmergeable_email(recipient.id, merge_request.id) }
+
+ it_behaves_like 'a multiple recipients email'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
+ it_behaves_like 'it should show Gmail Actions View Merge request link'
+ it_behaves_like 'an unsubscribeable thread'
+
+ it 'is sent as the merge request author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(merge_request.author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'has the correct subject and body' do
+ reasons = %w[foo bar]
+
+ allow_any_instance_of(MergeRequestPresenter).to receive(:unmergeable_reasons).and_return(reasons)
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text(project_merge_request_path(project, merge_request))
+ is_expected.to have_body_text('reasons:')
+ reasons.each do |reason|
+ is_expected.to have_body_text(reason)
+ end
+ end
+ end
+ end
+
shared_examples 'a push to an existing merge request' do
let(:push_user) { create(:user) }
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index 5489c17bd82..77b07cf1ac9 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -3,34 +3,11 @@ require 'rails_helper'
describe Appearance do
subject { build(:appearance) }
- it { is_expected.to be_valid }
+ it { include(CacheableAttributes) }
+ it { expect(described_class.current_without_cache).to eq(described_class.first) }
it { is_expected.to have_many(:uploads) }
- describe '.current', :use_clean_rails_memory_store_caching do
- let!(:appearance) { create(:appearance) }
-
- it 'returns the current appearance row' do
- expect(described_class.current).to eq(appearance)
- end
-
- it 'caches the result' do
- expect(described_class).to receive(:first).once
-
- 2.times { described_class.current }
- end
- end
-
- describe '#flush_redis_cache' do
- it 'flushes the cache in Redis' do
- appearance = create(:appearance)
-
- expect(Rails.cache).to receive(:delete).with(described_class::CACHE_KEY)
-
- appearance.flush_redis_cache
- end
- end
-
describe '#single_appearance_row' do
it 'adds an error when more than 1 row exists' do
create(:appearance)
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 10d6109cae7..7e47043a1cb 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -3,6 +3,9 @@ require 'spec_helper'
describe ApplicationSetting do
let(:setting) { described_class.create_from_defaults }
+ it { include(CacheableAttributes) }
+ it { expect(described_class.current_without_cache).to eq(described_class.last) }
+
it { expect(setting).to be_valid }
it { expect(setting.uuid).to be_present }
it { expect(setting).to have_db_column(:auto_devops_enabled) }
@@ -318,33 +321,6 @@ describe ApplicationSetting do
end
end
- describe '.current' do
- context 'redis unavailable' do
- it 'returns an ApplicationSetting' do
- allow(Rails.cache).to receive(:fetch).and_call_original
- allow(described_class).to receive(:last).and_return(:last)
- expect(Rails.cache).to receive(:fetch).with(ApplicationSetting::CACHE_KEY).and_raise(ArgumentError)
-
- expect(described_class.current).to eq(:last)
- end
- end
-
- context 'when an ApplicationSetting is not yet present' do
- it 'does not cache nil object' do
- # when missing settings a nil object is returned, but not cached
- allow(described_class).to receive(:last).and_return(nil).twice
- expect(described_class.current).to be_nil
-
- # when the settings are set the method returns a valid object
- allow(described_class).to receive(:last).and_return(:last)
- expect(described_class.current).to eq(:last)
-
- # subsequent calls get everything from cache
- expect(described_class.current).to eq(:last)
- end
- end
- end
-
context 'restrict creating duplicates' do
before do
described_class.create_from_defaults
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 96173889ccd..77179028ede 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1546,7 +1546,7 @@ describe Ci::Build do
{ key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
- { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
+ { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true },
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 407e2fc598a..d2302583ac8 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -79,7 +79,7 @@ describe Clusters::Applications::Prometheus do
end
it 'creates proper url' do
- expect(subject.prometheus_client.url).to eq('http://example.com/api/v1/proxy/namespaces/gitlab-managed-apps/service/prometheus-prometheus-server:80')
+ expect(subject.prometheus_client.url).to eq('http://example.com/api/v1/namespaces/gitlab-managed-apps/service/prometheus-prometheus-server:80/proxy')
end
it 'copies options and headers from kube client to proxy client' do
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
new file mode 100644
index 00000000000..49e4b23ebc7
--- /dev/null
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -0,0 +1,153 @@
+require 'spec_helper'
+
+describe CacheableAttributes do
+ let(:minimal_test_class) do
+ Class.new do
+ include ActiveModel::Model
+ extend ActiveModel::Callbacks
+ define_model_callbacks :commit
+ include CacheableAttributes
+
+ def self.name
+ 'TestClass'
+ end
+
+ def self.first
+ @_first ||= new('foo' => 'a')
+ end
+
+ def self.last
+ @_last ||= new('foo' => 'a', 'bar' => 'b')
+ end
+
+ attr_accessor :attributes
+
+ def initialize(attrs = {})
+ @attributes = attrs
+ end
+ end
+ end
+
+ shared_context 'with defaults' do
+ before do
+ minimal_test_class.define_singleton_method(:defaults) do
+ { foo: 'a', bar: 'b', baz: 'c' }
+ end
+ end
+ end
+
+ describe '.current_without_cache' do
+ it 'defaults to last' do
+ expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.last)
+ end
+
+ it 'can be overriden' do
+ minimal_test_class.define_singleton_method(:current_without_cache) do
+ first
+ end
+
+ expect(minimal_test_class.current_without_cache).to eq(minimal_test_class.first)
+ end
+ end
+
+ describe '.cache_key' do
+ it 'excludes cache attributes' do
+ expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:json")
+ end
+ end
+
+ describe '.defaults' do
+ it 'defaults to {}' do
+ expect(minimal_test_class.defaults).to eq({})
+ end
+
+ context 'with defaults defined' do
+ include_context 'with defaults'
+
+ it 'can be overriden' do
+ expect(minimal_test_class.defaults).to eq({ foo: 'a', bar: 'b', baz: 'c' })
+ end
+ end
+ end
+
+ describe '.build_from_defaults' do
+ include_context 'with defaults'
+
+ context 'without any attributes given' do
+ it 'intializes a new object with the defaults' do
+ expect(minimal_test_class.build_from_defaults).not_to be_persisted
+ end
+ end
+
+ context 'without attributes given' do
+ it 'intializes a new object with the given attributes merged into the defaults' do
+ expect(minimal_test_class.build_from_defaults(foo: 'd').attributes[:foo]).to eq('d')
+ end
+ end
+ end
+
+ describe '.current', :use_clean_rails_memory_store_caching do
+ context 'redis unavailable' do
+ it 'returns an uncached record' do
+ allow(minimal_test_class).to receive(:last).and_return(:last)
+ expect(Rails.cache).to receive(:read).and_raise(Redis::BaseError)
+
+ expect(minimal_test_class.current).to eq(:last)
+ end
+ end
+
+ context 'when a record is not yet present' do
+ it 'does not cache nil object' do
+ # when missing settings a nil object is returned, but not cached
+ allow(minimal_test_class).to receive(:last).twice.and_return(nil)
+
+ expect(minimal_test_class.current).to be_nil
+ expect(Rails.cache.exist?(minimal_test_class.cache_key)).to be(false)
+ end
+
+ it 'cache non-nil object' do
+ # when the settings are set the method returns a valid object
+ allow(minimal_test_class).to receive(:last).and_call_original
+
+ expect(minimal_test_class.current).to eq(minimal_test_class.last)
+ expect(Rails.cache.exist?(minimal_test_class.cache_key)).to be(true)
+
+ # subsequent calls retrieve the record from the cache
+ last_record = minimal_test_class.last
+ expect(minimal_test_class).not_to receive(:last)
+ expect(minimal_test_class.current.attributes).to eq(last_record.attributes)
+ end
+ end
+ end
+
+ describe '.cached', :use_clean_rails_memory_store_caching do
+ context 'when cache is cold' do
+ it 'returns nil' do
+ expect(minimal_test_class.cached).to be_nil
+ end
+ end
+
+ context 'when cached settings do not include the latest defaults' do
+ before do
+ Rails.cache.write(minimal_test_class.cache_key, { bar: 'b', baz: 'c' }.to_json)
+ minimal_test_class.define_singleton_method(:defaults) do
+ { foo: 'a', bar: 'b', baz: 'c' }
+ end
+ end
+
+ it 'includes attributes from defaults' do
+ expect(minimal_test_class.cached.attributes[:foo]).to eq(minimal_test_class.defaults[:foo])
+ end
+ end
+ end
+
+ describe '#cache!', :use_clean_rails_memory_store_caching do
+ let(:appearance_record) { create(:appearance) }
+
+ it 'caches the attributes' do
+ appearance_record.cache!
+
+ expect(Rails.cache.read(Appearance.cache_key)).to eq(appearance_record.attributes.to_json)
+ end
+ end
+end
diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb
index 91591017587..fcb5250278e 100644
--- a/spec/models/concerns/resolvable_note_spec.rb
+++ b/spec/models/concerns/resolvable_note_spec.rb
@@ -326,4 +326,12 @@ describe Note, ResolvableNote do
end
end
end
+
+ describe "#potentially_resolvable?" do
+ it 'returns false if noteable could not be found' do
+ allow(subject).to receive(:noteable).and_return(nil)
+
+ expect(subject.potentially_resolvable?).to be_falsey
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 04379e7d2c3..92e33a64d26 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1245,38 +1245,50 @@ describe MergeRequest do
describe '#check_if_can_be_merged' do
let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) }
- subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
+ shared_examples 'checking if can be merged' do
+ context 'when it is not broken and has no conflicts' do
+ before do
+ allow(subject).to receive(:broken?) { false }
+ allow(project.repository).to receive(:can_be_merged?).and_return(true)
+ end
- context 'when it is not broken and has no conflicts' do
- before do
- allow(subject).to receive(:broken?) { false }
- allow(project.repository).to receive(:can_be_merged?).and_return(true)
+ it 'is marked as mergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
+ end
end
- it 'is marked as mergeable' do
- expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
- end
- end
+ context 'when broken' do
+ before do
+ allow(subject).to receive(:broken?) { true }
+ end
- context 'when broken' do
- before do
- allow(subject).to receive(:broken?) { true }
+ it 'becomes unmergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+ end
end
- it 'becomes unmergeable' do
- expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+ context 'when it has conflicts' do
+ before do
+ allow(subject).to receive(:broken?) { false }
+ allow(project.repository).to receive(:can_be_merged?).and_return(false)
+ end
+
+ it 'becomes unmergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+ end
end
end
- context 'when it has conflicts' do
- before do
- allow(subject).to receive(:broken?) { false }
- allow(project.repository).to receive(:can_be_merged?).and_return(false)
- end
+ context 'when merge_status is unchecked' do
+ subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
- it 'becomes unmergeable' do
- expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
- end
+ it_behaves_like 'checking if can be merged'
+ end
+
+ context 'when merge_status is unchecked' do
+ subject { create(:merge_request, source_project: project, merge_status: :cannot_be_merged_recheck) }
+
+ it_behaves_like 'checking if can be merged'
end
end
@@ -2064,6 +2076,53 @@ describe MergeRequest do
expect(subject.merge_jid).to be_nil
end
end
+
+ describe 'transition to cannot_be_merged' do
+ let(:notification_service) { double(:notification_service) }
+ let(:todo_service) { double(:todo_service) }
+
+ subject { create(:merge_request, merge_status: :unchecked) }
+
+ before do
+ allow(NotificationService).to receive(:new).and_return(notification_service)
+ allow(TodoService).to receive(:new).and_return(todo_service)
+ end
+
+ it 'notifies, but does not notify again if rechecking still results in cannot_be_merged' do
+ expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once
+ expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
+
+ subject.mark_as_unmergeable
+ subject.mark_as_unchecked
+ subject.mark_as_unmergeable
+ end
+
+ it 'notifies whenever merge request is newly unmergeable' do
+ expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice
+ expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice
+
+ subject.mark_as_unmergeable
+ subject.mark_as_unchecked
+ subject.mark_as_mergeable
+ subject.mark_as_unchecked
+ subject.mark_as_unmergeable
+ end
+ end
+
+ describe 'check_state?' do
+ it 'indicates whether MR is still checking for mergeability' do
+ state_machine = described_class.state_machines[:merge_status]
+ check_states = [:unchecked, :cannot_be_merged_recheck]
+
+ check_states.each do |merge_status|
+ expect(state_machine.check_state?(merge_status)).to be true
+ end
+
+ (state_machine.states.map(&:name) - check_states).each do |merge_status|
+ expect(state_machine.check_state?(merge_status)).to be false
+ end
+ end
+ end
end
describe '#should_be_rebased?' do
@@ -2195,4 +2254,39 @@ describe MergeRequest do
expect(merge_request.can_allow_maintainer_to_push?(user)).to be_truthy
end
end
+
+ describe '#merge_participants' do
+ it 'contains author' do
+ expect(subject.merge_participants).to eq([subject.author])
+ end
+
+ describe 'when merge_when_pipeline_succeeds? is true' do
+ describe 'when merge user is author' do
+ let(:user) { create(:user) }
+ subject do
+ create(:merge_request,
+ merge_when_pipeline_succeeds: true,
+ merge_user: user,
+ author: user)
+ end
+
+ it 'contains author only' do
+ expect(subject.merge_participants).to eq([subject.author])
+ end
+ end
+
+ describe 'when merge user and author are different users' do
+ let(:merge_user) { create(:user) }
+ subject do
+ create(:merge_request,
+ merge_when_pipeline_succeeds: true,
+ merge_user: merge_user)
+ end
+
+ it 'contains author and merge user' do
+ expect(subject.merge_participants).to eq([subject.author, merge_user])
+ end
+ end
+ end
+ end
end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index e3b37739e8e..d5fb4a7742c 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -70,6 +70,41 @@ describe MergeRequestPresenter do
end
end
+ describe "#unmergeable_reasons" do
+ let(:presenter) { described_class.new(resource, current_user: user) }
+
+ before do
+ # Mergeable base state
+ allow(resource).to receive(:has_no_commits?).and_return(false)
+ allow(resource).to receive(:source_branch_exists?).and_return(true)
+ allow(resource).to receive(:target_branch_exists?).and_return(true)
+ allow(resource.project.repository).to receive(:can_be_merged?).and_return(true)
+ end
+
+ it "handles mergeable request" do
+ expect(presenter.unmergeable_reasons).to eq([])
+ end
+
+ it "handles no commit" do
+ allow(resource).to receive(:has_no_commits?).and_return(true)
+
+ expect(presenter.unmergeable_reasons).to eq(["no commits"])
+ end
+
+ it "handles branches missing" do
+ allow(resource).to receive(:source_branch_exists?).and_return(false)
+ allow(resource).to receive(:target_branch_exists?).and_return(false)
+
+ expect(presenter.unmergeable_reasons).to eq(["source branch is missing", "target branch is missing"])
+ end
+
+ it "handles merge conflict" do
+ allow(resource.project.repository).to receive(:can_be_merged?).and_return(false)
+
+ expect(presenter.unmergeable_reasons).to eq(["has merge conflicts"])
+ end
+ end
+
describe '#conflict_resolution_path' do
let(:project) { create :project }
let(:user) { create :user }
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 7bbf34422b8..38b618191fb 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -18,7 +18,7 @@ describe API::Version do
expect(response).to have_gitlab_http_status(200)
expect(json_response['version']).to eq(Gitlab::VERSION)
- expect(json_response['revision']).to eq(Gitlab::REVISION)
+ expect(json_response['revision']).to eq(Gitlab.revision)
end
end
end
diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb
index 473dbcbb692..bca1e71bef2 100644
--- a/spec/services/clusters/applications/schedule_installation_service_spec.rb
+++ b/spec/services/clusters/applications/schedule_installation_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Clusters::Applications::ScheduleInstallationService do
def count_scheduled
- application_class&.with_status(:scheduled)&.count || 0
+ application&.class&.with_status(:scheduled)&.count || 0
end
shared_examples 'a failing service' do
@@ -10,45 +10,42 @@ describe Clusters::Applications::ScheduleInstallationService do
expect(ClusterInstallAppWorker).not_to receive(:perform_async)
count_before = count_scheduled
- expect { service.execute }.to raise_error(StandardError)
+ expect { service.execute(application) }.to raise_error(StandardError)
expect(count_scheduled).to eq(count_before)
end
end
describe '#execute' do
- let(:application_class) { Clusters::Applications::Helm }
- let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
- let(:project) { cluster.project }
- let(:service) { described_class.new(project, nil, cluster: cluster, application_class: application_class) }
+ let(:project) { double(:project) }
+ let(:service) { described_class.new(project, nil) }
- it 'creates a new application' do
- allow(ClusterInstallAppWorker).to receive(:perform_async)
+ context 'when application is installable' do
+ let(:application) { create(:clusters_applications_helm, :installable) }
- expect { service.execute }.to change { application_class.count }.by(1)
- end
-
- it 'make the application scheduled' do
- expect(ClusterInstallAppWorker).to receive(:perform_async).with(application_class.application_name, kind_of(Numeric)).once
+ it 'make the application scheduled' do
+ expect(ClusterInstallAppWorker).to receive(:perform_async).with(application.name, kind_of(Numeric)).once
- expect { service.execute }.to change { application_class.with_status(:scheduled).count }.by(1)
+ expect { service.execute(application) }.to change { application.class.with_status(:scheduled).count }.by(1)
+ end
end
context 'when installation is already in progress' do
let(:application) { create(:clusters_applications_helm, :installing) }
- let(:cluster) { application.cluster }
it_behaves_like 'a failing service'
end
- context 'when application_class is nil' do
- let(:application_class) { nil }
+ context 'when application is nil' do
+ let(:application) { nil }
it_behaves_like 'a failing service'
end
context 'when application cannot be persisted' do
+ let(:application) { create(:clusters_applications_helm) }
+
before do
- expect_any_instance_of(application_class).to receive(:make_scheduled!).once.and_raise(ActiveRecord::RecordInvalid)
+ expect(application).to receive(:make_scheduled!).once.and_raise(ActiveRecord::RecordInvalid)
end
it_behaves_like 'a failing service'
diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb
index 837b8a56d12..d57852615d9 100644
--- a/spec/services/merge_requests/conflicts/list_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/list_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe MergeRequests::Conflicts::ListService do
describe '#can_be_resolved_in_ui?' do
def create_merge_request(source_branch)
- create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start') do |mr|
+ create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 240aa638f79..8838742a637 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -112,32 +112,6 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
service.trigger(unrelated_pipeline)
end
end
-
- context 'when the merge request is not mergeable' do
- let(:mr_conflict) do
- create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
- source_branch: 'master', target_branch: 'feature-conflict',
- source_project: project, target_project: project)
- end
-
- let(:conflict_pipeline) do
- create(:ci_pipeline, project: project, ref: mr_conflict.source_branch,
- sha: mr_conflict.diff_head_sha, status: 'success',
- head_pipeline_of: mr_conflict)
- end
-
- it 'does not merge the merge request' do
- expect(MergeWorker).not_to receive(:perform_async)
-
- service.trigger(conflict_pipeline)
- end
-
- it 'creates todos for unmergeability' do
- expect_any_instance_of(TodoService).to receive(:merge_request_became_unmergeable).with(mr_conflict)
-
- service.trigger(conflict_pipeline)
- end
- end
end
describe "#cancel" do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 5f28bc123f3..831678b949d 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1224,6 +1224,32 @@ describe NotificationService, :mailer do
end
end
+ describe '#merge_request_unmergeable' do
+ it "sends email to merge request author" do
+ notification.merge_request_unmergeable(merge_request)
+
+ should_email(merge_request.author)
+ expect(email_recipients.size).to eq(1)
+ end
+
+ describe 'when merge_when_pipeline_succeeds is true' do
+ before do
+ merge_request.update_attributes(
+ merge_when_pipeline_succeeds: true,
+ merge_user: create(:user)
+ )
+ end
+
+ it "sends email to merge request author and merge_user" do
+ notification.merge_request_unmergeable(merge_request)
+
+ should_email(merge_request.author)
+ should_email(merge_request.merge_user)
+ expect(email_recipients.size).to eq(2)
+ end
+ end
+ end
+
describe '#closed_merge_request' do
before do
update_custom_notification(:close_merge_request, @u_guest_custom, resource: project)
diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb
index f964f9972cd..06b470849b3 100644
--- a/spec/services/projects/open_issues_count_service_spec.rb
+++ b/spec/services/projects/open_issues_count_service_spec.rb
@@ -2,20 +2,53 @@ require 'spec_helper'
describe Projects::OpenIssuesCountService do
describe '#count' do
- it 'returns the number of open issues' do
- project = create(:project)
- create(:issue, :opened, project: project)
+ let(:project) { create(:project) }
- expect(described_class.new(project).count).to eq(1)
+ context 'when user is nil' do
+ it 'does not include confidential issues in the issue count' do
+ create(:issue, :opened, project: project)
+ create(:issue, :opened, confidential: true, project: project)
+
+ expect(described_class.new(project).count).to eq(1)
+ end
end
- it 'does not include confidential issues in the issue count' do
- project = create(:project)
+ context 'when user is provided' do
+ let(:user) { create(:user) }
+
+ context 'when user can read confidential issues' do
+ before do
+ project.add_reporter(user)
+ end
+
+ it 'returns the right count with confidential issues' do
+ create(:issue, :opened, project: project)
+ create(:issue, :opened, confidential: true, project: project)
+
+ expect(described_class.new(project, user).count).to eq(2)
+ end
+
+ it 'uses total_open_issues_count cache key' do
+ expect(described_class.new(project, user).cache_key_name).to eq('total_open_issues_count')
+ end
+ end
+
+ context 'when user cannot read confidential issues' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'does not include confidential issues' do
+ create(:issue, :opened, project: project)
+ create(:issue, :opened, confidential: true, project: project)
- create(:issue, :opened, project: project)
- create(:issue, :opened, confidential: true, project: project)
+ expect(described_class.new(project, user).count).to eq(1)
+ end
- expect(described_class.new(project).count).to eq(1)
+ it 'uses public_open_issues_count cache key' do
+ expect(described_class.new(project, user).cache_key_name).to eq('public_open_issues_count')
+ end
+ end
end
end
end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 562b89e6767..9a51c873b30 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -721,17 +721,18 @@ describe TodoService do
end
describe '#merge_request_build_failed' do
- it 'creates a pending todo for the merge request author' do
- service.merge_request_build_failed(mr_unassigned)
+ let(:merge_participants) { [mr_unassigned.author, admin] }
- should_create_todo(user: author, target: mr_unassigned, action: Todo::BUILD_FAILED)
+ before do
+ allow(mr_unassigned).to receive(:merge_participants).and_return(merge_participants)
end
- it 'creates a pending todo for merge_user' do
- mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
+ it 'creates a pending todo for each merge_participant' do
service.merge_request_build_failed(mr_unassigned)
- should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::BUILD_FAILED)
+ merge_participants.each do |participant|
+ should_create_todo(user: participant, author: participant, target: mr_unassigned, action: Todo::BUILD_FAILED)
+ end
end
end
@@ -747,11 +748,19 @@ describe TodoService do
end
describe '#merge_request_became_unmergeable' do
- it 'creates a pending todo for a merge_user' do
+ let(:merge_participants) { [admin, create(:user)] }
+
+ before do
+ allow(mr_unassigned).to receive(:merge_participants).and_return(merge_participants)
+ end
+
+ it 'creates a pending todo for each merge_participant' do
mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_became_unmergeable(mr_unassigned)
- should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::UNMERGEABLE)
+ merge_participants.each do |participant|
+ should_create_todo(user: participant, author: participant, target: mr_unassigned, action: Todo::UNMERGEABLE)
+ end
end
end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 72e5c2d66dd..f7b71bf42e3 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -132,6 +132,14 @@ module LoginHelpers
env['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
end
+ def stub_omniauth_failure(strategy, message_key, exception = nil)
+ env = @request.env
+
+ env['omniauth.error'] = exception
+ env['omniauth.error.type'] = message_key.to_sym
+ env['omniauth.error.strategy'] = strategy
+ end
+
def stub_omniauth_saml_config(messages)
set_devise_mapping(context: Rails.application)
Rails.application.routes.disable_clear_and_finalize = true
diff --git a/spec/support/helpers/routes_helpers.rb b/spec/support/helpers/routes_helpers.rb
new file mode 100644
index 00000000000..c4129606418
--- /dev/null
+++ b/spec/support/helpers/routes_helpers.rb
@@ -0,0 +1,7 @@
+module RoutesHelpers
+ def fake_routes(&block)
+ @routes = @routes.dup
+ @routes.formatter.clear
+ ActionDispatch::Routing::Mapper.new(@routes).instance_exec(&block)
+ end
+end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index a2e5642a72c..c9252bebb2e 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -125,6 +125,16 @@ describe 'gitlab:app namespace rake task' do
expect(Dir.entries(File.join(project.repository.path, 'custom_hooks'))).to include("dummy.txt")
end
end
+
+ context 'specific backup tasks' do
+ let(:task_list) { %w(db repo uploads builds artifacts pages lfs registry) }
+
+ it 'prints a progress message to stdout' do
+ task_list.each do |task|
+ expect { run_rake_task("gitlab:backup:#{task}:create") }.to output(/Dumping /).to_stdout
+ end
+ end
+ end
end
context 'tar creation' do
diff --git a/spec/views/admin/dashboard/index.html.haml_spec.rb b/spec/views/admin/dashboard/index.html.haml_spec.rb
index 099baacf019..59c777ea338 100644
--- a/spec/views/admin/dashboard/index.html.haml_spec.rb
+++ b/spec/views/admin/dashboard/index.html.haml_spec.rb
@@ -22,6 +22,6 @@ describe 'admin/dashboard/index.html.haml' do
it "includes revision of GitLab" do
render
- expect(rendered).to have_content "#{Gitlab::VERSION} (#{Gitlab::REVISION})"
+ expect(rendered).to have_content "#{Gitlab::VERSION} (#{Gitlab.revision})"
end
end
diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb
index 0a78606171d..836d452304c 100644
--- a/spec/views/help/index.html.haml_spec.rb
+++ b/spec/views/help/index.html.haml_spec.rb
@@ -39,7 +39,7 @@ describe 'help/index' do
def stub_version(version, revision)
stub_const('Gitlab::VERSION', version)
- stub_const('Gitlab::REVISION', revision)
+ allow(Gitlab).to receive(:revision).and_return(revision)
end
def stub_helpers
diff --git a/spec/workers/update_merge_requests_worker_spec.rb b/spec/workers/update_merge_requests_worker_spec.rb
index 0fa19ac84bb..80137815d2b 100644
--- a/spec/workers/update_merge_requests_worker_spec.rb
+++ b/spec/workers/update_merge_requests_worker_spec.rb
@@ -18,8 +18,13 @@ describe UpdateMergeRequestsWorker do
end
it 'executes MergeRequests::RefreshService with expected values' do
- expect(MergeRequests::RefreshService).to receive(:new).with(project, user).and_call_original
- expect_any_instance_of(MergeRequests::RefreshService).to receive(:execute).with(oldrev, newrev, ref)
+ expect(MergeRequests::RefreshService).to receive(:new)
+ .with(project, user).and_wrap_original do |method, *args|
+ method.call(*args).tap do |refresh_service|
+ expect(refresh_service)
+ .to receive(:execute).with(oldrev, newrev, ref)
+ end
+ end
perform
end
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index a00c6e89a1d..589ebcf1414 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -77,7 +77,7 @@ test:
only:
- branches
-codequality:
+code_quality:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -86,9 +86,9 @@ codequality:
- docker:stable-dind
script:
- setup_docker
- - codeclimate
+ - code_quality
artifacts:
- paths: [codeclimate.json]
+ paths: [gl-code-quality-report.json]
performance:
stage: performance
@@ -136,7 +136,7 @@ dependency_scanning:
artifacts:
paths: [gl-dependency-scanning-report.json]
-sast:container:
+container_scanning:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -145,9 +145,9 @@ sast:container:
- docker:stable-dind
script:
- setup_docker
- - sast_container
+ - container_scanning
artifacts:
- paths: [gl-sast-container-report.json]
+ paths: [gl-container-scanning-report.json]
dast:
stage: dast
@@ -388,7 +388,7 @@ rollout 100%:
# Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- function sast_container() {
+ function container_scanning() {
if [[ -n "$CI_REGISTRY_USER" ]]; then
echo "Logging to GitLab Container Registry with CI credentials..."
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
@@ -406,10 +406,10 @@ rollout 100%:
retries=0
echo "Waiting for clair daemon to start"
while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
- ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
+ ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
}
- function codeclimate() {
+ function code_quality() {
docker run --env SOURCE_CODE="$PWD" \
--volume "$PWD":/code \
--volume /var/run/docker.sock:/var/run/docker.sock \
diff --git a/yarn.lock b/yarn.lock
index e0d46609f7d..bbc4672f84b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -54,9 +54,9 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.22.0":
- version "1.22.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.22.0.tgz#9f2daefebcda911cba8341313c8c464c8087fe44"
+"@gitlab-org/gitlab-svgs@^1.23.0":
+ version "1.23.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.23.0.tgz#42047aeedcc06bc12d417ed1efadad1749af9670"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"