Commit cf76a6c1 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into ee-38869-groups-select

* master: (139 commits)
  btn-small -> btn-sm
  Merge branch 'fix-application-setting-nil-cache' into 'master'
  no CI replication
  [CE->EE] Resove frontent conflicts
  [CE->EE] Fix wrong resolvation of specs
  [CE->EE] Resolve group routes
  [CE->EE][ci skip] Resolve conflicts
  Resolve conflict in lib/backup/manager.rb
  Resolve conflict in .gitlab-ci.yml
  [CE->EE][ci skip] Resolve conflicts
  [CE->EE][ci skip] resolve frontend conflicts
  Remove `<script>` and `<template>` from CHANGELOG.md
  Simple docs fixes
  Fix button type
  Changes after review
  Removes group_avatar & group_label_subscription from global namespace
  Fix CSS in load more participants
  Fix the description of the new branch created by an issue's new branch button
  Fix a small typo in the delete merged branches documentation
  Moves vue resource docs into a new file. Adds docs regarding handling 204 response
  ...
parents 465f95a8 142ecc58
...@@ -52,11 +52,11 @@ stages: ...@@ -52,11 +52,11 @@ stages:
- gitlab-org - gitlab-org
.tests-metadata-state: &tests-metadata-state .tests-metadata-state: &tests-metadata-state
services: [] <<: *dedicated-runner
variables: variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache" TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache"
before_script:
- source scripts/utils.sh
artifacts: artifacts:
expire_in: 31d expire_in: 31d
paths: paths:
...@@ -92,6 +92,7 @@ stages: ...@@ -92,6 +92,7 @@ stages:
.rspec-metadata: &rspec-metadata .rspec-metadata: &rspec-metadata
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache <<: *pull-cache
<<: *except-docs
stage: test stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
...@@ -121,7 +122,6 @@ stages: ...@@ -121,7 +122,6 @@ stages:
.rspec-metadata-pg: &rspec-metadata-pg .rspec-metadata-pg: &rspec-metadata-pg
<<: *rspec-metadata <<: *rspec-metadata
<<: *use-pg <<: *use-pg
<<: *except-docs
.rspec-geo-pg-9-6: &rspec-metadata-pg-geo .rspec-geo-pg-9-6: &rspec-metadata-pg-geo
<<: *use-pg-9-6 <<: *use-pg-9-6
...@@ -137,11 +137,11 @@ stages: ...@@ -137,11 +137,11 @@ stages:
.rspec-metadata-mysql: &rspec-metadata-mysql .rspec-metadata-mysql: &rspec-metadata-mysql
<<: *rspec-metadata <<: *rspec-metadata
<<: *use-mysql <<: *use-mysql
<<: *except-docs
.spinach-metadata: &spinach-metadata .spinach-metadata: &spinach-metadata
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache <<: *pull-cache
<<: *except-docs
stage: test stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
...@@ -164,12 +164,10 @@ stages: ...@@ -164,12 +164,10 @@ stages:
.spinach-metadata-pg: &spinach-metadata-pg .spinach-metadata-pg: &spinach-metadata-pg
<<: *spinach-metadata <<: *spinach-metadata
<<: *use-pg <<: *use-pg
<<: *except-docs
.spinach-metadata-mysql: &spinach-metadata-mysql .spinach-metadata-mysql: &spinach-metadata-mysql
<<: *spinach-metadata <<: *spinach-metadata
<<: *use-mysql <<: *use-mysql
<<: *except-docs
.only-canonical-masters: &only-canonical-masters .only-canonical-masters: &only-canonical-masters
only: only:
...@@ -180,13 +178,8 @@ stages: ...@@ -180,13 +178,8 @@ stages:
# Trigger a package build in omnibus-gitlab repository # Trigger a package build in omnibus-gitlab repository
build-package: build-package:
image: ruby:2.3-alpine image: ruby:2.4-alpine
before_script: [] before_script: []
services: []
variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
EE_PACKAGE: "true"
stage: build stage: build
cache: {} cache: {}
when: manual when: manual
...@@ -207,13 +200,9 @@ build-package: ...@@ -207,13 +200,9 @@ build-package:
- apk add --update openssl - apk add --update openssl
- wget https://gitlab.com/gitlab-org/gitlab-ce/raw/master/scripts/trigger-build-docs - wget https://gitlab.com/gitlab-org/gitlab-ce/raw/master/scripts/trigger-build-docs
- chmod 755 trigger-build-docs - chmod 755 trigger-build-docs
services: []
cache: {} cache: {}
dependencies: [] dependencies: []
artifacts: {}
variables: variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
GIT_STRATEGY: none GIT_STRATEGY: none
when: manual when: manual
only: only:
...@@ -246,7 +235,6 @@ review-docs-cleanup: ...@@ -246,7 +235,6 @@ review-docs-cleanup:
# Retrieve knapsack and rspec_flaky reports # Retrieve knapsack and rspec_flaky reports
retrieve-tests-metadata: retrieve-tests-metadata:
<<: *tests-metadata-state <<: *tests-metadata-state
<<: *dedicated-runner
<<: *except-docs <<: *except-docs
stage: prepare stage: prepare
cache: cache:
...@@ -264,7 +252,6 @@ retrieve-tests-metadata: ...@@ -264,7 +252,6 @@ retrieve-tests-metadata:
update-tests-metadata: update-tests-metadata:
<<: *tests-metadata-state <<: *tests-metadata-state
<<: *dedicated-runner
<<: *only-canonical-masters <<: *only-canonical-masters
stage: post-test stage: post-test
cache: cache:
...@@ -329,71 +316,71 @@ setup-test-env: ...@@ -329,71 +316,71 @@ setup-test-env:
- public/assets - public/assets
- tmp/tests - tmp/tests
rspec-pg 0 25: *rspec-metadata-pg
rspec-pg 1 25: *rspec-metadata-pg
rspec-pg 2 25: *rspec-metadata-pg
rspec-pg 3 25: *rspec-metadata-pg
rspec-pg 4 25: *rspec-metadata-pg
rspec-pg 5 25: *rspec-metadata-pg
rspec-pg 6 25: *rspec-metadata-pg
rspec-pg 7 25: *rspec-metadata-pg
rspec-pg 8 25: *rspec-metadata-pg
rspec-pg 9 25: *rspec-metadata-pg
rspec-pg 10 25: *rspec-metadata-pg
rspec-pg 11 25: *rspec-metadata-pg
rspec-pg 12 25: *rspec-metadata-pg
rspec-pg 13 25: *rspec-metadata-pg
rspec-pg 14 25: *rspec-metadata-pg
rspec-pg 15 25: *rspec-metadata-pg
rspec-pg 16 25: *rspec-metadata-pg
rspec-pg 17 25: *rspec-metadata-pg
rspec-pg 18 25: *rspec-metadata-pg
rspec-pg 19 25: *rspec-metadata-pg
rspec-pg 20 25: *rspec-metadata-pg
rspec-pg 21 25: *rspec-metadata-pg
rspec-pg 22 25: *rspec-metadata-pg
rspec-pg 23 25: *rspec-metadata-pg
rspec-pg 24 25: *rspec-metadata-pg
rspec-pg geo: *rspec-metadata-pg-geo rspec-pg geo: *rspec-metadata-pg-geo
rspec-mysql 0 25: *rspec-metadata-mysql rspec-pg 0 26: *rspec-metadata-pg
rspec-mysql 1 25: *rspec-metadata-mysql rspec-pg 1 26: *rspec-metadata-pg
rspec-mysql 2 25: *rspec-metadata-mysql rspec-pg 2 26: *rspec-metadata-pg
rspec-mysql 3 25: *rspec-metadata-mysql rspec-pg 3 26: *rspec-metadata-pg
rspec-mysql 4 25: *rspec-metadata-mysql rspec-pg 4 26: *rspec-metadata-pg
rspec-mysql 5 25: *rspec-metadata-mysql rspec-pg 5 26: *rspec-metadata-pg
rspec-mysql 6 25: *rspec-metadata-mysql rspec-pg 6 26: *rspec-metadata-pg
rspec-mysql 7 25: *rspec-metadata-mysql rspec-pg 7 26: *rspec-metadata-pg
rspec-mysql 8 25: *rspec-metadata-mysql rspec-pg 8 26: *rspec-metadata-pg
rspec-mysql 9 25: *rspec-metadata-mysql rspec-pg 9 26: *rspec-metadata-pg
rspec-mysql 10 25: *rspec-metadata-mysql rspec-pg 10 26: *rspec-metadata-pg
rspec-mysql 11 25: *rspec-metadata-mysql rspec-pg 11 26: *rspec-metadata-pg
rspec-mysql 12 25: *rspec-metadata-mysql rspec-pg 12 26: *rspec-metadata-pg
rspec-mysql 13 25: *rspec-metadata-mysql rspec-pg 13 26: *rspec-metadata-pg
rspec-mysql 14 25: *rspec-metadata-mysql rspec-pg 14 26: *rspec-metadata-pg
rspec-mysql 15 25: *rspec-metadata-mysql rspec-pg 15 26: *rspec-metadata-pg
rspec-mysql 16 25: *rspec-metadata-mysql rspec-pg 16 26: *rspec-metadata-pg
rspec-mysql 17 25: *rspec-metadata-mysql rspec-pg 17 26: *rspec-metadata-pg
rspec-mysql 18 25: *rspec-metadata-mysql rspec-pg 18 26: *rspec-metadata-pg
rspec-mysql 19 25: *rspec-metadata-mysql rspec-pg 19 26: *rspec-metadata-pg
rspec-mysql 20 25: *rspec-metadata-mysql rspec-pg 20 26: *rspec-metadata-pg
rspec-mysql 21 25: *rspec-metadata-mysql rspec-pg 21 26: *rspec-metadata-pg
rspec-mysql 22 25: *rspec-metadata-mysql rspec-pg 22 26: *rspec-metadata-pg
rspec-mysql 23 25: *rspec-metadata-mysql rspec-pg 23 26: *rspec-metadata-pg
rspec-mysql 24 25: *rspec-metadata-mysql rspec-pg 24 26: *rspec-metadata-pg
rspec-pg 25 26: *rspec-metadata-pg
spinach-pg 0 5: *spinach-metadata-pg
spinach-pg 1 5: *spinach-metadata-pg rspec-mysql 0 26: *rspec-metadata-mysql
spinach-pg 2 5: *spinach-metadata-pg rspec-mysql 1 26: *rspec-metadata-mysql
spinach-pg 3 5: *spinach-metadata-pg rspec-mysql 2 26: *rspec-metadata-mysql
spinach-pg 4 5: *spinach-metadata-pg rspec-mysql 3 26: *rspec-metadata-mysql
rspec-mysql 4 26: *rspec-metadata-mysql
spinach-mysql 0 5: *spinach-metadata-mysql rspec-mysql 5 26: *rspec-metadata-mysql
spinach-mysql 1 5: *spinach-metadata-mysql rspec-mysql 6 26: *rspec-metadata-mysql
spinach-mysql 2 5: *spinach-metadata-mysql rspec-mysql 7 26: *rspec-metadata-mysql
spinach-mysql 3 5: *spinach-metadata-mysql rspec-mysql 8 26: *rspec-metadata-mysql
spinach-mysql 4 5: *spinach-metadata-mysql rspec-mysql 9 26: *rspec-metadata-mysql
rspec-mysql 10 26: *rspec-metadata-mysql
rspec-mysql 11 26: *rspec-metadata-mysql
rspec-mysql 12 26: *rspec-metadata-mysql
rspec-mysql 13 26: *rspec-metadata-mysql
rspec-mysql 14 26: *rspec-metadata-mysql
rspec-mysql 15 26: *rspec-metadata-mysql
rspec-mysql 16 26: *rspec-metadata-mysql
rspec-mysql 17 26: *rspec-metadata-mysql
rspec-mysql 18 26: *rspec-metadata-mysql
rspec-mysql 19 26: *rspec-metadata-mysql
rspec-mysql 20 26: *rspec-metadata-mysql
rspec-mysql 21 26: *rspec-metadata-mysql
rspec-mysql 22 26: *rspec-metadata-mysql
rspec-mysql 23 26: *rspec-metadata-mysql
rspec-mysql 24 26: *rspec-metadata-mysql
rspec-mysql 25 26: *rspec-metadata-mysql
spinach-pg 0 4: *spinach-metadata-pg
spinach-pg 1 4: *spinach-metadata-pg
spinach-pg 2 4: *spinach-metadata-pg
spinach-pg 3 4: *spinach-metadata-pg
spinach-mysql 0 4: *spinach-metadata-mysql
spinach-mysql 1 4: *spinach-metadata-mysql
spinach-mysql 2 4: *spinach-metadata-mysql
spinach-mysql 3 4: *spinach-metadata-mysql
# Static analysis jobs # Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
......
...@@ -626,7 +626,7 @@ Style/PredicateName: ...@@ -626,7 +626,7 @@ Style/PredicateName:
# branches, and conditions. # branches, and conditions.
Metrics/AbcSize: Metrics/AbcSize:
Enabled: true Enabled: true
Max: 55.25 Max: 54.28
# This cop checks if the length of a block exceeds some maximum value. # This cop checks if the length of a block exceeds some maximum value.
Metrics/BlockLength: Metrics/BlockLength:
...@@ -667,7 +667,7 @@ Metrics/ParameterLists: ...@@ -667,7 +667,7 @@ Metrics/ParameterLists:
# A complexity metric geared towards measuring complexity for a human reader. # A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Enabled: true Enabled: true
Max: 15 Max: 14
# Lint ######################################################################## # Lint ########################################################################
......
0.47.0 0.49.0
\ No newline at end of file
...@@ -105,7 +105,7 @@ gem 'fog-google', '~> 0.5' ...@@ -105,7 +105,7 @@ gem 'fog-google', '~> 0.5'
gem 'fog-local', '~> 0.3' gem 'fog-local', '~> 0.3'
gem 'fog-openstack', '~> 0.1' gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1' gem 'fog-rackspace', '~> 0.1.1'
gem 'fog-aliyun', '~> 0.1.0' gem 'fog-aliyun', '~> 0.2.0'
# for Google storage # for Google storage
gem 'google-api-client', '~> 0.13.6' gem 'google-api-client', '~> 0.13.6'
...@@ -414,7 +414,7 @@ group :ed25519 do ...@@ -414,7 +414,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.42.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.45.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -238,7 +238,7 @@ GEM ...@@ -238,7 +238,7 @@ GEM
flowdock (0.7.1) flowdock (0.7.1)
httparty (~> 0.7) httparty (~> 0.7)
multi_json multi_json
fog-aliyun (0.1.0) fog-aliyun (0.2.0)
fog-core (~> 1.27) fog-core (~> 1.27)
fog-json (~> 1.0) fog-json (~> 1.0)
ipaddress (~> 0.8) ipaddress (~> 0.8)
...@@ -297,7 +297,7 @@ GEM ...@@ -297,7 +297,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.42.0) gitaly-proto (0.45.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1050,7 +1050,7 @@ DEPENDENCIES ...@@ -1050,7 +1050,7 @@ DEPENDENCIES
flay (~> 2.8.0) flay (~> 2.8.0)
flipper (~> 0.10.2) flipper (~> 0.10.2)
flipper-active_record (~> 0.10.2) flipper-active_record (~> 0.10.2)
fog-aliyun (~> 0.1.0) fog-aliyun (~> 0.2.0)
fog-aws (~> 1.4) fog-aws (~> 1.4)
fog-core (~> 1.44) fog-core (~> 1.44)
fog-google (~> 0.5) fog-google (~> 0.5)
...@@ -1065,7 +1065,7 @@ DEPENDENCIES ...@@ -1065,7 +1065,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.42.0) gitaly-proto (~> 0.45.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
......
# GitLab Maintenance Policy # GitLab Maintenance Policy
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: See [doc/policy/maintenance.md](doc/policy/maintenance.md)
`(Major).(Minor).(Patch)` in a [pragmatic way].
- **Major version**: Whenever there is something significant or any backwards
incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced
to the public API or a minor feature is introduced, or when a set of smaller
features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix
incorrect behavior.
The current stable release will receive security patches and bug fixes
(eg. `8.9.0` -> `8.9.1`). Feature releases will mark the next supported stable
release where the minor version is increased numerically by increments of one
(eg. `8.9 -> 8.10`).
Our current policy is to support one stable release at any given time, but for
medium-level security issues, we may consider [backporting to the previous two
monthly releases][rel-sec].
We encourage everyone to run the latest stable release to ensure that you can
easily upgrade to the most secure and feature-rich GitLab experience. In order
to make sure you can easily run the most recent stable release, we are working
hard to keep the update process simple and reliable.
More information about the release procedures can be found in our
[release-tools documentation][rel]. You may also want to read our
[Responsible Disclosure Policy][disclosure].
[rel-sec]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/security.md#backporting
[rel]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/
[disclosure]: https://about.gitlab.com/disclosure/
[pragmatic way]: https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e
10.1.0-pre 10.2.0-pre
/* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props, no-new */ /* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props, no-new */
/* global GroupsSelect */
/* global ProjectSelect */ /* global ProjectSelect */
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import './groups_select'; import groupsSelect from './groups_select';
import './project_select'; import './project_select';
class AuditLogs { class AuditLogs {
...@@ -13,7 +12,7 @@ class AuditLogs { ...@@ -13,7 +12,7 @@ class AuditLogs {
initFilters() { initFilters() {
new ProjectSelect(); new ProjectSelect();
new GroupsSelect(); groupsSelect();
new UsersSelect(); new UsersSelect();
this.initFilterDropdown($('.js-type-filter'), 'event_type', null, () => { this.initFilterDropdown($('.js-type-filter'), 'event_type', null, () => {
......
/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */ /* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
/* global Dropzone */ import Dropzone from 'dropzone';
import '../lib/utils/url_utility'; import '../lib/utils/url_utility';
import { HIDDEN_CLASS } from '../lib/utils/constants'; import { HIDDEN_CLASS } from '../lib/utils/constants';
import csrf from '../lib/utils/csrf'; import csrf from '../lib/utils/csrf';
......
...@@ -25,6 +25,11 @@ ...@@ -25,6 +25,11 @@
type: String, type: String,
required: true, required: true,
}, },
viewType: {
type: String,
required: false,
default: 'child',
},
}, },
mixins: [ mixins: [
pipelinesMixin, pipelinesMixin,
...@@ -110,6 +115,7 @@ ...@@ -110,6 +115,7 @@
:pipelines="state.pipelines" :pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown" :update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath" :auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/> />
</div> </div>
</div> </div>
......
/* eslint-disable class-methods-use-this */
import './lib/utils/url_utility'; import './lib/utils/url_utility';
import FilesCommentButton from './files_comment_button'; import FilesCommentButton from './files_comment_button';
import SingleFileDiff from './single_file_diff'; import SingleFileDiff from './single_file_diff';
...@@ -8,7 +6,7 @@ import imageDiffHelper from './image_diff/helpers/index'; ...@@ -8,7 +6,7 @@ import imageDiffHelper from './image_diff/helpers/index';
const UNFOLD_COUNT = 20; const UNFOLD_COUNT = 20;
let isBound = false; let isBound = false;
class Diff { export default class Diff {
constructor() { constructor() {
const $diffFile = $('.files .diff-file'); const $diffFile = $('.files .diff-file');
...@@ -104,7 +102,7 @@ class Diff { ...@@ -104,7 +102,7 @@ class Diff {
} }
this.highlightSelectedLine(); this.highlightSelectedLine();
} }
// eslint-disable-next-line class-methods-use-this
handleParallelLineDown(e) { handleParallelLineDown(e) {
const line = $(e.currentTarget); const line = $(e.currentTarget);
const table = line.closest('table'); const table = line.closest('table');
...@@ -116,11 +114,11 @@ class Diff { ...@@ -116,11 +114,11 @@ class Diff {
table.addClass(`${lineClass}-selected`); table.addClass(`${lineClass}-selected`);
} }
} }
// eslint-disable-next-line class-methods-use-this
diffViewType() { diffViewType() {
return $('.inline-parallel-buttons a.active').data('view-type'); return $('.inline-parallel-buttons a.active').data('view-type');
} }
// eslint-disable-next-line class-methods-use-this
lineNumbers(line) { lineNumbers(line) {
const children = line.find('.diff-line-num').toArray(); const children = line.find('.diff-line-num').toArray();
if (children.length !== 2) { if (children.length !== 2) {
...@@ -128,7 +126,7 @@ class Diff { ...@@ -128,7 +126,7 @@ class Diff {
} }
return children.map(elm => parseInt($(elm).data('linenumber'), 10) || 0); return children.map(elm => parseInt($(elm).data('linenumber'), 10) || 0);
} }
// eslint-disable-next-line class-methods-use-this
highlightSelectedLine() { highlightSelectedLine() {
const hash = gl.utils.getLocationHash(); const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file'); const $diffFiles = $('.diff-file');
...@@ -141,6 +139,3 @@ class Diff { ...@@ -141,6 +139,3 @@ class Diff {
} }
} }
} }
window.gl = window.gl || {};
window.gl.Diff = Diff;
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
/* global NewBranchForm */ /* global NewBranchForm */
/* global NotificationsForm */ /* global NotificationsForm */
/* global NotificationsDropdown */ /* global NotificationsDropdown */
/* global GroupAvatar */ import groupAvatar from './group_avatar';
import GroupLabelSubscription from './group_label_subscription';
/* global LineHighlighter */ /* global LineHighlighter */
import BuildArtifacts from './build_artifacts'; import BuildArtifacts from './build_artifacts';
import CILintEditor from './ci_lint_editor'; import CILintEditor from './ci_lint_editor';
...@@ -90,6 +91,7 @@ import U2FAuthenticate from './u2f/authenticate'; ...@@ -90,6 +91,7 @@ import U2FAuthenticate from './u2f/authenticate';
import Members from './members'; import Members from './members';
import memberExpirationDate from './member_expiration_date'; import memberExpirationDate from './member_expiration_date';
import DueDateSelectors from './due_date_select'; import DueDateSelectors from './due_date_select';
import Diff from './diff';
// EE-only // EE-only
import ApproversSelect from './approvers_select'; import ApproversSelect from './approvers_select';
...@@ -262,7 +264,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -262,7 +264,7 @@ import initGroupAnalytics from './init_group_analytics';
new GLForm($('.milestone-form'), true); new GLForm($('.milestone-form'), true);
break; break;
case 'projects:compare:show': case 'projects:compare:show':
new gl.Diff(); new Diff();
initChangesDropdown(); initChangesDropdown();
break; break;
case 'projects:branches:new': case 'projects:branches:new':
...@@ -300,7 +302,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -300,7 +302,7 @@ import initGroupAnalytics from './init_group_analytics';
new UserCallout(); new UserCallout();
case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:creations:diffs':
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
new gl.Diff(); new Diff();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new GLForm($('.merge-request-form'), true); new GLForm($('.merge-request-form'), true);
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
...@@ -334,7 +336,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -334,7 +336,7 @@ import initGroupAnalytics from './init_group_analytics';
new GLForm($('.release-form'), true); new GLForm($('.release-form'), true);
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new gl.Diff(); new Diff();
shortcut_handler = new ShortcutsIssuable(true); shortcut_handler = new ShortcutsIssuable(true);
new ZenMode(); new ZenMode();
...@@ -350,7 +352,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -350,7 +352,7 @@ import initGroupAnalytics from './init_group_analytics';
new gl.Activities(); new gl.Activities();
break; break;
case 'projects:commit:show': case 'projects:commit:show':
new gl.Diff(); new Diff();
new ZenMode(); new ZenMode();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new MiniPipelineGraph({ new MiniPipelineGraph({
...@@ -398,7 +400,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -398,7 +400,7 @@ import initGroupAnalytics from './init_group_analytics';
break; break;
case 'projects:edit': case 'projects:edit':
new UsersSelect(); new UsersSelect();
new GroupsSelect(); groupsSelect();
setupProjectEdit(); setupProjectEdit();
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
...@@ -459,11 +461,11 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -459,11 +461,11 @@ import initGroupAnalytics from './init_group_analytics';
case 'admin:groups:create': case 'admin:groups:create':
BindInOut.initAll(); BindInOut.initAll();
new Group(); new Group();
new GroupAvatar(); groupAvatar();
break; break;
case 'groups:edit': case 'groups:edit':
case 'admin:groups:edit': case 'admin:groups:edit':
new GroupAvatar(); groupAvatar();
break; break;
case 'projects:tree:show': case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
...@@ -518,7 +520,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -518,7 +520,7 @@ import initGroupAnalytics from './init_group_analytics';
const $el = $(el); const $el = $(el);
if ($el.find('.dropdown-group-label').length) { if ($el.find('.dropdown-group-label').length) {
new gl.GroupLabelSubscription($el); new GroupLabelSubscription($el);
} else { } else {
new gl.ProjectLabelSubscription($el); new gl.ProjectLabelSubscription($el);
} }
......
This diff is collapsed.
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, consistent-return */
/* global notes */
/* Developer beware! Do not add logic to showButton or hideButton /* Developer beware! Do not add logic to showButton or hideButton
* that will force a reflow. Doing so will create a signficant performance * that will force a reflow. Doing so will create a signficant performance
* bottleneck for pages with large diffs. For a comprehensive list of what * bottleneck for pages with large diffs. For a comprehensive list of what
...@@ -20,8 +17,10 @@ const DIFF_EXPANDED_CLASS = 'diff-expanded'; ...@@ -20,8 +17,10 @@ const DIFF_EXPANDED_CLASS = 'diff-expanded';
export default { export default {
init($diffFile) { init($diffFile) {
/* Caching is used only when the following members are *true*. This is because there are likely to be /* Caching is used only when the following members are *true*.
* differently configured versions of diffs in the same session. However if these values are true, they * This is because there are likely to be
* differently configured versions of diffs in the same session.
* However if these values are true, they
* will be true in all cases */ * will be true in all cases */
if (!this.userCanCreateNote) { if (!this.userCanCreateNote) {
......
/* global DropzoneInput */
/* global autosize */ /* global autosize */
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import dropzoneInput from './dropzone_input';
export default class GLForm { export default class GLForm {
constructor(form, enableGFM = false) { constructor(form, enableGFM = false) {
...@@ -41,7 +41,7 @@ export default class GLForm { ...@@ -41,7 +41,7 @@ export default class GLForm {
mergeRequests: this.enableGFM, mergeRequests: this.enableGFM,
labels: this.enableGFM, labels: this.enableGFM,
}); });
new DropzoneInput(this.form); // eslint-disable-line no-new dropzoneInput(this.form);
autosize(this.textarea); autosize(this.textarea);
} }
// form and textarea event listeners // form and textarea event listeners
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, one-var, one-var-declaration-per-line, no-useless-escape, max-len */ export default function groupAvatar() {
$('.js-choose-group-avatar-button').on('click', function onClickGroupAvatar() {
window.GroupAvatar = (function() { const form = $(this).closest('form');
function GroupAvatar() { return form.find('.js-group-avatar-input').click();
$('.js-choose-group-avatar-button').on("click", function() { });
var form; $('.js-group-avatar-input').on('change', function onChangeAvatarInput() {
form = $(this).closest("form"); const form = $(this).closest('form');
return form.find(".js-group-avatar-input").click(); // eslint-disable-next-line no-useless-escape
}); const filename = $(this).val().replace(/^.*[\\\/]/, '');
$('.js-group-avatar-input').on("change", function() { return form.find('.js-avatar-filename').text(filename);
var filename, form; });
form = $(this).closest("form"); }
filename = $(this).val().replace(/^.*[\\\/]/, '');
return form.find(".js-avatar-filename").text(filename);
});
}
return GroupAvatar;
})();
/* eslint-disable func-names, object-shorthand, comma-dangle, wrap-iife, space-before-function-paren, no-param-reassign, max-len */ export default class GroupLabelSubscription {
class GroupLabelSubscription {
constructor(container) { constructor(container) {
const $container = $(container); const $container = $(container);
this.$dropdown = $container.find('.dropdown'); this.$dropdown = $container.find('.dropdown');
...@@ -18,7 +16,7 @@ class GroupLabelSubscription { ...@@ -18,7 +16,7 @@ class GroupLabelSubscription {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: url url,
}).done(() => { }).done(() => {
this.toggleSubscriptionButtons(); this.toggleSubscriptionButtons();
this.$unsubscribeButtons.removeAttr('data-url'); this.$unsubscribeButtons.removeAttr('data-url');
...@@ -35,7 +33,7 @@ class GroupLabelSubscription { ...@@ -35,7 +33,7 @@ class GroupLabelSubscription {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: url url,
}).done(() => { }).done(() => {
this.toggleSubscriptionButtons(); this.toggleSubscriptionButtons();
}); });
...@@ -47,6 +45,3 @@ class GroupLabelSubscription { ...@@ -47,6 +45,3 @@ class GroupLabelSubscription {
this.$unsubscribeButtons.toggleClass('hidden'); this.$unsubscribeButtons.toggleClass('hidden');
} }
} }
window.gl = window.gl || {};
window.gl.GroupLabelSubscription = GroupLabelSubscription;
...@@ -119,7 +119,7 @@ export default { ...@@ -119,7 +119,7 @@ export default {
v-if="canAddRelatedIssues" v-if="canAddRelatedIssues"
ref="issueCountBadgeAddButton" ref="issueCountBadgeAddButton"
type="button" type="button"
class="js-issue-count-badge-add-button issue-count-badge-add-button btn btn-small btn-default" class="js-issue-count-badge-add-button issue-count-badge-add-button btn btn-sm btn-default"
aria-label="Add an issue" aria-label="Add an issue"
data-placement="top" data-placement="top"
@click="toggleAddRelatedIssuesForm"> @click="toggleAddRelatedIssuesForm">
......
...@@ -51,20 +51,19 @@ const PARTICIPANTS_ROW_COUNT = 7; ...@@ -51,20 +51,19 @@ const PARTICIPANTS_ROW_COUNT = 7;
} }
IssuableContext.prototype.initParticipants = function() { IssuableContext.prototype.initParticipants = function() {
$(document).on("click", ".js-participants-more", this.toggleHiddenParticipants); $(document).on('click', '.js-participants-more', this.toggleHiddenParticipants);
return $(".js-participants-author").each(function(i) { return $('.js-participants-author').each(function(i) {
if (i >= PARTICIPANTS_ROW_COUNT) { if (i >= PARTICIPANTS_ROW_COUNT) {
return $(this).addClass("js-participants-hidden").hide(); return $(this).addClass('js-participants-hidden').hide();
} }
}); });
}; };
IssuableContext.prototype.toggleHiddenParticipants = function(e) { IssuableContext.prototype.toggleHiddenParticipants = function() {
var currentText, lessText, originalText; const currentText = $(this).text().trim();
e.preventDefault(); const lessText = $(this).data('less-text');
currentText = $(this).text().trim(); const originalText = $(this).data('original-text');
lessText = $(this).data("less-text");
originalText = $(this).data("original-text");
if (currentText === originalText) { if (currentText === originalText) {
$(this).text(lessText); $(this).text(lessText);
...@@ -73,7 +72,7 @@ const PARTICIPANTS_ROW_COUNT = 7; ...@@ -73,7 +72,7 @@ const PARTICIPANTS_ROW_COUNT = 7;
$(this).text(originalText); $(this).text(originalText);
} }
$(".js-participants-hidden").toggle(); $('.js-participants-hidden').toggle();
}; };
return IssuableContext; return IssuableContext;
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */
/* global GitLab */ /* global GitLab */
/* global GroupsSelect */
import Pikaday from 'pikaday'; import Pikaday from 'pikaday';
import Autosave from './autosave'; import Autosave from './autosave';
...@@ -8,6 +7,7 @@ import UsersSelect from './users_select'; ...@@ -8,6 +7,7 @@ import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode'; import ZenMode from './zen_mode';
import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
import groupsSelect from './groups_select';
(function() { (function() {
this.IssuableForm = (function() { this.IssuableForm = (function() {
...@@ -22,7 +22,7 @@ import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; ...@@ -22,7 +22,7 @@ import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(); new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
new UsersSelect(); new UsersSelect();
new GroupsSelect(); groupsSelect();
new ZenMode(); new ZenMode();
this.titleField = this.form.find("input[name*='[title]']"); this.titleField = this.form.find("input[name*='[title]']");
this.descriptionField = this.form.find("textarea[name*='[description]']"); this.descriptionField = this.form.find("textarea[name*='[description]']");
......
...@@ -16,15 +16,15 @@ ...@@ -16,15 +16,15 @@
<fieldset> <fieldset>
<label <label
class="sr-only" class="sr-only"
for="issue-title"> for="issuable-title">
Title Title
</label> </label>
<input <input
id="issue-title" id="issuable-title"
class="form-control" class="form-control"
type="text" type="text"
placeholder="Issue title" placeholder="Title"
aria-label="Issue title" aria-label="Title"
v-model="formState.title" v-model="formState.title"
@keydown.meta.enter="updateIssuable" @keydown.meta.enter="updateIssuable"
@keydown.ctrl.enter="updateIssuable" /> @keydown.ctrl.enter="updateIssuable" />
......
...@@ -56,8 +56,6 @@ import './gl_dropdown'; ...@@ -56,8 +56,6 @@ import './gl_dropdown';
import './gl_field_error'; import './gl_field_error';
import './gl_field_errors'; import './gl_field_errors';
import './gl_form'; import './gl_form';
import './group_avatar';
import './group_label_subscription';
import './header'; import './header';
import './importer_status'; import './importer_status';
import './issuable_index'; import './issuable_index';
......
...@@ -11,8 +11,8 @@ import { ...@@ -11,8 +11,8 @@ import {
handleLocationHash, handleLocationHash,
isMetaClick, isMetaClick,
} from './lib/utils/common_utils'; } from './lib/utils/common_utils';
import initDiscussionTab from './image_diff/init_discussion_tab'; import initDiscussionTab from './image_diff/init_discussion_tab';
import Diff from './diff';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
...@@ -292,7 +292,7 @@ import initDiscussionTab from './image_diff/init_discussion_tab'; ...@@ -292,7 +292,7 @@ import initDiscussionTab from './image_diff/init_discussion_tab';
} }
this.diffsLoaded = true; this.diffsLoaded = true;
new gl.Diff(); new Diff();
this.scrollToElement('#diffs'); this.scrollToElement('#diffs');
$('.diff-file').each((i, el) => { $('.diff-file').each((i, el) => {
......
...@@ -13,7 +13,6 @@ import $ from 'jquery'; ...@@ -13,7 +13,6 @@ import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import autosize from 'vendor/autosize'; import autosize from 'vendor/autosize';
import Dropzone from 'dropzone';
import 'vendor/jquery.caret'; // required by jquery.atwho import 'vendor/jquery.caret'; // required by jquery.atwho
import 'vendor/jquery.atwho'; import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache'; import AjaxCache from '~/lib/utils/ajax_cache';
...@@ -28,7 +27,6 @@ import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from ...@@ -28,7 +27,6 @@ import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from
import imageDiffHelper from './image_diff/helpers/index'; import imageDiffHelper from './image_diff/helpers/index';
window.autosize = autosize; window.autosize = autosize;
window.Dropzone = Dropzone;
function normalizeNewlines(str) { function normalizeNewlines(str) {
return str.replace(/\r\n/g, '\n'); return str.replace(/\r\n/g, '\n');
...@@ -1283,10 +1281,12 @@ export default class Notes { ...@@ -1283,10 +1281,12 @@ export default class Notes {
* Get data from Form attributes to use for saving/submitting comment. * Get data from Form attributes to use for saving/submitting comment.
*/ */
getFormData($form) { getFormData($form) {
const content = $form.find('.js-note-text').val();
return { return {
formData: $form.serialize(), formData: $form.serialize(),
formContent: _.escape($form.find('.js-note-text').val()), formContent: _.escape(content),
formAction: $form.attr('action'), formAction: $form.attr('action'),
formContentOriginal: content,
}; };
} }
...@@ -1418,7 +1418,7 @@ export default class Notes { ...@@ -1418,7 +1418,7 @@ export default class Notes {
const isMainForm = $form.hasClass('js-main-target-form'); const isMainForm = $form.hasClass('js-main-target-form');
const isDiscussionForm = $form.hasClass('js-discussion-note-form'); const isDiscussionForm = $form.hasClass('js-discussion-note-form');
const isDiscussionResolve = $submitBtn.hasClass('js-comment-resolve-button'); const isDiscussionResolve = $submitBtn.hasClass('js-comment-resolve-button');
const { formData, formContent, formAction } = this.getFormData($form); const { formData, formContent, formAction, formContentOriginal } = this.getFormData($form);
let noteUniqueId; let noteUniqueId;
let systemNoteUniqueId; let systemNoteUniqueId;
let hasQuickActions = false; let hasQuickActions = false;
...@@ -1577,7 +1577,7 @@ export default class Notes { ...@@ -1577,7 +1577,7 @@ export default class Notes {
$form = $notesContainer.parent().find('form'); $form = $notesContainer.parent().find('form');
} }
$form.find('.js-note-text').val(formContent); $form.find('.js-note-text').val(formContentOriginal);
this.reenableTargetFormSubmitButton(e); this.reenableTargetFormSubmitButton(e);
this.addNoteError($form); this.addNoteError($form);
}); });
......
...@@ -12,6 +12,15 @@ ...@@ -12,6 +12,15 @@
type: Object, type: Object,
required: true, required: true,
}, },
// Can be rendered in 3 different places, with some visual differences
// Accepts root | child
// `root` -> main view
// `child` -> rendered inside MR or Commit View
viewType: {
type: String,
required: false,
default: 'root',
},
}, },
components: { components: {
tablePagination, tablePagination,
...@@ -187,7 +196,7 @@ ...@@ -187,7 +196,7 @@
:empty-state-svg-path="emptyStateSvgPath" :empty-state-svg-path="emptyStateSvgPath"
/> />
<error-state <error-state
v-if="shouldRenderErrorState" v-if="shouldRenderErrorState"
:error-state-svg-path="errorStateSvgPath" :error-state-svg-path="errorStateSvgPath"
/> />
...@@ -206,6 +215,7 @@ ...@@ -206,6 +215,7 @@
:pipelines="state.pipelines" :pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown" :update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsPath" :auto-devops-help-path="autoDevopsPath"
:view-type="viewType"
/> />
</div> </div>
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
type: String, type: String,
required: true, required: true,
}, },
viewType: {
type: String,
required: true,
},
}, },
components: { components: {
pipelinesTableRowComponent, pipelinesTableRowComponent,
...@@ -59,6 +63,7 @@ ...@@ -59,6 +63,7 @@
:pipeline="model" :pipeline="model"
:update-graph-dropdown="updateGraphDropdown" :update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath" :auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/> />
</div> </div>
</template> </template>
...@@ -29,6 +29,10 @@ export default { ...@@ -29,6 +29,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
viewType: {
type: String,
required: true,
},
}, },
components: { components: {
asyncButtonComponent, asyncButtonComponent,
...@@ -203,9 +207,13 @@ export default { ...@@ -203,9 +207,13 @@ export default {
displayPipelineActions() { displayPipelineActions() {
return this.pipeline.flags.retryable || return this.pipeline.flags.retryable ||
this.pipeline.flags.cancelable || this.pipeline.flags.cancelable ||
this.pipeline.details.manual_actions.length || this.pipeline.details.manual_actions.length ||
this.pipeline.details.artifacts.length; this.pipeline.details.artifacts.length;
},
isChildView() {
return this.viewType === 'child';
}, },
}, },
}; };
...@@ -218,7 +226,10 @@ export default { ...@@ -218,7 +226,10 @@ export default {
Status Status
</div> </div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<ci-badge :status="pipelineStatus"/> <ci-badge
:status="pipelineStatus"
:show-text="!isChildView"
/>
</div> </div>
</div> </div>
...@@ -240,7 +251,9 @@ export default { ...@@ -240,7 +251,9 @@ export default {
:commit-url="commitUrl" :commit-url="commitUrl"
:short-sha="commitShortSha" :short-sha="commitShortSha"
:title="commitTitle" :title="commitTitle"
:author="commitAuthor"/> :author="commitAuthor"
:show-branch="!isChildView"
/>
</div> </div>
</div> </div>
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
}, },
showError(message) { showError(message) {
Flash((errorMessages[message])); Flash(errorMessages[message]);
}, },
}, },
}; };
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
}, },
showError(message) { showError(message) {
Flash((errorMessages[message])); Flash(errorMessages[message]);
}, },
}, },
}; };
......
...@@ -29,11 +29,9 @@ export const fetchList = ({ commit }, { repo, page }) => { ...@@ -29,11 +29,9 @@ export const fetchList = ({ commit }, { repo, page }) => {
}); });
}; };
export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath) export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath);
.then(res => res.json());
export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath) export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath);
.then(res => res.json());
export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data);
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING); export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING);
...@@ -38,7 +38,7 @@ export default { ...@@ -38,7 +38,7 @@ export default {
tag: element.name, tag: element.name,
revision: element.revision, revision: element.revision,
shortRevision: element.short_revision, shortRevision: element.short_revision,
size: element.size, size: element.total_size,
layers: element.layers, layers: element.layers,
location: element.location, location: element.location,
createdAt: element.created_at, createdAt: element.created_at,
......
<script>
import flash, { hideFlash } from '../../flash';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import eventHub from '../event_hub';
export default {
components: {
loadingIcon,
},
props: {
currentBranch: {
type: String,
required: true,
},
},
data() {
return {
branchName: '',
loading: false,
};
},
computed: {
btnDisabled() {
return this.loading || this.branchName === '';
},
},
methods: {
toggleDropdown() {
this.$dropdown.dropdown('toggle');
},
submitNewBranch() {
// need to query as the element is appended outside of Vue
const flashEl = this.$refs.flashContainer.querySelector('.flash-alert');
this.loading = true;
if (flashEl) {
hideFlash(flashEl, false);
}
eventHub.$emit('createNewBranch', this.branchName);
},
showErrorMessage(message) {
this.loading = false;
flash(message, 'alert', this.$el);
},
createdNewBranch(newBranchName) {
this.loading = false;
this.branchName = '';
if (this.dropdownText) {
this.dropdownText.textContent = newBranchName;
}
},
},
created() {
// Dropdown is outside of Vue instance & is controlled by Bootstrap
this.$dropdown = $('.git-revision-dropdown');
// text element is outside Vue app
this.dropdownText = document.querySelector('.project-refs-form .dropdown-toggle-text');
eventHub.$on('createNewBranchSuccess', this.createdNewBranch);
eventHub.$on('createNewBranchError', this.showErrorMessage);
eventHub.$on('toggleNewBranchDropdown', this.toggleDropdown);
},
destroyed() {
eventHub.$off('createNewBranchSuccess', this.createdNewBranch);
eventHub.$off('toggleNewBranchDropdown', this.toggleDropdown);
eventHub.$off('createNewBranchError', this.showErrorMessage);
},
};
</script>
<template>
<div>
<div
class="flash-container"
ref="flashContainer"
>
</div>
<p>
Create from:
<code>{{ currentBranch }}</code>
</p>
<input
class="form-control js-new-branch-name"
type="text"
placeholder="Name new branch"
v-model="branchName"
@keyup.enter.stop.prevent="submitNewBranch"
/>
<div class="prepend-top-default clearfix">
<button
type="button"
class="btn btn-primary pull-left"
:disabled="btnDisabled"
@click.stop.prevent="submitNewBranch"
>
<loading-icon
v-if="loading"
:inline="true"
/>
<span>Create</span>
</button>
<button
type="button"
class="btn btn-default pull-right"
@click.stop.prevent="toggleDropdown"
>
Cancel
</button>
</div>
</div>
</template>
...@@ -8,7 +8,9 @@ import RepoMixin from '../mixins/repo_mixin'; ...@@ -8,7 +8,9 @@ import RepoMixin from '../mixins/repo_mixin';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import Store from '../stores/repo_store'; import Store from '../stores/repo_store';
import Helper from '../helpers/repo_helper'; import Helper from '../helpers/repo_helper';
import Service from '../services/repo_service';
import MonacoLoaderHelper from '../helpers/monaco_loader_helper'; import MonacoLoaderHelper from '../helpers/monaco_loader_helper';
import eventHub from '../event_hub';
export default { export default {
data() { data() {
...@@ -24,12 +26,19 @@ export default { ...@@ -24,12 +26,19 @@ export default {
PopupDialog, PopupDialog,
RepoPreview, RepoPreview,
}, },
created() {
eventHub.$on('createNewBranch', this.createNewBranch);
},
mounted() { mounted() {
Helper.getContent().catch(Helper.loadingError); Helper.getContent().catch(Helper.loadingError);
}, },
destroyed() {
eventHub.$off('createNewBranch', this.createNewBranch);
},
methods: { methods: {
getCurrentLocation() {
return location.href;
},
toggleDialogOpen(toggle) { toggleDialogOpen(toggle) {
this.dialog.open = toggle; this.dialog.open = toggle;
}, },
...@@ -38,8 +47,25 @@ export default { ...@@ -38,8 +47,25 @@ export default {
this.toggleDialogOpen(false); this.toggleDialogOpen(false);
this.dialog.status = status; this.dialog.status = status;
}, },
toggleBlobView: Store.toggleBlobView, toggleBlobView: Store.toggleBlobView,
createNewBranch(branch) {
Service.createBranch({
branch,
ref: Store.currentBranch,
}).then((res) => {
const newBranchName = res.data.name;
const newUrl = this.getCurrentLocation().replace(Store.currentBranch, newBranchName);
Store.currentBranch = newBranchName;
history.pushState({ key: Helper.key }, '', newUrl);
eventHub.$emit('createNewBranchSuccess', newBranchName);
eventHub.$emit('toggleNewBranchDropdown');
}).catch((err) => {
eventHub.$emit('createNewBranchError', err.response.data.message);
});
},
}, },
}; };
</script> </script>
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
marginLeft: `${this.file.level * 16}px`, marginLeft: `${this.file.level * 16}px`,
}; };
}, },
shortId() {
return this.file.id.substr(0, 8);
},
}, },
methods: { methods: {
linkClicked(file) { linkClicked(file) {
...@@ -55,6 +58,17 @@ ...@@ -55,6 +58,17 @@
> >
{{ file.name }} {{ file.name }}
</a> </a>
<template v-if="file.type === 'submodule' && file.id">
@
<span class="commit-sha">
<a
@click.stop
:href="file.tree_url"
>
{{ shortId }}
</a>
</span>
</template>
</td> </td>
<template v-if="!isMini"> <template v-if="!isMini">
...@@ -69,7 +83,10 @@ ...@@ -69,7 +83,10 @@
</td> </td>
<td class="commit-update hidden-xs text-right"> <td class="commit-update hidden-xs text-right">
<span :title="tooltipTitle(file.lastCommit.updatedAt)"> <span
v-if="file.lastCommit.updatedAt"
:title="tooltipTitle(file.lastCommit.updatedAt)"
>
{{ timeFormated(file.lastCommit.updatedAt) }} {{ timeFormated(file.lastCommit.updatedAt) }}
</span> </span>
</td> </td>
......
...@@ -74,6 +74,10 @@ export default { ...@@ -74,6 +74,10 @@ export default {
if (file.type === 'tree' && file.opened) { if (file.type === 'tree' && file.opened) {
Helper.setDirectoryToClosed(file); Helper.setDirectoryToClosed(file);
Store.setActiveLine(lineNumber); Store.setActiveLine(lineNumber);
} else if (file.type === 'submodule') {
file.loading = true;
gl.utils.visitUrl(file.url);
} else { } else {
const openFile = Helper.getFileFromPath(file.url); const openFile = Helper.getFileFromPath(file.url);
......
...@@ -95,7 +95,7 @@ const RepoHelper = { ...@@ -95,7 +95,7 @@ const RepoHelper = {
return Service.getContent() return Service.getContent()
.then((response) => { .then((response) => {
const data = response.data; const data = response.data;
if (response.headers && response.headers['page-title']) data.pageTitle = response.headers['page-title']; if (response.headers && response.headers['page-title']) data.pageTitle = decodeURI(response.headers['page-title']);
if (response.headers && response.headers['is-root'] && !Store.isInitialRoot) { if (response.headers && response.headers['is-root'] && !Store.isInitialRoot) {
Store.isRoot = convertPermissionToBoolean(response.headers['is-root']); Store.isRoot = convertPermissionToBoolean(response.headers['is-root']);
Store.isInitialRoot = Store.isRoot; Store.isInitialRoot = Store.isRoot;
...@@ -157,12 +157,14 @@ const RepoHelper = { ...@@ -157,12 +157,14 @@ const RepoHelper = {
}, },
serializeRepoEntity(type, entity, level = 0) { serializeRepoEntity(type, entity, level = 0) {
const { url, name, icon, last_commit } = entity; const { id, url, name, icon, last_commit, tree_url } = entity;
return { return {
id,
type, type,
name, name,
url, url,
tree_url,
level, level,
icon: `fa-${icon}`, icon: `fa-${icon}`,
files: [], files: [],
......
...@@ -5,6 +5,7 @@ import Service from './services/repo_service'; ...@@ -5,6 +5,7 @@ import Service from './services/repo_service';
import Store from './stores/repo_store'; import Store from './stores/repo_store';
import Repo from './components/repo.vue'; import Repo from './components/repo.vue';
import RepoEditButton from './components/repo_edit_button.vue'; import RepoEditButton from './components/repo_edit_button.vue';
import newBranchForm from './components/new_branch_form.vue';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
function initDropdowns() { function initDropdowns() {
...@@ -62,6 +63,26 @@ function initRepoEditButton(el) { ...@@ -62,6 +63,26 @@ function initRepoEditButton(el) {
}); });
} }
function initNewBranchForm() {
const el = document.querySelector('.js-new-branch-dropdown');
if (!el) return null;
return new Vue({
el,
components: {
newBranchForm,
},
render(createElement) {
return createElement('new-branch-form', {
props: {
currentBranch: Store.currentBranch,
},
});
},
});
}
function initRepoBundle() { function initRepoBundle() {
const repo = document.getElementById('repo'); const repo = document.getElementById('repo');
const editButton = document.querySelector('.editable-mode'); const editButton = document.querySelector('.editable-mode');
...@@ -73,6 +94,7 @@ function initRepoBundle() { ...@@ -73,6 +94,7 @@ function initRepoBundle() {
initRepo(repo); initRepo(repo);
initRepoEditButton(editButton); initRepoEditButton(editButton);
initNewBranchForm();
} }
$(initRepoBundle); $(initRepoBundle);
......
import axios from 'axios'; import axios from 'axios';
import csrf from '../../lib/utils/csrf';
import Store from '../stores/repo_store'; import Store from '../stores/repo_store';
import Api from '../../api'; import Api from '../../api';
import Helper from '../helpers/repo_helper'; import Helper from '../helpers/repo_helper';
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
const RepoService = { const RepoService = {
url: '', url: '',
options: { options: {
...@@ -10,6 +13,7 @@ const RepoService = { ...@@ -10,6 +13,7 @@ const RepoService = {
format: 'json', format: 'json',
}, },
}, },
createBranchPath: '/api/:version/projects/:id/repository/branches',
richExtensionRegExp: /md/, richExtensionRegExp: /md/,
getRaw(url) { getRaw(url) {
...@@ -73,6 +77,12 @@ const RepoService = { ...@@ -73,6 +77,12 @@ const RepoService = {
.then(this.commitFlash); .then(this.commitFlash);
}, },
createBranch(payload) {
const url = Api.buildUrl(this.createBranchPath)
.replace(':id', Store.projectId);
return axios.post(url, payload);
},
commitFlash(data) { commitFlash(data) {
if (data.short_id && data.stats) { if (data.short_id && data.stats) {
window.Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); window.Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice');
......
...@@ -13,6 +13,7 @@ const RepoStore = { ...@@ -13,6 +13,7 @@ const RepoStore = {
projectId: '', projectId: '',
projectName: '', projectName: '',
projectUrl: '', projectUrl: '',
branchUrl: '',
blobRaw: '', blobRaw: '',
currentBlobView: 'repo-preview', currentBlobView: 'repo-preview',
openedFiles: [], openedFiles: [],
......
<script> <script>
import ciIcon from './ci_icon.vue'; import ciIcon from './ci_icon.vue';
/** import tooltip from '../directives/tooltip';
* Renders CI Badge link with CI icon and status text based on /**
* API response shared between all places where it is used. * Renders CI Badge link with CI icon and status text based on
* * API response shared between all places where it is used.
* Receives status object containing: *
* status: { * Receives status object containing:
* details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url * status: {
* group:"running" // used for CSS class * details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url
* icon: "icon_status_running" // used to render the icon * group:"running" // used for CSS class
* label:"running" // used for potential tooltip * icon: "icon_status_running" // used to render the icon
* text:"running" // text rendered * label:"running" // used for potential tooltip
* } * text:"running" // text rendered
* * }
* Used in: *
* - Pipelines table - first column * Used in:
* - Jobs table - first column * - Pipelines table - first column
* - Pipeline show view - header * - Jobs table - first column
* - Job show view - header * - Pipeline show view - header
* - MR widget * - Job show view - header
*/ * - MR widget
*/
export default { export default {
props: { props: {
status: { status: {
type: Object, type: Object,
required: true, required: true,
},
showText: {
type: Boolean,
required: false,
default: true,
},
}, },
}, components: {
ciIcon,
components: { },
ciIcon, directives: {
}, tooltip,
},
computed: { computed: {
cssClass() { cssClass() {
const className = this.status.group; const className = this.status.group;
return className ? `ci-status ci-${this.status.group}` : 'ci-status'; return className ? `ci-status ci-${className}` : 'ci-status';
},
}, },
}, };
};
</script> </script>
<template> <template>
<a <a
:href="status.details_path" :href="status.details_path"
:class="cssClass"> :class="cssClass"
v-tooltip
:title="!showText ? status.text : ''">
<ci-icon :status="status" /> <ci-icon :status="status" />
{{status.text}}
<template v-if="showText">
{{status.text}}
</template>
</a> </a>
</template> </template>
...@@ -63,14 +63,17 @@ ...@@ -63,14 +63,17 @@
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
showBranch: {
type: Boolean,
required: false,
default: true,
},
}, },
computed: { computed: {
/** /**
* Used to verify if all the properties needed to render the commit * Used to verify if all the properties needed to render the commit
* ref section were provided. * ref section were provided.
* *
* TODO: Improve this! Use lodash _.has when we have it.
*
* @returns {Boolean} * @returns {Boolean}
*/ */
hasCommitRef() { hasCommitRef() {
...@@ -80,8 +83,6 @@ ...@@ -80,8 +83,6 @@
* Used to verify if all the properties needed to render the commit * Used to verify if all the properties needed to render the commit
* author section were provided. * author section were provided.
* *
* TODO: Improve this! Use lodash _.has when we have it.
*
* @returns {Boolean} * @returns {Boolean}
*/ */
hasAuthor() { hasAuthor() {
...@@ -114,31 +115,30 @@ ...@@ -114,31 +115,30 @@
</script> </script>
<template> <template>
<div class="branch-commit"> <div class="branch-commit">
<div <template v-if="hasCommitRef && showBranch">
v-if="hasCommitRef" <div
class="icon-container hidden-xs"> class="icon-container hidden-xs">
<i <i
v-if="tag" v-if="tag"
class="fa fa-tag" class="fa fa-tag"
aria-hidden="true"> aria-hidden="true">
</i> </i>
<i <i
v-if="!tag" v-if="!tag"
class="fa fa-code-fork" class="fa fa-code-fork"
aria-hidden="true"> aria-hidden="true">
</i> </i>
</div> </div>
<a
v-if="hasCommitRef"
class="ref-name hidden-xs"
:href="commitRef.ref_url"
v-tooltip
data-container="body"
:title="commitRef.name">
{{commitRef.name}}
</a>
<a
class="ref-name hidden-xs"
:href="commitRef.ref_url"
v-tooltip
data-container="body"
:title="commitRef.name">
{{commitRef.name}}
</a>
</template>
<div <div
v-html="commitIconSvg" v-html="commitIconSvg"
class="commit-icon js-commit-icon"> class="commit-icon js-commit-icon">
......
...@@ -11,8 +11,6 @@ import Dropzone from 'dropzone'; ...@@ -11,8 +11,6 @@ import Dropzone from 'dropzone';
import 'mousetrap'; import 'mousetrap';
import 'mousetrap/plugins/pause/mousetrap-pause'; import 'mousetrap/plugins/pause/mousetrap-pause';
window.Dropzone = Dropzone;
// //
// ### Events // ### Events
// //
......
...@@ -23,6 +23,11 @@ ...@@ -23,6 +23,11 @@
@include webkit-prefix(animation-duration, 2s); @include webkit-prefix(animation-duration, 2s);
} }
&.spin {
transform-origin: center;
animation: spin 4s linear infinite;
}
&.flipOutX, &.flipOutX,
&.flipOutY, &.flipOutY,
&.bounceIn, &.bounceIn,
...@@ -271,3 +276,9 @@ a { ...@@ -271,3 +276,9 @@ a {
transform: translateX(468px); transform: translateX(468px);
} }
} }
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
...@@ -330,7 +330,7 @@ ...@@ -330,7 +330,7 @@
position: fixed; position: fixed;
bottom: 0; bottom: 0;
padding: 16px; padding: 16px;
background-color: $gray-normal; background-color: $gray-light;
border: 0; border: 0;
border-top: 2px solid $border-color; border-top: 2px solid $border-color;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
......
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
border-radius: $border-radius-default; border-radius: $border-radius-default;
line-height: 16px; line-height: 16px;
font-weight: $gl-font-weight-normal; font-weight: $gl-font-weight-normal;
padding: $gl-btn-padding; padding: 8px;
} }
...@@ -361,6 +361,6 @@ class ApplicationController < ActionController::Base ...@@ -361,6 +361,6 @@ class ApplicationController < ActionController::Base
def set_page_title_header def set_page_title_header
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8 # Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
response.headers['Page-Title'] = page_title('GitLab').encode('ISO-8859-1') response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
end end
end end
...@@ -148,6 +148,17 @@ class GroupsController < Groups::ApplicationController ...@@ -148,6 +148,17 @@ class GroupsController < Groups::ApplicationController
end end
def load_events def load_events
params[:sort] ||= 'latest_activity_desc'
options = {}
options[:only_owned] = true if params[:shared] == '0'
options[:only_shared] = true if params[:shared] == '1'
@projects = GroupProjectsFinder.new(params: params, group: group, options: options, current_user: current_user)
.execute
.includes(:namespace)
.page(params[:page])
@events = EventCollection @events = EventCollection
.new(@projects, offset: params[:offset].to_i, filter: event_filter) .new(@projects, offset: params[:offset].to_i, filter: event_filter)
.to_a .to_a
......
...@@ -48,6 +48,8 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -48,6 +48,8 @@ class Projects::CommitsController < Projects::ApplicationController
private private
def set_commits def set_commits
render_404 unless request.format == :atom || @repository.blob_at(@commit.id, @path) || @repository.tree(@commit.id, @path).entries.present?
@limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i
search = params[:search] search = params[:search]
......
...@@ -313,4 +313,8 @@ module ApplicationHelper ...@@ -313,4 +313,8 @@ module ApplicationHelper
def show_new_repo? def show_new_repo?
cookies["new_repo"] == "true" && body_data_page != 'projects:show' cookies["new_repo"] == "true" && body_data_page != 'projects:show'
end end
def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end
end end
...@@ -36,7 +36,8 @@ module PreferencesHelper ...@@ -36,7 +36,8 @@ module PreferencesHelper
def project_view_choices def project_view_choices
[ [
['Files and Readme (default)', :files], ['Files and Readme (default)', :files],
['Activity', :activity] ['Activity', :activity],
['Readme', :readme]
] ]
end end
......
...@@ -17,7 +17,7 @@ module ProjectsHelper ...@@ -17,7 +17,7 @@ module ProjectsHelper
end end
def link_to_member_avatar(author, opts = {}) def link_to_member_avatar(author, opts = {})
default_opts = { size: 16 } default_opts = { size: 16, lazy_load: false }
opts = default_opts.merge(opts) opts = default_opts.merge(opts)
classes = %W[avatar avatar-inline s#{opts[:size]}] classes = %W[avatar avatar-inline s#{opts[:size]}]
...@@ -29,8 +29,26 @@ module ProjectsHelper ...@@ -29,8 +29,26 @@ module ProjectsHelper
image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar) image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar)
end end
def author_content_tag(author, opts = {})
default_opts = { author_class: 'author', tooltip: false, by_username: false }
opts = default_opts.merge(opts)
has_tooltip = !opts[:by_username] && opts[:tooltip]
username = opts[:by_username] ? author.to_reference : author.name
name_tag_options = { class: [opts[:author_class]] }
if has_tooltip
name_tag_options[:title] = author.to_reference
name_tag_options[:data] = { placement: 'top' }
name_tag_options[:class] << 'has-tooltip'
end
content_tag(:span, sanitize(username), name_tag_options)
end
def link_to_member(project, author, opts = {}, &block) def link_to_member(project, author, opts = {}, &block)
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false, lazy_load: false } default_opts = { avatar: true, name: true, title: ":name" }
opts = default_opts.merge(opts) opts = default_opts.merge(opts)
return "(deleted)" unless author return "(deleted)" unless author
...@@ -41,12 +59,7 @@ module ProjectsHelper ...@@ -41,12 +59,7 @@ module ProjectsHelper
author_html << link_to_member_avatar(author, opts) if opts[:avatar] author_html << link_to_member_avatar(author, opts) if opts[:avatar]
# Build name span tag # Build name span tag
if opts[:by_username] author_html << author_content_tag(author, opts) if opts[:name]
author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
else
tooltip_data = { placement: 'top' }
author_html << content_tag(:span, sanitize(author.name), class: [opts[:author_class], ('has-tooltip' if opts[:tooltip])], title: (author.to_reference if opts[:tooltip]), data: (tooltip_data if opts[:tooltip])) if opts[:name]
end
author_html << capture(&block) if block author_html << capture(&block) if block
......
...@@ -228,7 +228,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -228,7 +228,10 @@ class ApplicationSetting < ActiveRecord::Base
ensure_cache_setup ensure_cache_setup
Rails.cache.fetch(CACHE_KEY) do Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last ApplicationSetting.last.tap do |settings|
# do not cache nils
raise 'missing settings' unless settings
end
end end
rescue rescue
# Fall back to an uncached value if there are any problems (e.g. redis down) # Fall back to an uncached value if there are any problems (e.g. redis down)
......
...@@ -2,7 +2,7 @@ module Ci ...@@ -2,7 +2,7 @@ module Ci
class ArtifactBlob class ArtifactBlob
include BlobLike include BlobLike
EXTENTIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json].freeze EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json].freeze
attr_reader :entry attr_reader :entry
...@@ -36,17 +36,22 @@ module Ci ...@@ -36,17 +36,22 @@ module Ci
def external_url(project, job) def external_url(project, job)
return unless external_link?(job) return unless external_link?(job)
components = project.full_path_components full_path_parts = project.full_path_components
components << "-/jobs/#{job.id}/artifacts/file/#{path}" top_level_group = full_path_parts.shift
artifact_path = components[1..-1].join('/')
"#{pages_config.protocol}://#{components[0]}.#{pages_config.host}/#{artifact_path}" artifact_path = [
'-', *full_path_parts, '-',
'jobs', job.id,
'artifacts', path
].join('/')
"#{pages_config.protocol}://#{top_level_group}.#{pages_config.host}/#{artifact_path}"
end end
def external_link?(job) def external_link?(job)
pages_config.enabled && pages_config.enabled &&
pages_config.artifacts_server && pages_config.artifacts_server &&
EXTENTIONS_SERVED_BY_PAGES.include?(File.extname(name)) && EXTENSIONS_SERVED_BY_PAGES.include?(File.extname(name)) &&
job.project.public? job.project.public?
end end
......
...@@ -185,13 +185,8 @@ class User < ActiveRecord::Base ...@@ -185,13 +185,8 @@ class User < ActiveRecord::Base
enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos] enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos]
# User's Project preference # User's Project preference
# # Note: When adding an option, it MUST go on the end of the array.
# Note: When adding an option, it MUST go on the end of the hash with a enum project_view: [:readme, :activity, :files]
# number higher than the current max. We cannot move options and/or change
# their numbers.
#
# We skip 0 because this was used by an option that has since been removed.
enum project_view: { activity: 1, files: 2 }
alias_attribute :private_token, :authentication_token alias_attribute :private_token, :authentication_token
......
class ContainerTagEntity < Grape::Entity class ContainerTagEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :name, :location, :revision, :total_size, :created_at expose :name, :location, :revision, :short_revision, :total_size, :created_at
expose :destroy_path, if: -> (*) { can_destroy? } do |tag| expose :destroy_path, if: -> (*) { can_destroy? } do |tag|
project_registry_repository_tag_path(project, tag.repository, tag.name, format: :json) project_registry_repository_tag_path(project, tag.repository, tag.name)
end end
private private
......
...@@ -7,7 +7,7 @@ class SubmoduleEntity < Grape::Entity ...@@ -7,7 +7,7 @@ class SubmoduleEntity < Grape::Entity
'archive' 'archive'
end end
expose :project_url do |blob| expose :url do |blob|
submodule_links(blob, request).first submodule_links(blob, request).first
end end
......
...@@ -52,7 +52,7 @@ module Projects ...@@ -52,7 +52,7 @@ module Projects
end end
def wiki_path def wiki_path
repo_path + '.wiki' project.wiki.disk_path
end end
def trash_repositories! def trash_repositories!
......
...@@ -16,8 +16,8 @@ module Users ...@@ -16,8 +16,8 @@ module Users
user_cache_key user_cache_key
] ]
if event.project.forked? if forked_from = event.project.forked_from_project
keys << project_cache_key(event.project.forked_from_project) keys << project_cache_key(forked_from)
end end
keys.each { |key| set_key(key, event.id) } keys.each { |key| set_key(key, event.id) }
......
...@@ -44,4 +44,4 @@ ...@@ -44,4 +44,4 @@
= render "discussions/diff_with_notes", discussion: discussion = render "discussions/diff_with_notes", discussion: discussion
- else - else
.panel.panel-default .panel.panel-default
= render "discussions/notes", discussion: discussion = render partial: "discussions/notes", locals: { discussion: discussion, disable_collapse_class: true }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%td.notes_line.old %td.notes_line.old
%td.notes_content.parallel.old %td.notes_content.parallel.old
.content{ class: ('hide' unless discussions_left.any?(&:expanded?)) } .content{ class: ('hide' unless discussions_left.any?(&:expanded?)) }
= render partial: "discussions/notes", collection: discussions_left, as: :discussion, line_type: 'old' = render partial: "discussions/notes", collection: discussions_left, as: :discussion, line_type: 'old', locals: { disable_collapse_class: true }
- else - else
%td.notes_line.old= ("") %td.notes_line.old= ("")
%td.notes_content.parallel.old %td.notes_content.parallel.old
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%td.notes_line.new %td.notes_line.new
%td.notes_content.parallel.new %td.notes_content.parallel.new
.content{ class: ('hide' unless discussions_right.any?(&:expanded?)) } .content{ class: ('hide' unless discussions_right.any?(&:expanded?)) }
= render partial: "discussions/notes", collection: discussions_right, as: :discussion, line_type: 'new' = render partial: "discussions/notes", collection: discussions_right, as: :discussion, line_type: 'new', locals: { disable_collapse_class: true }
- else - else
%td.notes_line.new= ("") %td.notes_line.new= ("")
%td.notes_content.parallel.new %td.notes_content.parallel.new
......
...@@ -20,13 +20,6 @@ ...@@ -20,13 +20,6 @@
= render 'shared/issuable/search_bar', type: :issues = render 'shared/issuable/search_bar', type: :issues
.row-content-block.second-block
Only issues from the
%strong= @group.name
group are listed here.
- if current_user
To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
= render 'shared/issues' = render 'shared/issues'
- else - else
= render 'shared/empty_states/issues', project_select_button: true = render 'shared/empty_states/issues', project_select_button: true
...@@ -15,11 +15,4 @@ ...@@ -15,11 +15,4 @@
= render 'shared/issuable/search_bar', type: :merge_requests = render 'shared/issuable/search_bar', type: :merge_requests
.row-content-block.second-block
Only merge requests from
%strong= @group.name
group are listed here.
- if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
= render 'shared/merge_requests' = render 'shared/merge_requests'
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
- if content_for?(:library_javascripts) - if content_for?(:library_javascripts)
= yield :library_javascripts = yield :library_javascripts
= javascript_include_tag asset_path("locale/#{I18n.locale.to_s || I18n.default_locale.to_s}/app.js") unless I18n.locale == :en = javascript_include_tag locale_path unless I18n.locale == :en
= webpack_bundle_tag "webpack_runtime" = webpack_bundle_tag "webpack_runtime"
= webpack_bundle_tag "common" = webpack_bundle_tag "common"
= webpack_bundle_tag "main" = webpack_bundle_tag "main"
......
...@@ -288,6 +288,11 @@ ...@@ -288,6 +288,11 @@
= sprite_icon('users') = sprite_icon('users')
%span.nav-item-name %span.nav-item-name
Members Members
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: %w[members#show], html_options: { class: "fly-out-top-item" } ) do
= link_to project_settings_members_path(@project) do
%strong.fly-out-top-item-name
#{ _('Members') }
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
......
- local_assigns.fetch(:view)
%strong
%span{ data: { defer_to: "#{view.defer_key}-duration" } } ...
\/
%span{ data: { defer_to: "#{view.defer_key}-calls" } } ...
Gitaly
- if (readme = @repository.readme) && readme.rich_viewer
%article.file-holder.readme-holder{ id: 'readme', class: ("limited-width-container" unless fluid_layout) }
.js-file-title.file-title
= blob_icon readme.mode, readme.name
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
%strong
= readme.name
= render 'projects/blob/viewer', viewer: readme.rich_viewer, viewer_url: namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, readme.path), viewer: :rich, format: :json)
- else
.row-content-block.second-block.center
%h3.page-title
This project does not have a README yet
- if can?(current_user, :push_code, @project)
%p
A
%code README
file contains information about other files in a repository and is commonly
distributed with computer software, forming part of its documentation.
%p
We recommend you to
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
file to the repository and GitLab will render it here instead of this message.
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.col-sm-8.col-sm-offset-4.signin-with-google .col-sm-8.col-sm-offset-4.signin-with-google
- if @authorize_url - if @authorize_url
= link_to @authorize_url do = link_to @authorize_url do
= image_tag('auth_buttons/signin_with_google.png') = image_tag('auth_buttons/signin_with_google.png', width: '191px')
- else - else
- link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer') - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer')
= s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link } = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link }
.tree-ref-container .tree-ref-container
.tree-ref-holder .tree-ref-holder
= render 'shared/ref_switcher', destination: 'tree', path: @path = render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true
- unless show_new_repo? - unless show_new_repo?
= render 'projects/tree/old_tree_header' = render 'projects/tree/old_tree_header'
......
- show_new_branch_form = show_new_repo? && show_create && can?(current_user, :push_code, @project)
- dropdown_toggle_text = @ref || @project.default_branch - dropdown_toggle_text = @ref || @project.default_branch
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= hidden_field_tag :destination, destination = hidden_field_tag :destination, destination
...@@ -7,8 +8,20 @@ ...@@ -7,8 +8,20 @@
= hidden_field_tag key, value, id: nil = hidden_field_tag key, value, id: nil
.dropdown .dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project), field_name: 'ref', submit_form_on_click: true, visit: true }, { toggle_class: "js-project-refs-dropdown" } = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project), field_name: 'ref', submit_form_on_click: true, visit: true }, { toggle_class: "js-project-refs-dropdown" }
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } .dropdown-menu.dropdown-menu-selectable.git-revision-dropdown.dropdown-menu-paging{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
= dropdown_title _("Switch branch/tag") .dropdown-page-one
= dropdown_filter _("Search branches and tags") = dropdown_title _("Switch branch/tag")
= dropdown_content = dropdown_filter _("Search branches and tags")
= dropdown_loading = dropdown_content
= dropdown_loading
- if show_new_branch_form
= dropdown_footer do
%ul.dropdown-footer-list
%li
%a.dropdown-toggle-page{ href: "#" }
Create new branch
- if show_new_branch_form
.dropdown-page-two
= dropdown_title("Create new branch", options: { back: true })
= dropdown_content do
.js-new-branch-dropdown
This diff is collapsed.
...@@ -14,5 +14,5 @@ ...@@ -14,5 +14,5 @@
= link_to_member(@project, participant, name: false, size: 24, lazy_load: true) = link_to_member(@project, participant, name: false, size: 24, lazy_load: true)
- if participants_extra > 0 - if participants_extra > 0
.hide-collapsed.participants-more .hide-collapsed.participants-more
%a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } } %button.btn-transparent.btn-blank.js-participants-more{ type: 'button', data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } }
+ #{participants_extra} more + #{participants_extra} more
---
title: fix height of rebase and approve buttons
merge_request:
author:
type: fixed
---
title: Decrease ABC threshold to 54.28
merge_request: 14920
author: Maxim Rydkin
type: other
---
title: Add readme only option as project view
merge_request: 14900
author:
type: changed
---
title: Decrease Perceived Complexity threshold to 14
merge_request: 14231
author: Maxim Rydkin
type: other
---
title: Get Project Branch API shows an helpful error message on invalid refname
merge_request: 14884
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Remove help text from group issues page and group merge requests page
merge_request: 14963
author:
type: removed
---
title: Animate auto devops graphic
merge_request:
author:
type: other
---
title: Fix the atom feed for group events
merge_request: 14974
author:
type: fixed
---
title: Only cache last push event for existing projects when pushing to a fork
merge_request: 14989
author:
type: fixed
---
title: Get true failure from evalulate_script by checking for element beforehand
merge_request: 14898
author:
type: fixed
---
title: Fix application setting to cache nil object
merge_request:
author:
type: fixed
---
title: Clarify system_hook triggers in documentation
merge_request: 14957
author: Joe Marty
type: other
---
title: Added submodule support in multi-file editor
merge_request:
author:
type: added
---
title: Renders 404 in commits controller if no commits are found for a given path
merge_request: 14610
author: Guilherme Vieira
type: fixed
---
title: Fix deletion of container registry or images returning an error
merge_request:
author:
type: fixed
---
title: Fix SAML error 500 when no groups are defined for user
merge_request: 14913
author:
type: fixed
---
title: Use title as placeholder instead of issue title for reusability
merge_request:
author:
type: other
---
title: Add Gitaly metrics to the performance bar
merge_request:
author:
type: other
...@@ -16,6 +16,7 @@ Peek.into Peek::Views::Redis ...@@ -16,6 +16,7 @@ Peek.into Peek::Views::Redis
Peek.into Peek::Views::Sidekiq Peek.into Peek::Views::Sidekiq
Peek.into Peek::Views::Rblineprof Peek.into Peek::Views::Rblineprof
Peek.into Peek::Views::GC Peek.into Peek::Views::GC
Peek.into Peek::Views::Gitaly
# rubocop:disable Style/ClassAndModuleCamelCase # rubocop:disable Style/ClassAndModuleCamelCase
class PEEK_DB_CLIENT class PEEK_DB_CLIENT
......
...@@ -316,8 +316,13 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -316,8 +316,13 @@ constraints(ProjectUrlConstrainer.new) do
namespace :registry do namespace :registry do
resources :repository, only: [] do resources :repository, only: [] do
resources :tags, only: [:index, :destroy], # We default to JSON format in the controller to avoid ambiguity.
constraints: { id: Gitlab::Regex.container_registry_tag_regex } # `latest.json` could either be a request for a tag named `latest`
# in JSON format, or a request for tag named `latest.json`.
scope format: false do
resources :tags, only: [:index, :destroy],
constraints: { id: Gitlab::Regex.container_registry_tag_regex }
end
end end
end end
......
...@@ -94,7 +94,7 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i ...@@ -94,7 +94,7 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
### Issues and Merge Requests (MRs) ### Issues and Merge Requests (MRs)
- [Discussions](user/discussions/index.md) Threads, comments, and resolvable discussions in issues, commits, and merge requests. - [Discussions](user/discussions/index.md): Threads, comments, and resolvable discussions in issues, commits, and merge requests.
- [Issues](user/project/issues/index.md) - [Issues](user/project/issues/index.md)
- [Project issue Board](user/project/issue_board.md) - [Project issue Board](user/project/issue_board.md)
- **(EEP)** [Group Issue Boards](user/project/issue_board.md#group-issue-boards) - **(EEP)** [Group Issue Boards](user/project/issue_board.md#group-issue-boards)
...@@ -175,7 +175,7 @@ have access to GitLab administration tools and settings. ...@@ -175,7 +175,7 @@ have access to GitLab administration tools and settings.
- [Git LFS configuration](workflow/lfs/lfs_administration.md): Learn how to use LFS under GitLab. - [Git LFS configuration](workflow/lfs/lfs_administration.md): Learn how to use LFS under GitLab.
- [GitLab Pages configuration](administration/pages/index.md): Configure GitLab Pages. - [GitLab Pages configuration](administration/pages/index.md): Configure GitLab Pages.
- [High Availability](administration/high_availability/README.md): Configure multiple servers for scaling or high availability. - [High Availability](administration/high_availability/README.md): Configure multiple servers for scaling or high availability.
- [User cohorts](user/admin_area/user_cohorts.md) View user activity over time. - [User cohorts](user/admin_area/user_cohorts.md): View user activity over time.
- [Web terminals](administration/integration/terminal.md): Provide terminal access to environments from within GitLab. - [Web terminals](administration/integration/terminal.md): Provide terminal access to environments from within GitLab.
- **(EES/EEP)** [Audit logs and events](administration/audit_events.md): View the changes made within the GitLab server. - **(EES/EEP)** [Audit logs and events](administration/audit_events.md): View the changes made within the GitLab server.
- **(EES/EEP)** [Elasticsearch](integration/elasticsearch.md): Enable Elasticsearch which powers GitLab's Advanced Global Search. Useful when you deal with a huge amount of data. - **(EES/EEP)** [Elasticsearch](integration/elasticsearch.md): Enable Elasticsearch which powers GitLab's Advanced Global Search. Useful when you deal with a huge amount of data.
......
...@@ -18,8 +18,7 @@ other than production, the corresponding logfile is shown here.) ...@@ -18,8 +18,7 @@ other than production, the corresponding logfile is shown here.)
It contains a structured log for Rails controller requests received from It contains a structured log for Rails controller requests received from
GitLab, thanks to [Lograge](https://github.com/roidrage/lograge/). Note that GitLab, thanks to [Lograge](https://github.com/roidrage/lograge/). Note that
requests from the API [are not yet logged to this requests from the API are logged to a separate file in `api_json.log`.
file](https://gitlab.com/gitlab-org/gitlab-ce/issues/36189).
Each line contains a JSON line that can be ingested by Elasticsearch, Splunk, etc. For example: Each line contains a JSON line that can be ingested by Elasticsearch, Splunk, etc. For example:
...@@ -73,6 +72,27 @@ In this example we can see that server processed an HTTP request with URL ...@@ -73,6 +72,27 @@ In this example we can see that server processed an HTTP request with URL
19:34:53 +0200. Also we can see that request was processed by 19:34:53 +0200. Also we can see that request was processed by
`Projects::TreeController`. `Projects::TreeController`.
## `api_json.log`
Introduced in GitLab 10.0, this file lives in
`/var/log/gitlab/gitlab-rails/api_json.log` for Omnibus GitLab packages or in
`/home/git/gitlab/log/api_json.log` for installations from source.
It helps you see requests made directly to the API. For example:
```json
{"time":"2017-10-10T12:30:11.579Z","severity":"INFO","duration":16.84,"db":1.57,"view":15.27,"status":200,"method":"POST","path":"/api/v4/internal/allowed","params":{"action":"git-upload-pack","changes":"_any","gl_repository":null,"project":"root/foobar.git","protocol":"ssh","env":"{}","key_id":"[FILTERED]","secret_token":"[FILTERED]"},"host":"127.0.0.1","ip":"127.0.0.1","ua":"Ruby"}
```
This entry above shows an access to an internal endpoint to check whether an
associated SSH key can download the project in question via a `git fetch` or
`git clone`. In this example, we see:
1. `method`: The HTTP method used to make the request
1. `path`: The relative path of the query
1. `params`: Key-value pairs passed in a query string or HTTP body. Sensitive parameters (e.g. passwords, tokens, etc.) are filtered out.
1. `ua`: The User-Agent of the requester
## `application.log` ## `application.log`
This file lives in `/var/log/gitlab/gitlab-rails/application.log` for This file lives in `/var/log/gitlab/gitlab-rails/application.log` for
......
...@@ -64,7 +64,21 @@ following locations: ...@@ -64,7 +64,21 @@ following locations:
## Road to GraphQL ## Road to GraphQL
We have changed our plans to move to GraphQL. After reviewing the GraphQL license, anything related to the Facebook BSD plus patent license will not be allowed at GitLab. Going forward, we will start on moving to
[GraphQL](http://graphql.org/learn/best-practices/) and deprecate the use of
controller-specific endpoints. GraphQL has a number of benefits:
1. We avoid having to maintain two different APIs.
2. Callers of the API can request only what they need.
3. It is versioned by default.
It will co-exist with the current v4 REST API. If we have a v5 API, this should
be a compatibility layer on top of GraphQL.
Although there were some patenting and licensing concerns with GraphQL, these
have been resolved to our satisfaction by the relicensing of the reference
implementations under MIT, and the use of the OWF license for the GraphQL
specification.
## Basic usage ## Basic usage
...@@ -442,6 +456,23 @@ Content-Type: application/json ...@@ -442,6 +456,23 @@ Content-Type: application/json
} }
``` ```
## Encoding `+` in ISO 8601 dates
If you need to include a `+` in a query parameter, you may need to use `%2B` instead due
a [W3 recommendation](http://www.w3.org/Addressing/URL/4_URI_Recommentations.html) that
causes a `+` to be interpreted as a space. For example, in an ISO 8601 date, you may want to pass
a time in Mountain Standard Time, such as:
```
2017-10-17T23:11:13.000+05:30
```
The correct encoding for the query parameter would be:
```
2017-10-17T23:11:13.000%2B05:30
```
## Clients ## Clients
There are many unofficial GitLab API Clients for most of the popular There are many unofficial GitLab API Clients for most of the popular
......
...@@ -43,6 +43,7 @@ future GitLab releases.** ...@@ -43,6 +43,7 @@ future GitLab releases.**
| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. | | **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` | | **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` |
| **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | | **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled |
| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. |
| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | | **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job |
| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | | **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. |
| **CI_ENVIRONMENT_URL** | 9.3 | all | The URL of the environment for this job | | **CI_ENVIRONMENT_URL** | 9.3 | all | The URL of the environment for this job |
...@@ -74,6 +75,7 @@ future GitLab releases.** ...@@ -74,6 +75,7 @@ future GitLab releases.**
| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | | **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs |
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | | **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | | **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job | | **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | | **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | | **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
......
...@@ -95,6 +95,12 @@ be an array or a multi-line string. ...@@ -95,6 +95,12 @@ be an array or a multi-line string.
`after_script` is used to define the command that will be run after for all `after_script` is used to define the command that will be run after for all
jobs. This has to be an array or a multi-line string. jobs. This has to be an array or a multi-line string.
> **Note:**
The `before_script` and the main `script` are concatenated and run in a single context/container.
The `after_script` is run separately, so depending on the executor, changes done
outside of the working tree might not be visible, e.g. software installed in the
`before_script`.
### stages ### stages
`stages` is used to define stages that can be used by jobs. `stages` is used to define stages that can be used by jobs.
......
...@@ -71,6 +71,9 @@ Vue specific design patterns and practices. ...@@ -71,6 +71,9 @@ Vue specific design patterns and practices.
--- ---
## [Vue Resource](vue_resource.md)
Vue resource specific practices and gotchas.
## [Icons](icons.md) ## [Icons](icons.md)
How we use SVG for our Icons. How we use SVG for our Icons.
......
...@@ -179,6 +179,7 @@ itself, please read this guide: [State Management][state-management] ...@@ -179,6 +179,7 @@ itself, please read this guide: [State Management][state-management]
The Service is a class used only to communicate with the server. The Service is a class used only to communicate with the server.
It does not store or manipulate any data. It is not aware of the store or the components. It does not store or manipulate any data. It is not aware of the store or the components.
We use [vue-resource][vue-resource-repo] to communicate with the server. We use [vue-resource][vue-resource-repo] to communicate with the server.
Refer to [vue resource](vue_resource.md) for more details.
Vue Resource should only be imported in the service file. Vue Resource should only be imported in the service file.
...@@ -189,55 +190,6 @@ Vue Resource should only be imported in the service file. ...@@ -189,55 +190,6 @@ Vue Resource should only be imported in the service file.
Vue.use(VueResource); Vue.use(VueResource);
``` ```
#### Vue-resource gotchas
#### Headers
Headers are being parsed into a plain object in an interceptor.
In Vue-resource 1.x `headers` object was changed into an `Headers` object. In order to not change all old code, an interceptor was added.
If you need to write a unit test that takes the headers in consideration, you need to include an interceptor to parse the headers after your test interceptor.
You can see an example in `spec/javascripts/environments/environment_spec.js`:
```javascript
import { headersInterceptor } from './helpers/vue_resource_helper';
beforeEach(() => {
Vue.http.interceptors.push(myInterceptor);
Vue.http.interceptors.push(headersInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, myInterceptor);
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
```
#### `.json()`
When making a request to the server, you will most likely need to access the body of the response.
Use `.json()` to convert. Because `.json()` returns a Promise the follwoing structure should be used:
```javascript
service.get('url')
.then(resp => resp.json())
.then((data) => {
this.store.storeData(data);
})
.catch(() => new Flash('Something went wrong'));
```
When using `Poll` (`app/assets/javascripts/lib/utils/poll.js`), the `successCallback` needs to handle `.json()` as a Promise:
```javascript
successCallback: (response) => {
return response.json().then((data) => {
// handle the response
});
}
```
#### CSRF token
We use a Vue Resource interceptor to manage the CSRF token.
`app/assets/javascripts/vue_shared/vue_resource_interceptor.js` holds all our common interceptors.
Note: You don't need to load `app/assets/javascripts/vue_shared/vue_resource_interceptor.js`
since it's already being loaded by `common_vue.js`.
### End Result ### End Result
The following example shows an application: The following example shows an application:
...@@ -769,7 +721,6 @@ describe('component', () => { ...@@ -769,7 +721,6 @@ describe('component', () => {
[component-system]: https://vuejs.org/v2/guide/#Composing-with-Components [component-system]: https://vuejs.org/v2/guide/#Composing-with-Components
[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch [state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
[one-way-data-flow]: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow [one-way-data-flow]: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow
[vue-resource-repo]: https://github.com/pagekit/vue-resource
[vue-resource-interceptor]: https://github.com/pagekit/vue-resource/blob/develop/docs/http.md#interceptors [vue-resource-interceptor]: https://github.com/pagekit/vue-resource/blob/develop/docs/http.md#interceptors
[vue-test]: https://vuejs.org/v2/guide/unit-testing.html [vue-test]: https://vuejs.org/v2/guide/unit-testing.html
[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6 [issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
......
# Vue Resouce
In Vue applications we use [vue-resource][vue-resource-repo] to communicate with the server.
## HTTP Status Codes
### `.json()`
When making a request to the server, you will most likely need to access the body of the response.
Use `.json()` to convert. Because `.json()` returns a Promise the follwoing structure should be used:
```javascript
service.get('url')
.then(resp => resp.json())
.then((data) => {
this.store.storeData(data);
})
.catch(() => new Flash('Something went wrong'));
```
When using `Poll` (`app/assets/javascripts/lib/utils/poll.js`), the `successCallback` needs to handle `.json()` as a Promise:
```javascript
successCallback: (response) => {
return response.json().then((data) => {
// handle the response
});
}
```
### 204
Some endpoints - usually `delete` endpoints - return `204` as the success response.
When handling `204 - No Content` responses, we cannot use `.json()` since it tries to parse the non-existant body content.
When handling `204` responses, do not use `.json`, otherwise the promise will throw an error and will enter the `catch` statement:
```javascript
Vue.http.delete('path')
.then(() => {
// success!
})
.catch(() => {
// handle error
})
```
## Headers
Headers are being parsed into a plain object in an interceptor.
In Vue-resource 1.x `headers` object was changed into an `Headers` object. In order to not change all old code, an interceptor was added.
If you need to write a unit test that takes the headers in consideration, you need to include an interceptor to parse the headers after your test interceptor.
You can see an example in `spec/javascripts/environments/environment_spec.js`:
```javascript
import { headersInterceptor } from './helpers/vue_resource_helper';
beforeEach(() => {
Vue.http.interceptors.push(myInterceptor);
Vue.http.interceptors.push(headersInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, myInterceptor);
Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor);
});
```
## CSRF token
We use a Vue Resource interceptor to manage the CSRF token.
`app/assets/javascripts/vue_shared/vue_resource_interceptor.js` holds all our common interceptors.
Note: You don't need to load `app/assets/javascripts/vue_shared/vue_resource_interceptor.js`
since it's already being loaded by `common_vue.js`.
[vue-resource-repo]: https://github.com/pagekit/vue-resource
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment