diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 588f255eff8bb89c03b0e2ec76ab63a599b8b9c5..7fbfda4a5a872fe04eca9d779150d4da465e5b98 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -141,6 +141,7 @@ stages: # Trigger a package build on omnibus-gitlab repository build-package: + before_script: [] services: [] variables: SETUP_DB: "false" @@ -148,17 +149,7 @@ build-package: stage: build when: manual script: - # If no branch in omnibus is specified, trigger pipeline against master - - if [ -z "$OMNIBUS_BRANCH" ] ; then export OMNIBUS_BRANCH=master ;fi - - echo "token=${BUILD_TRIGGER_TOKEN}" > version_details - - echo "ref=${OMNIBUS_BRANCH}" >> version_details - - echo "variables[ALTERNATIVE_SOURCES]=true" >> version_details - - echo "variables[GITLAB_VERSION]=${CI_COMMIT_SHA}" >> version_details - # Collect version details of all components - - for f in *_VERSION; do echo "variables[$f]=$(cat $f)" >> version_details; done - # Trigger the API and pass values collected above as parameters to it - - cat version_details | tr '\n' '&' | curl -X POST https://gitlab.com/api/v4/projects/20699/trigger/pipeline --data-binary @- - - rm version_details + - scripts/trigger-build # Prepare and merge knapsack tests knapsack: @@ -417,9 +408,6 @@ rake gitlab:assets:compile: - webpack-report/ rake karma: - cache: - paths: - - vendor/ruby stage: test <<: *use-pg <<: *dedicated-runner diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 66e1e0e20b340d2095caa1e67191d4513a2b3e79..58af062e75e882b38bec39e05594d76da65c24f9 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -40,6 +40,7 @@ logs, and code as it's very hard to read otherwise.) #### Results of GitLab environment info
+Expand for output related to GitLab environment info
 
 (For installations with omnibus-gitlab package run and paste the output of:
@@ -54,6 +55,7 @@ logs, and code as it's very hard to read otherwise.)
 #### Results of GitLab application Check
 
 
+Expand for output related to the GitLab application check
 
 (For installations with omnibus-gitlab package run and paste the output of:
diff --git a/.rubocop.yml b/.rubocop.yml
index e53af97a92cbde17ce059e32b9a88de66bdf4923..4e1d456d8d145d30631610e92b2637bcc353bdbe 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -494,7 +494,13 @@ Style/TrailingBlankLines:
 
 # This cop checks for trailing comma in array and hash literals.
 Style/TrailingCommaInLiteral:
-  Enabled: false
+  Enabled: true
+  EnforcedStyleForMultiline: no_comma
+
+# This cop checks for trailing comma in argument lists.
+Style/TrailingCommaInArguments:
+  Enabled: true
+  EnforcedStyleForMultiline: no_comma
 
 # Checks for %W when interpolation is not needed.
 Style/UnneededCapitalW:
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 38b22afdf82dc522030625d21cd464db2e407fd8..7582f761bcb07b7d39b77b3b523b31a5609e184a 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -369,13 +369,6 @@ Style/SymbolProc:
 Style/TernaryParentheses:
   Enabled: false
 
-# Offense count: 53
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
-# SupportedStylesForMultiline: comma, consistent_comma, no_comma
-Style/TrailingCommaInArguments:
-  Enabled: false
-
 # Offense count: 18
 # Cop supports --auto-correct.
 # Configuration parameters: AllowNamedUnderscoreVariables.
diff --git a/Gemfile b/Gemfile
index 0cffee6db5623e89d430663f8c94b118edbefd1e..2be46e99c6f594888348f883c1c63ca8650c75b2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -145,12 +145,12 @@ gem 'acts-as-taggable-on', '~> 4.0'
 
 # Background jobs
 gem 'sidekiq', '~> 5.0'
-gem 'sidekiq-cron', '~> 0.4.4'
+gem 'sidekiq-cron', '~> 0.6.0'
 gem 'redis-namespace', '~> 1.5.2'
 gem 'sidekiq-limit_fetch', '~> 3.4'
 
 # Cron Parser
-gem 'rufus-scheduler', '~> 3.1.10'
+gem 'rufus-scheduler', '~> 3.4'
 
 # HTTP requests
 gem 'httparty', '~> 0.13.3'
diff --git a/Gemfile.lock b/Gemfile.lock
index c0c56aa960266d1fac9d264d77653e46d5087ba7..6c52f1d70d91b5adbebf2a1a9d87cc86524e4896 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -181,6 +181,8 @@ GEM
     equalizer (0.0.11)
     erubis (2.7.0)
     escape_utils (1.1.1)
+    et-orbi (1.0.3)
+      tzinfo
     eventmachine (1.0.8)
     excon (0.55.0)
     execjs (2.6.0)
@@ -697,7 +699,8 @@ GEM
     rubyntlm (0.5.2)
     rubypants (0.2.0)
     rubyzip (1.2.1)
-    rufus-scheduler (3.1.10)
+    rufus-scheduler (3.4.0)
+      et-orbi (~> 1.0)
     rugged (0.25.1.1)
     safe_yaml (1.0.4)
     sanitize (2.1.0)
@@ -734,9 +737,8 @@ GEM
       connection_pool (~> 2.2, >= 2.2.0)
       rack-protection (>= 1.5.0)
       redis (~> 3.3, >= 3.3.3)
-    sidekiq-cron (0.4.4)
-      redis-namespace (>= 1.5.2)
-      rufus-scheduler (>= 2.0.24)
+    sidekiq-cron (0.6.0)
+      rufus-scheduler (>= 3.3.0)
       sidekiq (>= 4.2.1)
     sidekiq-limit_fetch (3.4.0)
       sidekiq (>= 4)
@@ -1013,7 +1015,7 @@ DEPENDENCIES
   ruby-fogbugz (~> 0.2.1)
   ruby-prof (~> 0.16.2)
   ruby_parser (~> 3.8.4)
-  rufus-scheduler (~> 3.1.10)
+  rufus-scheduler (~> 3.4)
   rugged (~> 0.25.1.1)
   sanitize (~> 2.0)
   sass-rails (~> 5.0.6)
@@ -1025,7 +1027,7 @@ DEPENDENCIES
   sham_rack (~> 1.3.6)
   shoulda-matchers (~> 2.8.0)
   sidekiq (~> 5.0)
-  sidekiq-cron (~> 0.4.4)
+  sidekiq-cron (~> 0.6.0)
   sidekiq-limit_fetch (~> 3.4)
   simplecov (~> 0.14.0)
   slack-notifier (~> 1.5.1)
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index 07d67d49aa57e5d28b1854df32fb7f90af85f5c0..849da633c89946fab3adf62893994ae106a1f70d 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -1,17 +1,38 @@
 /* global Flash */
 export default class BlobViewer {
   constructor() {
+    BlobViewer.initAuxiliaryViewer();
+
+    this.initMainViewers();
+  }
+
+  static initAuxiliaryViewer() {
+    const auxiliaryViewer = document.querySelector('.blob-viewer[data-type="auxiliary"]');
+    if (!auxiliaryViewer) return;
+
+    BlobViewer.loadViewer(auxiliaryViewer);
+  }
+
+  initMainViewers() {
+    this.$fileHolder = $('.file-holder');
+    if (!this.$fileHolder.length) return;
+
     this.switcher = document.querySelector('.js-blob-viewer-switcher');
     this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
     this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
-    this.simpleViewer = document.querySelector('.blob-viewer[data-type="simple"]');
-    this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
-    this.$fileHolder = $('.file-holder');
 
-    let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type');
+    this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
+    this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
 
     this.initBindings();
 
+    this.switchToInitialViewer();
+  }
+
+  switchToInitialViewer() {
+    const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
+    let initialViewerName = initialViewer.getAttribute('data-type');
+
     if (this.switcher && location.hash.indexOf('#L') === 0) {
       initialViewerName = 'simple';
     }
@@ -61,40 +82,13 @@ export default class BlobViewer {
     $(this.copySourceBtn).tooltip('fixTitle');
   }
 
-  loadViewer(viewerParam) {
-    const viewer = viewerParam;
-    const url = viewer.getAttribute('data-url');
-
-    if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
-      return;
-    }
-
-    viewer.setAttribute('data-loading', 'true');
-
-    $.ajax({
-      url,
-      dataType: 'JSON',
-    })
-    .fail(() => new Flash('Error loading source view'))
-    .done((data) => {
-      viewer.innerHTML = data.html;
-      $(viewer).syntaxHighlight();
-
-      viewer.setAttribute('data-loaded', 'true');
-
-      this.$fileHolder.trigger('highlight:line');
-
-      this.toggleCopyButtonState();
-    });
-  }
-
   switchToViewer(name) {
-    const newViewer = document.querySelector(`.blob-viewer[data-type='${name}']`);
+    const newViewer = this.$fileHolder[0].querySelector(`.blob-viewer[data-type='${name}']`);
     if (this.activeViewer === newViewer) return;
 
     const oldButton = document.querySelector('.js-blob-viewer-switch-btn.active');
     const newButton = document.querySelector(`.js-blob-viewer-switch-btn[data-viewer='${name}']`);
-    const oldViewer = document.querySelector(`.blob-viewer:not([data-type='${name}'])`);
+    const oldViewer = this.$fileHolder[0].querySelector(`.blob-viewer:not([data-type='${name}'])`);
 
     if (oldButton) {
       oldButton.classList.remove('active');
@@ -115,6 +109,40 @@ export default class BlobViewer {
 
     this.toggleCopyButtonState();
 
-    this.loadViewer(newViewer);
+    BlobViewer.loadViewer(newViewer)
+    .then((viewer) => {
+      $(viewer).syntaxHighlight();
+
+      this.$fileHolder.trigger('highlight:line');
+
+      this.toggleCopyButtonState();
+    })
+    .catch(() => new Flash('Error loading viewer'));
+  }
+
+  static loadViewer(viewerParam) {
+    const viewer = viewerParam;
+    const url = viewer.getAttribute('data-url');
+
+    return new Promise((resolve, reject) => {
+      if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
+        resolve(viewer);
+        return;
+      }
+
+      viewer.setAttribute('data-loading', 'true');
+
+      $.ajax({
+        url,
+        dataType: 'JSON',
+      })
+      .fail(reject)
+      .done((data) => {
+        viewer.innerHTML = data.html;
+        viewer.setAttribute('data-loaded', 'true');
+
+        resolve(viewer);
+      });
+    });
   }
 }
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 239eeacf2d74842e94c3de7a5958ef26fccbf6db..0d23bdeeb99f73ecfc4935f8884d1a5980d8b3a0 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -35,7 +35,10 @@ gl.issueBoards.Board = Vue.extend({
     filter: {
       handler() {
         this.list.page = 1;
-        this.list.getIssues(true);
+        this.list.getIssues(true)
+          .catch(() => {
+            // TODO: handle request error
+          });
       },
       deep: true,
     },
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js
index 3fc68457961022e2cea40c5829e9126e5b677532..870e115bd1a0cc0864ead66ea4665c7bd8bcd4b4 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js
+++ b/app/assets/javascripts/boards/components/board_blank_state.js
@@ -70,7 +70,10 @@ export default {
 
             list.id = listObj.id;
             list.label.id = listObj.label.id;
-            list.getIssues();
+            list.getIssues()
+              .catch(() => {
+                // TODO: handle request error
+              });
           });
         })
         .catch(() => {
diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js
index b13386536bf9426daf8278b857807ecc6569e1d5..7ee2696e720cf14b236b62fed0db55e7f454a2cd 100644
--- a/app/assets/javascripts/boards/components/board_list.js
+++ b/app/assets/javascripts/boards/components/board_list.js
@@ -2,6 +2,7 @@
 import boardNewIssue from './board_new_issue';
 import boardCard from './board_card';
 import eventHub from '../eventhub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
 
 const Store = gl.issueBoards.BoardsStore;
 
@@ -44,6 +45,7 @@ export default {
   components: {
     boardCard,
     boardNewIssue,
+    loadingIcon,
   },
   methods: {
     listHeight() {
@@ -90,7 +92,10 @@ export default {
         if (this.scrollHeight() <= this.listHeight() &&
           this.list.issuesSize > this.list.issues.length) {
           this.list.page += 1;
-          this.list.getIssues(false);
+          this.list.getIssues(false)
+            .catch(() => {
+              // TODO: handle request error
+            });
         }
 
         if (this.scrollHeight() > Math.ceil(this.listHeight())) {
@@ -153,10 +158,7 @@ export default {
         class="board-list-loading text-center"
         aria-label="Loading issues"
         v-if="loading">
-        
+        
       
       
-          
+
+          
+
           
             Showing all issues
           
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 317cef9f2275028f5c29df75ad0f6d8a9645d07e..9bcea302da2a6f7e442c348aba042d5c99b87d62 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({
     },
     assigneeId() {
       return this.issue.assignee ? this.issue.assignee.id : 0;
+    },
+    milestoneTitle() {
+      return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
     }
   },
   watch: {
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index fdab317dc23a493b0f32ee5a84ae65c3d9480bbe..507f16f3f06053d335ebea8ada070e3ed212299a 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -2,6 +2,7 @@
 
 import Vue from 'vue';
 import queryData from '../../utils/query_data';
+import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
 
 require('./header');
 require('./list');
@@ -108,6 +109,8 @@ gl.issueBoards.IssuesModal = Vue.extend({
         if (!this.issuesCount) {
           this.issuesCount = data.size;
         }
+      }).catch(() => {
+        // TODO: handle request error
       });
     },
   },
@@ -135,6 +138,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
     'modal-list': gl.issueBoards.ModalList,
     'modal-footer': gl.issueBoards.ModalFooter,
     'empty-state': gl.issueBoards.ModalEmptyState,
+    loadingIcon,
   },
   template: `
     
- +
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index bd2f62bcc1aabaf5024151db1eb1a2b8b5eb8a28..90561d0f7a8d022fa15a34cb4714d13adb08d823 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -25,7 +25,9 @@ class List { } if (this.type !== 'blank' && this.id) { - this.getIssues(); + this.getIssues().catch(() => { + // TODO: handle request error + }); } } @@ -52,11 +54,17 @@ class List { gl.issueBoards.BoardsStore.state.lists.splice(index, 1); gl.issueBoards.BoardsStore.updateNewListDropdown(this.id); - gl.boardService.destroyList(this.id); + gl.boardService.destroyList(this.id) + .catch(() => { + // TODO: handle request error + }); } update () { - gl.boardService.updateList(this.id, this.position); + gl.boardService.updateList(this.id, this.position) + .catch(() => { + // TODO: handle request error + }); } nextPage () { @@ -146,11 +154,17 @@ class List { this.issues.splice(oldIndex, 1); this.issues.splice(newIndex, 0, issue); - gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid); + gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid) + .catch(() => { + // TODO: handle request error + }); } updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) { - gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid); + gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid) + .catch(() => { + // TODO: handle request error + }); } findIssue (id) { diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js index ad9c600b499c15e92550bf5c11b514822bd9393f..b8be0d8a3015eb5023d99511aa8d59efba44ef8e 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.js +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js @@ -6,6 +6,7 @@ import PipelineStore from '../../pipelines/stores/pipelines_store'; import eventHub from '../../pipelines/event_hub'; import EmptyState from '../../pipelines/components/empty_state.vue'; import ErrorState from '../../pipelines/components/error_state.vue'; +import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import '../../lib/utils/common_utils'; import '../../vue_shared/vue_resource_interceptor'; import Poll from '../../lib/utils/poll'; @@ -17,8 +18,6 @@ import Poll from '../../lib/utils/poll'; * We need a store to store the received environemnts. * We need a service to communicate with the server. * - * Necessary SVG in the table are provided as props. This should be refactored - * as soon as we have Webpack and can load them directly into JS files. */ export default Vue.component('pipelines-table', { @@ -27,6 +26,7 @@ export default Vue.component('pipelines-table', { 'pipelines-table-component': PipelinesTableComponent, 'error-state': ErrorState, 'empty-state': EmptyState, + loadingIcon, }, /** @@ -151,13 +151,12 @@ export default Vue.component('pipelines-table', { template: `
-
-