diff options
author | Clement Ho <clemmakesapps@gmail.com> | 2018-10-31 15:27:13 +0000 |
---|---|---|
committer | Clement Ho <clemmakesapps@gmail.com> | 2018-10-31 15:27:13 +0000 |
commit | 76f41a5de8a8281830fca4fba397486ac227b9a1 (patch) | |
tree | 6c1a44ef934c47027c8ea8823821c7c3ad0ced4e | |
parent | 829a2172982d017cc35fdfb9c5b00d99fdc6a52a (diff) | |
parent | 7a8fdd7287beb275394ef0854bf3df8abfe5a6b3 (diff) | |
download | gitlab-ce-76f41a5de8a8281830fca4fba397486ac227b9a1.tar.gz |
Merge branch 'master' into 'gl-ui-progress-bar'
# Conflicts:
# app/assets/javascripts/commons/gitlab_ui.js
43 files changed, 439 insertions, 297 deletions
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js index 7aded5b34b3..36e6a18b843 100644 --- a/app/assets/javascripts/commons/gitlab_ui.js +++ b/app/assets/javascripts/commons/gitlab_ui.js @@ -1,15 +1,11 @@ import Vue from 'vue'; import { GlPagination, - GlModal, GlLoadingIcon, - GlModalDirective, GlTooltipDirective, } from '@gitlab-org/gitlab-ui'; Vue.component('gl-pagination', GlPagination); -Vue.component('gl-ui-modal', GlModal); Vue.component('gl-loading-icon', GlLoadingIcon); -Vue.directive('gl-modal', GlModalDirective); Vue.directive('gl-tooltip', GlTooltipDirective); diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index ad6151e3bf6..0a368f6558c 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -43,7 +43,7 @@ export default { 'currentProjectId', 'errorMessage', ]), - ...mapGetters(['activeFile', 'hasChanges', 'someUncommitedChanges', 'isCommitModeActive']), + ...mapGetters(['activeFile', 'hasChanges', 'someUncommittedChanges', 'isCommitModeActive']), }, mounted() { window.onbeforeunload = e => this.onBeforeUnload(e); @@ -63,7 +63,7 @@ export default { onBeforeUnload(e = {}) { const returnValue = __('Are you sure you want to lose unsaved changes?'); - if (!this.someUncommitedChanges) return undefined; + if (!this.someUncommittedChanges) return undefined; Object.assign(e, { returnValue, diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index d4c430cd2f3..364ab9426e0 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -25,11 +25,11 @@ export default { }, computed: { ...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']), - ...mapGetters(['currentProject', 'someUncommitedChanges']), + ...mapGetters(['currentProject', 'someUncommittedChanges']), showSuccessMessage() { return ( this.currentActivityView === activityBarViews.edit && - (this.lastCommitMsg && !this.someUncommitedChanges) + (this.lastCommitMsg && !this.someUncommittedChanges) ); }, }, diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index d3b24c5b793..5e86876c1c1 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -27,10 +27,10 @@ export default { 'unusedSeal', ]), ...mapState('commit', ['commitMessage', 'submitCommitLoading']), - ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']), + ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']), ...mapGetters('commit', ['discardDraftButtonDisabled']), showStageUnstageArea() { - return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal); + return !!(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal); }, activeFileKey() { return this.activeFile ? this.activeFile.key : null; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 709748fb530..8ad85074d6b 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -63,7 +63,7 @@ export const isEditModeActive = state => state.currentActivityView === activityB export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit; export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review; -export const someUncommitedChanges = state => +export const someUncommittedChanges = state => !!(state.changedFiles.length || state.stagedFiles.length); export const getChangesInFolder = state => path => { diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue index 75cb6374ad5..f970a5ebb64 100644 --- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue +++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue @@ -1,8 +1,15 @@ <script> import _ from 'underscore'; import { s__, sprintf } from '~/locale'; +import { GlModal, GlModalDirective } from '@gitlab-org/gitlab-ui'; export default { + components: { + GlModal, + }, + directives: { + 'gl-modal': GlModalDirective, + }, props: { deleteWikiUrl: { type: String, @@ -54,7 +61,7 @@ export default { > {{ __('Delete') }} </button> - <gl-ui-modal + <gl-modal :title="title" :ok-title="s__('WikiPageConfirmDelete|Delete page')" :modal-id="modalId" @@ -81,6 +88,6 @@ export default { name="authenticity_token" /> </form> - </gl-ui-modal> + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index 43f0b6651b9..8950ae31627 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; import GfmAutoComplete from '~/gfm_auto_complete'; import { __, s__ } from '~/locale'; import Api from '~/api'; +import { GlModal } from '@gitlab-org/gitlab-ui'; import eventHub from './event_hub'; import EmojiMenuInModal from './emoji_menu_in_modal'; @@ -13,6 +14,7 @@ const emojiMenuClass = 'js-modal-status-emoji-menu'; export default { components: { Icon, + GlModal, }, props: { currentEmoji: { @@ -152,7 +154,7 @@ export default { </script> <template> - <gl-ui-modal + <gl-modal :title="s__('SetStatusModal|Set a status')" :modal-id="modalId" :ok-title="s__('SetStatusModal|Set status')" @@ -237,5 +239,5 @@ export default { </div> </div> </div> - </gl-ui-modal> + </gl-modal> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index fee41b239e8..bd1946f337e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -1,5 +1,6 @@ <script> /* eslint-disable vue/require-default-prop */ +import { sprintf, __ } from '~/locale'; import PipelineStage from '~/pipelines/components/stage.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; @@ -36,6 +37,10 @@ export default { type: String, required: false, }, + troubleshootingDocsPath: { + type: String, + required: true, + }, }, computed: { hasPipeline() { @@ -57,6 +62,12 @@ export default { hasCommitInfo() { return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0; }, + errorText() { + return sprintf(__('Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}'), { + linkStart: `<a href="${this.troubleshootingDocsPath}">`, + linkEnd: '</a>', + }); + }, }, }; </script> @@ -77,8 +88,10 @@ export default { name="status_failed_borderless" /> </div> - <div class="media-body"> - Could not connect to the CI server. Please check your settings and try again + <div + class="media-body" + v-html="errorText" + > </div> </template> <template v-else-if="hasPipeline"> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index a5c69d2bc7a..33064f82ef7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -301,6 +301,7 @@ export default { :has-ci="mr.hasCI" :source-branch="mr.sourceBranch" :source-branch-link="mr.sourceBranchLink" + :troubleshooting-docs-path="mr.troubleshootingDocsPath" /> <deployment v-for="deployment in mr.deployments" @@ -355,6 +356,7 @@ export default { :has-ci="mr.hasCI" :source-branch="mr.targetBranch" :source-branch-link="mr.targetBranch" + :troubleshooting-docs-path="mr.troubleshootingDocsPath" /> <deployment v-for="postMergeDeployment in mr.postMergeDeployments" diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index a0c008e7314..c17e9c9e4d6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -18,6 +18,8 @@ export default class MergeRequestStore { this.squash = data.squash; this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path; + this.troubleshootingDocsPath = + this.troubleshootingDocsPath || data.troubleshooting_docs_path; this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true; this.iid = data.iid; diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index efc2d88172e..5d1bbb077af 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -21,6 +21,7 @@ window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget')} window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}'; + window.gl.mrWidgetData.troubleshooting_docs_path = '#{help_page_path('user/project/merge_requests', anchor: 'troubleshooting')}'; #js-vue-mr-widget.mr-widget diff --git a/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml b/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml new file mode 100644 index 00000000000..103419c1185 --- /dev/null +++ b/changelogs/unreleased/41545-gitlab-merge-request-status-could-not-connect-to-the-ci-server-please-check-your-settings-and-try-again.yml @@ -0,0 +1,5 @@ +--- +title: Reword error message for internal CI unknown pipeline status +merge_request: 22474 +author: +type: changed diff --git a/changelogs/unreleased/53052-mg-fix-broken-ie11.yml b/changelogs/unreleased/53052-mg-fix-broken-ie11.yml new file mode 100644 index 00000000000..c616efffa6b --- /dev/null +++ b/changelogs/unreleased/53052-mg-fix-broken-ie11.yml @@ -0,0 +1,5 @@ +--- +title: Fix incompatibility with IE11 due to non-transpiled gitlab-ui components +merge_request: 22695 +author: +type: fixed diff --git a/changelogs/unreleased/gl-ui-modal.yml b/changelogs/unreleased/gl-ui-modal.yml new file mode 100644 index 00000000000..fbdb8260d24 --- /dev/null +++ b/changelogs/unreleased/gl-ui-modal.yml @@ -0,0 +1,5 @@ +--- +title: Remove gitlab-ui's modal from global +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/gt-fix-ide-typos-in-props.yml b/changelogs/unreleased/gt-fix-ide-typos-in-props.yml new file mode 100644 index 00000000000..a81b227c82f --- /dev/null +++ b/changelogs/unreleased/gt-fix-ide-typos-in-props.yml @@ -0,0 +1,5 @@ +--- +title: Fix IDE typos in props +merge_request: 22685 +author: George Tsiolis +type: other diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md index dada59ce242..b65fbc9d958 100644 --- a/doc/development/rolling_out_changes_using_feature_flags.md +++ b/doc/development/rolling_out_changes_using_feature_flags.md @@ -152,14 +152,31 @@ in RC1, followed by the feature flag being removed in RC2. This in turn means the feature will be stable by the time we publish a stable package around the 22nd of the month. -## Undefined feature flags default to "on" +## Implicit feature flags -By default, the [`Project#feature_available?`][project-fa], +The [`Project#feature_available?`][project-fa], [`Namespace#feature_available?`][namespace-fa] (EE), and -[`License.feature_available?`][license-fa] (EE) methods will check if the -specified feature is behind a feature flag. Unless the feature is explicitly -disabled or limited to a percentage of users, the feature flag check will -default to `true`. +[`License.feature_available?`][license-fa] (EE) methods all implicitly check for +a feature flag by the same name as the provided argument. + +For example if a feature is license-gated, there's no need to add an additional +explicit feature flag check since the flag will be checked as part of the +`License.feature_available?` call. Similarly, there's no need to "clean up" a +feature flag once the feature has reached general availability. + +You'd still want to use an explicit `Feature.enabled?` check if your new feature +isn't gated by a License or Plan. + +[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68 +[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85 +[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300 + +### Undefined feature flags default to "on" + +An important side-effect of the [implicit feature +flags][#implicit-feature-flags] mentioned above is that unless the feature is +explicitly disabled or limited to a percentage of users, the feature flag check +will default to `true`. As an example, if you were to ship the backend half of a feature behind a flag, you'd want to explicitly disable that flag until the frontend half is also ready @@ -171,7 +188,3 @@ to be shipped. You can do this via ChatOps: Note that you can do this at any time, even before the merge request using the flag has been merged! - -[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68 -[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85 -[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300 diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 2b9cce6539f..76f5495ff78 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -9,37 +9,39 @@ You can only restore a backup to **exactly the same version and type (CE/EE)** of GitLab on which it was created. The best way to migrate your repositories from one server to another is through backup restore. -## Backup +## Requirements -GitLab provides a simple command line interface to backup your whole installation, -and is flexible enough to fit your needs. +In order to be able to backup and restore, you need two essential tools +installed on your system. -### Requirements +### Rsync -* rsync +If you installed GitLab: -If you're using GitLab with the Omnibus package, you're all set. If you -installed GitLab from source, make sure you have rsync installed. +- Using the Omnibus package, you're all set. +- From source, make sure `rsync` is installed: -If you're using Ubuntu, you could run: + ```sh + # Debian/Ubuntu + sudo apt-get install rsync -``` -sudo apt-get install -y rsync -``` + # RHEL/CentOS + sudo yum install rsync + ``` -* tar +### Tar Backup and restore tasks use `tar` under the hood to create and extract archives. Ensure you have version 1.30 or above of `tar` available in your system. To check the version, run: -``` +```sh tar --version ``` -### Backup timestamp +## Backup timestamp ->**Note:** +NOTE: **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_2018_04_25` would become `1493107454_2018_04_25_10.6.4-ce`. @@ -54,30 +56,46 @@ available. 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 +## Creating a backup of the GitLab system + +GitLab provides a simple command line interface to backup your whole instance. +It backs up your: + +- Database +- Attachments +- Git repositories data +- CI/CD job output logs +- CI/CD job artifacts +- LFS objects +- Container Registry images +- GitLab Pages content + +CAUTION: **Warning:** +GitLab does not back up any configuration files, SSL certificates, or system files. +You are highly advised to [read about storing configuration files](#storing-configuration-files). Use this command if you've installed GitLab with the Omnibus package: -``` +```sh sudo gitlab-rake gitlab:backup:create ``` Use this if you've installed GitLab from source: -``` +```sh sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` If you are running GitLab within a Docker container, you can run the backup from the host: -``` +```sh docker exec -t <container name> gitlab-rake gitlab:backup:create ``` If you are using the gitlab-omnibus helm chart on a Kubernetes cluster, you can -run the backup task on the gitlab application pod using kubectl +run the backup task on the gitlab application pod using kubectl: -``` +```sh kubectl exec -it <gitlab-gitlab pod> gitlab-rake gitlab:backup:create ``` @@ -110,9 +128,50 @@ Deleting tmp directories...[DONE] Deleting old backups... [SKIPPING] ``` +## Storing configuration files + +A backup performed by the [raketask GitLab provides](#creating-a-backup-of-the-gitlab-system) +does **not** store your configuration files. The primary reason for this is that your +database contains encrypted information for two-factor authentication, the CI/CD +'secure variables', etc. Storing encrypted information along with its key in the +same place defeats the purpose of using encryption in the first place. + +CAUTION: **Warning:** +The secrets file is essential to preserve your database encryption key. + +At the very **minimum**, you must backup: + +For Omnibus: + +- `/etc/gitlab/gitlab-secrets.json` +- `/etc/gitlab/gitlab.rb` + +For installation from source: + +- `/home/git/gitlab/config/secrets.yml` +- `/home/git/gitlab/config/gitlab.yml` + +For [Docker installations](https://docs.gitlab.com/omnibus/docker/), you must +back up the volume where the configuration files are stored. If you have created +the GitLab container according to the documentation, it should be under +`/srv/gitlab/config`. + +You may also want to back up any TLS keys and certificates, and your +[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). + +If you use Omnibus GitLab, see some additional information +[to backup your configuration](https://docs.gitlab.com/omnibus/settings/backups.html). + +In the unlikely event that the secrets file is lost, see the +[troubleshooting section](#when-the-secrets-file-is-lost). + +## Backup options + +The command line tool GitLab provides to backup your instance can take more options. + ### Backup strategy option -> **Note:** Introduced as an option in GitLab 8.17. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8728) in GitLab 8.17. The default backup strategy is to essentially stream data from the respective data locations to the backup using the Linux command `tar` and `gzip`. This works @@ -129,8 +188,11 @@ so the problem doesn't compound, but it could be a considerable change for large installations. This is why the `copy` strategy is not the default in 8.17. To use the `copy` strategy instead of the default streaming strategy, specify -`STRATEGY=copy` in the Rake task command. For example, -`sudo gitlab-rake gitlab:backup:create STRATEGY=copy`. +`STRATEGY=copy` in the Rake task command. For example: + +```sh +sudo gitlab-rake gitlab:backup:create STRATEGY=copy +``` ### Excluding specific directories from the backup @@ -151,11 +213,15 @@ Use a comma to specify several options at the same time: All wikis will be backed up as part of the `repositories` group. Non-existent wikis will be skipped during a backup. -``` -# use this command if you've installed GitLab with the Omnibus package +For Omnibus GitLab packages: + +```sh sudo gitlab-rake gitlab:backup:create SKIP=db,uploads +``` + +For installations from source: -# if you've installed GitLab from source +```sh sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production ``` @@ -208,7 +274,7 @@ This example can be used for a bucket in Amsterdam (AMS3). 1. [Reconfigure GitLab] for the changes to take effect -CAUTION: **Warning:** +NOTE: **Note:** If you see `400 Bad Request` by using Digital Ocean Spaces, the cause may be the usage of backup encryption. Remove or comment the line that contains `gitlab_rails['backup_encryption']` since Digital Ocean Spaces @@ -370,33 +436,43 @@ backups will be copied to, and will be created if it does not exist. If the directory that you want to copy the tarballs to is the root of your mounted directory, just use `.` instead. -For omnibus packages: -```ruby -gitlab_rails['backup_upload_connection'] = { - :provider => 'Local', - :local_root => '/mnt/backups' -} +For Omnibus GitLab packages: -# The directory inside the mounted folder to copy backups to -# Use '.' to store them in the root directory -gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups' -``` +1. Edit `/etc/gitlab/gitlab.rb`: + + ```ruby + gitlab_rails['backup_upload_connection'] = { + :provider => 'Local', + :local_root => '/mnt/backups' + } + + # The directory inside the mounted folder to copy backups to + # Use '.' to store them in the root directory + gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups' + ``` + +1. [Reconfigure GitLab] for the changes to take effect. + +--- For installations from source: -```yaml - backup: - # snip - upload: - # Fog storage connection settings, see http://fog.io/storage/ . - connection: - provider: Local - local_root: '/mnt/backups' - # The directory inside the mounted folder to copy backups to - # Use '.' to store them in the root directory - remote_directory: 'gitlab_backups' -``` +1. Edit `home/git/gitlab/config/gitlab.yml`: + + ```yaml + backup: + upload: + # Fog storage connection settings, see http://fog.io/storage/ . + connection: + provider: Local + local_root: '/mnt/backups' + # The directory inside the mounted folder to copy backups to + # Use '.' to store them in the root directory + remote_directory: 'gitlab_backups' + ``` + +1. [Restart GitLab] for the changes to take effect. ### Backup archive permissions @@ -405,45 +481,56 @@ will have owner/group git:git and 0600 permissions by default. This is meant to avoid other system users reading GitLab's data. If you need the backup archives to have different permissions you can use the 'archive_permissions' setting. -``` -# In /etc/gitlab/gitlab.rb, for omnibus packages -gitlab_rails['backup_archive_permissions'] = 0644 # Makes the backup archives world-readable -``` +For Omnibus GitLab packages: -``` -# In gitlab.yml, for installations from source: - backup: - archive_permissions: 0644 # Makes the backup archives world-readable -``` +1. Edit `/etc/gitlab/gitlab.rb`: -### Storing configuration files + ```ruby + gitlab_rails['backup_archive_permissions'] = 0644 # Makes the backup archives world-readable + ``` + +1. [Reconfigure GitLab] for the changes to take effect. + +--- + +For installations from source: -Please be informed that a backup does not store your configuration -files. One reason for this is that your database contains encrypted -information for two-factor authentication. Storing encrypted -information along with its key in the same place defeats the purpose -of using encryption in the first place! +1. Edit `/home/git/gitlab/config/gitlab.yml`: -If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration). -If you have a cookbook installation there should be a copy of your configuration in Chef. -If you installed from source, please consider backing up your `config/secrets.yml` file, `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). + ```yaml + backup: + archive_permissions: 0644 # Makes the backup archives world-readable + ``` -At the very **minimum** you should backup `/etc/gitlab/gitlab.rb` and -`/etc/gitlab/gitlab-secrets.json` (Omnibus), or -`/home/git/gitlab/config/secrets.yml` (source) to preserve your database -encryption key. +1. [Restart GitLab] for the changes to take effect. ### Configuring cron to make daily backups ->**Note:** +NOTE: **Note:** The following cron jobs do not [backup your GitLab configuration files](#storing-configuration-files) or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). -**For Omnibus installations** +For Omnibus GitLab packages: + +1. Edit `/etc/gitlab/gitlab.rb`: + + ```ruby + ## Limit backup lifetime to 7 days - 604800 seconds + gitlab_rails['backup_keep_time'] = 604800 + ``` + +1. [Reconfigure GitLab] for the changes to take effect. + +Note that the `backup_keep_time` configuration option only manages local +files. GitLab does not automatically prune old files stored in a third-party +object storage (e.g., AWS S3) because the user may not have permission to list +and delete files. We recommend that you configure the appropriate retention +policy for your object storage. For example, you can configure [the S3 backup +policy as described here](http://stackoverflow.com/questions/37553070/gitlab-omnibus-delete-backup-from-amazon-s3). To schedule a cron job that backs up your repositories and GitLab metadata, use the root user: -``` +```sh sudo su - crontab -e ``` @@ -455,26 +542,24 @@ There, add the following line to schedule the backup for everyday at 2 AM: ``` You may also want to set a limited lifetime for backups to prevent regular -backups using all your disk space. To do this add the following lines to -`/etc/gitlab/gitlab.rb` and reconfigure: +backups using all your disk space. -``` -# limit backup lifetime to 7 days - 604800 seconds -gitlab_rails['backup_keep_time'] = 604800 -``` +--- -Note that the `backup_keep_time` configuration option only manages local -files. GitLab does not automatically prune old files stored in a third-party -object storage (e.g., AWS S3) because the user may not have permission to list -and delete files. We recommend that you configure the appropriate retention -policy for your object storage. For example, you can configure [the S3 backup -policy as described here](http://stackoverflow.com/questions/37553070/gitlab-omnibus-delete-backup-from-amazon-s3). +For installations from source: + +1. Edit `home/git/gitlab/config/gitlab.yml`: -**For installation from source** + ```yaml + backup: + ## Limit backup lifetime to 7 days - 604800 seconds + keep_time: 604800 + ``` -``` -cd /home/git/gitlab -sudo -u git -H editor config/gitlab.yml # Enable keep_time in the backup section to automatically delete old backups +1. [Restart GitLab] for the changes to take effect. + + +```sh sudo -u git crontab -e # Edit the crontab for the git user ``` @@ -711,5 +796,53 @@ Those objects have no influence on the database backup/restore but they give thi For more information see similar questions on postgresql issue tracker[here](http://www.postgresql.org/message-id/201110220712.30886.adrian.klaver@gmail.com) and [here](http://www.postgresql.org/message-id/2039.1177339749@sss.pgh.pa.us) as well as [stack overflow](http://stackoverflow.com/questions/4368789/error-must-be-owner-of-language-plpgsql). +### When the secrets file is lost + +If you have failed to [back up the secrets file](#storing-configuration-files), +then users with 2FA enabled will not be able to log into GitLab. In that case, +you need to [disable 2FA for everyone](../security/two_factor_authentication.md#disabling-2fa-for-everyone). + +In the case of CI/CD, if your project has secure variables set, you might experience +some weird behavior, like stuck jobs or 500 errors. In that case, you can try +deleting the `ci_variables` table from the database. + +CAUTION: **Warning:** +Use the following commands at your own risk, and make sure you've taken a +backup beforehand. + +1. Enter the Rails console: + + For Omnibus GitLab packages: + + ```sh + sudo gitlab-rails dbconsole + ``` + + For installations from source: + + ```sh + sudo -u git -H bundle exec rails dbconsole RAILS_ENV=production + ``` + +1. Check the `ci_variables` table: + + ```sql + SELECT * FROM public."ci_variables"; + ``` + + Those are the variables that you need to delete. + +1. Drop the table: + + ```sql + DELETE FROM ci_variables; + ``` + +1. You may need to reconfigure or restart GitLab for the changes to take + effect. + +You should now be able to visit your project, and the jobs will start +running again. + [reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure [restart GitLab]: ../administration/restart_gitlab.md#installations-from-source diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md index 35a9d7adb28..bd0155dc712 100644 --- a/doc/user/admin_area/settings/usage_statistics.md +++ b/doc/user/admin_area/settings/usage_statistics.md @@ -42,7 +42,11 @@ not send any project names, usernames, or any other specific data. The information from the usage ping is not anonymous, it is linked to the hostname of the instance. -You can view the exact JSON payload in the administration panel. +You can view the exact JSON payload in the administration panel. To view the payload: + +1. Go to the **Admin area** (spanner symbol on the top bar). +1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**. +1. Expand **Usage statistics** and click on the **Preview payload** button. ### Deactivate the usage ping diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 48004471f0a..762d254d6cc 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -113,6 +113,14 @@ To add an existing Kubernetes cluster to your project: After a couple of minutes, your cluster will be ready to go. You can now proceed to install some [pre-defined applications](#installing-applications). +To determine the: + +- API URL, run `kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'`. +- Token: + 1. List the secrets by running: `kubectl get secrets`. Note the name of the secret you need the token for. + 1. Get the token for the appropriate secret by running: `kubectl get secret <SECRET_NAME> -o jsonpath="{['data']['token']}" | base64 -D`. +- CA certificate, run `kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 -D`. + ## Security implications CAUTION: **Important:** diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index f9ebf277125..0a7f7d37384 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -236,6 +236,35 @@ all your changes will be available to preview by anyone with the Review Apps lin Find out about [bulk editing merge requests](../../project/bulk_editing.md). +## Troubleshooting + +Sometimes things don't go as expected in a merge request, here are some +troubleshooting steps. + +### Merge request cannot retrieve the pipeline status + +This can occur for one of two reasons: + +* Sidekiq doesn't pick up the changes fast enough +* Because of the bug described in [#41545](https://gitlab.com/gitlab-org/gitlab-ce/issues/41545) + +#### Sidekiq + +Sidekiq didn't process the CI state change fast enough. Please wait a few +seconds and the status will update automatically. + +#### Bug + +Merge Request pipeline statuses can't be retrieved when the following occurs: + +1. A Merge Requst is created +1. The Merge Request is closed +1. Changes are made in the project +1. The Merge Request is reopened + +To enable the pipeline status to be properly retrieved, close and reopen the +Merge Request again. + ## Tips Here are some tips that will help you be more efficient with merge requests in diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 405fc30a2ed..e37083165f5 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -8,6 +8,15 @@ module API helpers ::Gitlab::IssuableMetadata + # EE::API::Issues would override the following helpers + helpers do + params :issues_params_ee do + end + + params :issue_params_ee do + end + end + helpers do # rubocop: disable CodeReuse/ActiveRecord def find_issues(args = {}) @@ -46,9 +55,11 @@ module API desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' use :pagination + + use :issues_params_ee end - params :issue_params_ce do + params :issue_params do optional :description, type: String, desc: 'The description of an issue' optional :assignee_ids, type: Array[Integer], desc: 'The array of user IDs to assign issue' optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue' @@ -57,10 +68,8 @@ module API optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY' optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked" - end - params :issue_params do - use :issue_params_ce + use :issue_params_ee end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 18038d84d45..324e5315821 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1986,6 +1986,9 @@ msgstr "" msgid "Copy token to clipboard" msgstr "" +msgid "Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}" +msgstr "" + msgid "Create" msgstr "" diff --git a/package.json b/package.json index d418147b92b..608d4e58dd3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@babel/plugin-syntax-import-meta": "^7.0.0", "@babel/preset-env": "^7.1.0", "@gitlab-org/gitlab-svgs": "^1.33.0", - "@gitlab-org/gitlab-ui": "^1.9.0", + "@gitlab-org/gitlab-ui": "^1.10.0", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-loader": "^8.0.4", @@ -39,7 +39,6 @@ module QA module Factory autoload :ApiFabricator, 'qa/factory/api_fabricator' autoload :Base, 'qa/factory/base' - autoload :Product, 'qa/factory/product' module Resource autoload :Sandbox, 'qa/factory/resource/sandbox' diff --git a/qa/qa/factory/README.md b/qa/qa/factory/README.md index cfce096ab39..42077f60611 100644 --- a/qa/qa/factory/README.md +++ b/qa/qa/factory/README.md @@ -88,44 +88,6 @@ end The [`Project` factory](./resource/project.rb) is a good real example of Browser UI and API implementations. -### Define attributes - -After the resource is fabricated, we would like to access the attributes on -the resource. We define the attributes with `attribute` method. Suppose -we want to access the name on the resource, we could change `attr_accessor` -to `attribute`: - -```ruby -module QA - module Factory - module Resource - class Shirt < Factory::Base - attribute :name - - # ... same as before - end - end - end -end -``` - -The difference between `attr_accessor` and `attribute` is that by using -`attribute` it can also be accessed from the product: - -```ruby -shirt = - QA::Factory::Resource::Shirt.fabricate! do |resource| - resource.name = "GitLab QA" - end - -shirt.name # => "GitLab QA" -``` - -In the above example, if we use `attr_accessor :name` then `shirt.name` won't -be available. On the other hand, using `attribute :name` will allow you to use -`shirt.name`, so most of the time you'll want to use `attribute` instead of -`attr_accessor` unless we clearly don't need it for the product. - #### Resource attributes A resource may need another resource to exist first. For instance, a project @@ -145,7 +107,7 @@ module QA module Factory module Resource class Shirt < Factory::Base - attribute :name + attr_accessor :name attribute :project do Factory::Resource::Project.fabricate! do |resource| @@ -206,7 +168,7 @@ module QA module Factory module Resource class Shirt < Factory::Base - attribute :name + attr_accessor :name attribute :project do Factory::Resource::Project.fabricate! do |resource| @@ -287,7 +249,7 @@ module QA shirt_new.create_shirt! end - brand # Eagerly construct the data + populate(:brand) # Eagerly construct the data end end end @@ -295,9 +257,12 @@ module QA end ``` -This will make sure we construct the data right after we created the shirt. -The drawback for this will become we're forced to construct the data even -if we don't really need to use it. +The `populate` method will iterate through its arguments and call each +attribute respectively. Here `populate(:brand)` has the same effect as +just `brand`. Using the populate method makes the intention clearer. + +With this, it will make sure we construct the data right after we create the +shirt. The drawback is that this will always construct the data when the resource is fabricated even if we don't need to use the data. Alternatively, we could just make sure we're on the right page before constructing the brand data: @@ -307,7 +272,7 @@ module QA module Factory module Resource class Shirt < Factory::Base - attribute :name + attr_accessor :name attribute :project do Factory::Resource::Project.fabricate! do |resource| @@ -385,7 +350,7 @@ end **Notes on attributes precedence:** -- attributes from the factory have the highest precedence +- factory instance variables have the highest precedence - attributes from the API response take precedence over attributes from the block (usually from Browser UI) - attributes without a value will raise a `QA::Factory::Base::NoValueError` error diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb index e82e16f9415..e28a00c545b 100644 --- a/qa/qa/factory/base.rb +++ b/qa/qa/factory/base.rb @@ -22,12 +22,16 @@ module QA visit(web_url) end + def populate(*attributes) + attributes.each(&method(:public_send)) + end + private def populate_attribute(name, block) value = attribute_value(name, block) - raise NoValueError, "No value was computed for product #{name} of factory #{self.class.name}." unless value + raise NoValueError, "No value was computed for #{name} of #{self.class.name}." unless value value end @@ -84,7 +88,7 @@ module QA resource_web_url = yield factory.web_url = resource_web_url - Factory::Product.new(factory) + factory end private_class_method :do_fabricate! diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb deleted file mode 100644 index 34df0bda8e5..00000000000 --- a/qa/qa/factory/product.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'capybara/dsl' - -module QA - module Factory - class Product - include Capybara::DSL - - attr_reader :factory - - def initialize(factory) - @factory = factory - - define_attributes - end - - def visit! - visit(web_url) - end - - def populate(*attributes) - attributes.each(&method(:public_send)) - end - - private - - def define_attributes - factory.class.attributes_names.each do |name| - define_singleton_method(name) do - factory.public_send(name) - end - end - end - end - end -end diff --git a/qa/qa/factory/repository/project_push.rb b/qa/qa/factory/repository/project_push.rb index a9dfbc0a783..272b7fc5818 100644 --- a/qa/qa/factory/repository/project_push.rb +++ b/qa/qa/factory/repository/project_push.rb @@ -9,8 +9,6 @@ module QA end end - attribute :output - def initialize @file_name = 'file.txt' @file_content = '# This is test project' diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb index 0fac4377040..b1e874af893 100644 --- a/qa/qa/factory/resource/fork.rb +++ b/qa/qa/factory/resource/fork.rb @@ -50,8 +50,7 @@ module QA end def fabricate! - push - user + populate(:push, :user) visit_project_with_retry diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb index 92b8bdf4a21..4b7d2287f98 100644 --- a/qa/qa/factory/resource/merge_request.rb +++ b/qa/qa/factory/resource/merge_request.rb @@ -12,8 +12,6 @@ module QA :milestone, :labels - attribute :source_branch - attribute :project do Factory::Resource::Project.fabricate! do |resource| resource.name = 'project-with-merge-request' @@ -52,8 +50,8 @@ module QA end def fabricate! - target - source + populate(:target, :source) + project.visit! Page::Project::Show.perform(&:new_merge_request) Page::MergeRequest::New.perform do |page| diff --git a/qa/qa/factory/resource/merge_request_from_fork.rb b/qa/qa/factory/resource/merge_request_from_fork.rb index fbe062539b9..1311bf625a6 100644 --- a/qa/qa/factory/resource/merge_request_from_fork.rb +++ b/qa/qa/factory/resource/merge_request_from_fork.rb @@ -18,8 +18,10 @@ module QA end def fabricate! - push + populate(:push) + fork.visit! + Page::Project::Show.perform(&:new_merge_request) Page::MergeRequest::New.perform(&:create_merge_request) end diff --git a/qa/qa/factory/resource/project_imported_from_github.rb b/qa/qa/factory/resource/project_imported_from_github.rb index f62092ae122..ce20641e6cc 100644 --- a/qa/qa/factory/resource/project_imported_from_github.rb +++ b/qa/qa/factory/resource/project_imported_from_github.rb @@ -4,14 +4,13 @@ module QA module Factory module Resource class ProjectImportedFromGithub < Resource::Project + attr_accessor :name attr_writer :personal_access_token, :github_repository_path attribute :group do Factory::Resource::Group.fabricate! end - attribute :name - def fabricate! group.visit! diff --git a/qa/qa/factory/resource/project_milestone.rb b/qa/qa/factory/resource/project_milestone.rb index cfda58dc103..383f534c12c 100644 --- a/qa/qa/factory/resource/project_milestone.rb +++ b/qa/qa/factory/resource/project_milestone.rb @@ -2,14 +2,13 @@ module QA module Factory module Resource class ProjectMilestone < Factory::Base + attr_reader :title attr_accessor :description attribute :project do Factory::Resource::Project.fabricate! end - attribute :title - def title=(title) @title = "#{title}-#{SecureRandom.hex(4)}" @description = 'A milestone' diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb index 56bcda9e2f3..a125bac65dd 100644 --- a/qa/qa/factory/resource/sandbox.rb +++ b/qa/qa/factory/resource/sandbox.rb @@ -9,7 +9,6 @@ module QA attr_reader :path attribute :id - attribute :path def initialize @path = Runtime::Namespace.sandbox_name diff --git a/qa/qa/factory/resource/ssh_key.rb b/qa/qa/factory/resource/ssh_key.rb index a48a93fbe65..6f952eda36f 100644 --- a/qa/qa/factory/resource/ssh_key.rb +++ b/qa/qa/factory/resource/ssh_key.rb @@ -6,11 +6,9 @@ module QA class SSHKey < Factory::Base extend Forwardable - def_delegators :key, :private_key, :public_key, :fingerprint + attr_accessor :title - attribute :private_key - attribute :title - attribute :fingerprint + def_delegators :key, :private_key, :public_key, :fingerprint def key @key ||= Runtime::Key::RSA.new diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb index 6e6f46f7a95..e361face1f0 100644 --- a/qa/qa/factory/resource/user.rb +++ b/qa/qa/factory/resource/user.rb @@ -5,6 +5,7 @@ module QA module Resource class User < Factory::Base attr_reader :unique_id + attr_writer :username, :password def initialize @unique_id = SecureRandom.hex(8) @@ -30,11 +31,6 @@ module QA defined?(@username) && defined?(@password) end - attribute :name - attribute :username - attribute :email - attribute :password - def fabricate! # Don't try to log-out if we're not logged-in if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) } diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb index d7b92052894..e9584a27d63 100644 --- a/qa/spec/factory/base_spec.rb +++ b/qa/spec/factory/base_spec.rb @@ -4,8 +4,7 @@ describe QA::Factory::Base do include Support::StubENV let(:factory) { spy('factory') } - let(:product) { spy('product') } - let(:product_location) { 'http://product_location' } + let(:location) { 'http://location' } shared_context 'fabrication context' do subject do @@ -17,9 +16,8 @@ describe QA::Factory::Base do end before do - allow(subject).to receive(:current_url).and_return(product_location) + allow(subject).to receive(:current_url).and_return(location) allow(subject).to receive(:new).and_return(factory) - allow(QA::Factory::Product).to receive(:new).with(factory).and_return(product) end end @@ -28,7 +26,7 @@ describe QA::Factory::Base do it 'yields factory before calling factory method' do expect(factory).to receive(:something!).ordered - expect(factory).to receive(fabrication_method_used).ordered.and_return(product_location) + expect(factory).to receive(fabrication_method_used).ordered.and_return(location) subject.public_send(fabrication_method_called, factory: factory) do |factory| factory.something! @@ -37,7 +35,7 @@ describe QA::Factory::Base do it 'does not log the factory and build method when QA_DEBUG=false' do stub_env('QA_DEBUG', 'false') - expect(factory).to receive(fabrication_method_used).and_return(product_location) + expect(factory).to receive(fabrication_method_used).and_return(location) expect { subject.public_send(fabrication_method_called, 'something', factory: factory) } .not_to output.to_stdout @@ -71,17 +69,17 @@ describe QA::Factory::Base do it_behaves_like 'fabrication method', :fabricate_via_api! - it 'instantiates the factory, calls factory method returns fabrication product' do - expect(factory).to receive(:fabricate_via_api!).and_return(product_location) + it 'instantiates the factory, calls factory method returns the resource' do + expect(factory).to receive(:fabricate_via_api!).and_return(location) result = subject.fabricate_via_api!(factory: factory, parents: []) - expect(result).to eq(product) + expect(result).to eq(factory) end it 'logs the factory and build method when QA_DEBUG=true' do stub_env('QA_DEBUG', 'true') - expect(factory).to receive(:fabricate_via_api!).and_return(product_location) + expect(factory).to receive(:fabricate_via_api!).and_return(location) expect { subject.fabricate_via_api!(factory: factory, parents: []) } .to output(/==> Built a MyFactory via api with args \[\] in [\d\w\.\-]+/) @@ -100,10 +98,10 @@ describe QA::Factory::Base do expect(factory).to have_received(:fabricate!).with('something') end - it 'returns fabrication product' do + it 'returns fabrication resource' do result = subject.fabricate_via_browser_ui!('something', factory: factory, parents: []) - expect(result).to eq(product) + expect(result).to eq(factory) end it 'logs the factory and build method when QA_DEBUG=true' do @@ -140,44 +138,44 @@ describe QA::Factory::Base do describe '.attribute' do include_context 'simple factory' - it 'appends new product attribute' do + it 'appends new attribute' do expect(subject.attributes_names).to eq([:no_block, :test, :web_url]) end - context 'when the product attribute is populated via a block' do - it 'returns a fabrication product and defines factory attributes as its methods' do + context 'when the attribute is populated via a block' do + it 'returns value from the block' do result = subject.fabricate!(factory: factory) - expect(result).to be_a(QA::Factory::Product) + expect(result).to be_a(described_class) expect(result.test).to eq('block') end end - context 'when the product attribute is populated via the api' do + context 'when the attribute is populated via the api' do let(:api_resource) { { no_block: 'api' } } before do expect(factory).to receive(:api_resource).and_return(api_resource) end - it 'returns a fabrication product and defines factory attributes as its methods' do + it 'returns value from api' do result = subject.fabricate!(factory: factory) - expect(result).to be_a(QA::Factory::Product) + expect(result).to be_a(described_class) expect(result.no_block).to eq('api') end - context 'when the attribute also has a block in the factory' do + context 'when the attribute also has a block' do let(:api_resource) { { test: 'api_with_block' } } before do allow(QA::Runtime::Logger).to receive(:info) end - it 'returns the api value and emits an INFO log entry' do + it 'returns value from api and emits an INFO log entry' do result = subject.fabricate!(factory: factory) - expect(result).to be_a(QA::Factory::Product) + expect(result).to be_a(described_class) expect(result.test).to eq('api_with_block') expect(QA::Runtime::Logger) .to have_received(:info).with(/api_with_block/) @@ -185,15 +183,15 @@ describe QA::Factory::Base do end end - context 'when the product attribute is populated via a factory attribute' do + context 'when the attribute is populated via direct assignment' do before do factory.test = 'value' end - it 'returns a fabrication product and defines factory attributes as its methods' do + it 'returns value from the assignment' do result = subject.fabricate!(factory: factory) - expect(result).to be_a(QA::Factory::Product) + expect(result).to be_a(described_class) expect(result.test).to eq('value') end @@ -202,21 +200,21 @@ describe QA::Factory::Base do allow(factory).to receive(:api_resource).and_return({ test: 'api' }) end - it 'returns the factory attribute for the product' do + it 'returns value from the assignment' do result = subject.fabricate!(factory: factory) - expect(result).to be_a(QA::Factory::Product) + expect(result).to be_a(described_class) expect(result.test).to eq('value') end end end - context 'when the product attribute has no value' do + context 'when the attribute has no value' do it 'raises an error because no values could be found' do result = subject.fabricate!(factory: factory) expect { result.no_block } - .to raise_error(described_class::NoValueError, "No value was computed for product no_block of factory #{factory.class.name}.") + .to raise_error(described_class::NoValueError, "No value was computed for no_block of #{factory.class.name}.") end end end diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb deleted file mode 100644 index 5b6eaa13e9c..00000000000 --- a/qa/spec/factory/product_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -describe QA::Factory::Product do - let(:factory) do - Class.new(QA::Factory::Base) do - attribute :test do - 'block' - end - - attribute :no_block - end.new - end - - let(:product) { spy('product') } - let(:product_location) { 'http://product_location' } - - subject { described_class.new(factory) } - - before do - factory.web_url = product_location - end - - describe '.visit!' do - it 'makes it possible to visit fabrication product' do - allow_any_instance_of(described_class) - .to receive(:visit).and_return('visited some url') - - expect(subject.visit!).to eq 'visited some url' - end - end -end diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index d2003b61b2a..0c610edd6d1 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -179,7 +179,7 @@ describe 'Merge request > User sees merge widget', :js do # Wait for the `ci_status` and `merge_check` requests wait_for_requests - expect(page).to have_text('Could not connect to the CI server. Please check your settings and try again') + expect(page).to have_text(%r{Could not retrieve the pipeline status\. For troubleshooting steps, read the <a href=\".+\">documentation\.</a>}) end end diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index 45cccbee63e..41f447fba95 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -41,8 +41,8 @@ describe 'Merge request > User sees pipelines', :js do visit project_merge_request_path(project, merge_request) wait_for_requests - expect(page.find('.ci-widget')).to have_content( - 'Could not connect to the CI server. Please check your settings and try again') + expect(page.find('.ci-widget')).to have_text( + %r{Could not retrieve the pipeline status\. For troubleshooting steps, read the <a href=\".+\">documentation\.</a>}) end end diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js index ea8007d2029..689580e6b91 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -22,6 +22,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockData.pipeline, ciStatus: 'success', hasCi: true, + troubleshootingDocsPath: 'help', }); expect(vm.hasPipeline).toEqual(true); @@ -30,6 +31,7 @@ describe('MRWidgetPipeline', () => { it('should return false when there is no pipeline', () => { vm = mountComponent(Component, { pipeline: {}, + troubleshootingDocsPath: 'help', }); expect(vm.hasPipeline).toEqual(false); @@ -42,6 +44,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockData.pipeline, hasCi: true, ciStatus: 'success', + troubleshootingDocsPath: 'help', }); expect(vm.hasCIError).toEqual(false); @@ -52,6 +55,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockData.pipeline, hasCi: true, ciStatus: null, + troubleshootingDocsPath: 'help', }); expect(vm.hasCIError).toEqual(true); @@ -65,11 +69,12 @@ describe('MRWidgetPipeline', () => { pipeline: mockData.pipeline, hasCi: true, ciStatus: null, + troubleshootingDocsPath: 'help', }); expect( vm.$el.querySelector('.media-body').textContent.trim(), - ).toEqual('Could not connect to the CI server. Please check your settings and try again'); + ).toContain('Could not retrieve the pipeline status. For troubleshooting steps, read the <a href="help">documentation.</a>'); }); describe('with a pipeline', () => { @@ -78,6 +83,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockData.pipeline, hasCi: true, ciStatus: 'success', + troubleshootingDocsPath: 'help', }); }); @@ -122,6 +128,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockCopy.pipeline, hasCi: true, ciStatus: 'success', + troubleshootingDocsPath: 'help', }); }); @@ -162,6 +169,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockCopy.pipeline, hasCi: true, ciStatus: 'success', + troubleshootingDocsPath: 'help', }); expect( @@ -179,6 +187,7 @@ describe('MRWidgetPipeline', () => { pipeline: mockCopy.pipeline, hasCi: true, ciStatus: 'success', + troubleshootingDocsPath: 'help', }); expect(vm.$el.querySelector('.js-mini-pipeline-graph')).toEqual(null); diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index 7fd1a2350f7..c5e30a730cb 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -219,4 +219,5 @@ export default { only_allow_merge_if_pipeline_succeeds: false, commit_change_content_path: '/root/acets-app/merge_requests/22/commit_change_content', merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775', + troubleshooting_docs_path: 'help' }; diff --git a/yarn.lock b/yarn.lock index 79f1b757252..0124ee0572d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -626,10 +626,10 @@ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.33.0.tgz#068566e8ee00795f6f09f58236f08e1716f9f04a" integrity sha512-8ajtUHk6gQ1xosL/CO5IzHSFM/t18hx5pfzQ3cd0VuQXcyR6QKGuXTLwbYdmJDYOw1Etoo5DqDWxPEClHyZpiA== -"@gitlab-org/gitlab-ui@^1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.9.0.tgz#c47851587316f60926e8304747d1fcdd1222c779" - integrity sha512-OQ/mhWnbeG4pmjnCGwLsyvmHDYdLh2IRnt4Jx6G9jf96oyjEHzY1rveImfqcQ2bvx9azfuI6CU9dmDSY3aWvvQ== +"@gitlab-org/gitlab-ui@^1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.10.0.tgz#3ac54ecaa25ea558324f0b382c97fcf9e3c4f0a5" + integrity sha512-kfoCKA+AmWZ3hf1wOS8W9mPJs/7lF+a01PK//+sw2MOLv6PlduJJmdN8drFuJ65o6cTJ1f9FMVB80R6D71XVKQ== dependencies: "@gitlab-org/gitlab-svgs" "^1.23.0" bootstrap-vue "^2.0.0-rc.11" |