Commit 0bf399f5 authored by Lin Jen-Shin (godfat)'s avatar Lin Jen-Shin (godfat)

Merge branch 'ce_upstream' into 'master'

CE upstream

Closes #2709, gitaly#654, and gitlab-ce#36417

See merge request gitlab-org/gitlab-ee!3159
parents 732750de 35de9283
......@@ -414,7 +414,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.41.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.42.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false
......
......@@ -297,7 +297,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly-proto (0.41.0)
gitaly-proto (0.42.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
......@@ -349,7 +349,9 @@ GEM
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
google-protobuf (3.4.0.2)
google-protobuf (3.4.1.1)
googleapis-common-protos-types (1.0.0)
google-protobuf (~> 3.0)
googleauth (0.5.3)
faraday (~> 0.12)
jwt (~> 1.4)
......@@ -376,8 +378,9 @@ GEM
rake
grape_logging (1.7.0)
grape
grpc (1.6.0)
grpc (1.6.6)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (~> 0.5.1)
gssapi (1.2.0)
ffi (>= 1.0.1)
......@@ -1062,7 +1065,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.41.0)
gitaly-proto (~> 0.42.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0)
......
......@@ -7,7 +7,7 @@ class BoardService {
this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, {
issues: {
method: 'GET',
url: `${gon.relative_url_root}/boards/${boardId}/issues.json`,
url: `${gon.relative_url_root}/-/boards/${boardId}/issues.json`,
}
});
this.lists = Vue.resource(`${listsEndpoint}{/id}`, {}, {
......@@ -16,7 +16,7 @@ class BoardService {
url: `${listsEndpoint}/generate.json`
}
});
this.issue = Vue.resource(`${gon.relative_url_root}/boards/${boardId}/issues{/id}`, {});
this.issue = Vue.resource(`${gon.relative_url_root}/-/boards/${boardId}/issues{/id}`, {});
this.issues = Vue.resource(`${listsEndpoint}{/id}/issues`, {}, {
bulkUpdate: {
method: 'POST',
......
......@@ -40,6 +40,10 @@ const createFlashEl = (message, type, isInContentWrapper = false) => `
</div>
`;
const removeFlashClickListener = (flashEl, fadeTransition) => {
flashEl.parentNode.addEventListener('click', () => hideFlash(flashEl, fadeTransition));
};
/*
* Flash banner supports different types of Flash configurations
* along with ability to provide actionConfig which can be used to show
......@@ -70,7 +74,7 @@ const createFlash = function createFlash(
flashContainer.innerHTML = createFlashEl(message, type, isInContentWrapper);
const flashEl = flashContainer.querySelector(`.flash-${type}`);
flashEl.addEventListener('click', () => hideFlash(flashEl, fadeTransition));
removeFlashClickListener(flashEl, fadeTransition);
if (actionConfig) {
flashEl.innerHTML += createAction(actionConfig);
......@@ -90,5 +94,6 @@ export {
createFlashEl,
createAction,
hideFlash,
removeFlashClickListener,
};
window.Flash = createFlash;
......@@ -43,16 +43,6 @@
type: 'link',
});
}
if (this.job.retry_path) {
actions.push({
label: 'Retry',
path: this.job.retry_path,
cssClass: 'js-retry-button btn btn-inverted-secondary visible-md-block visible-lg-block',
type: 'ujs-link',
});
}
return actions;
},
},
......
......@@ -403,7 +403,11 @@ export const setCiStatusFavicon = (pageUrl) => {
});
};
export const spriteIcon = icon => `<svg><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
export const spriteIcon = (icon, className = '') => {
const classAttribute = className.length > 0 ? `class="${className}"` : '';
return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
};
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
......
......@@ -54,7 +54,7 @@ import './diff';
import './dropzone_input';
import './due_date_select';
import './files_comment_button';
import Flash from './flash';
import Flash, { removeFlashClickListener } from './flash';
import './gl_dropdown';
import './gl_field_error';
import './gl_field_errors';
......@@ -344,4 +344,9 @@ $(function () {
* EE specific scripts
*/
$('#modal-upload-trial-license').modal('show');
const flashContainer = document.querySelector('.flash-container');
if (flashContainer && flashContainer.children.length) {
removeFlashClickListener(flashContainer.children[0]);
}
});
......@@ -22,7 +22,8 @@ const RepoEditor = {
const monacoInstance = Helper.monaco.editor.create(this.$el, {
model: null,
readOnly: false,
contextmenu: false,
contextmenu: true,
scrollBeyondLastLine: false,
});
Helper.monacoInstance = monacoInstance;
......
......@@ -838,6 +838,7 @@
a {
padding: 8px 40px;
&.is-indeterminate::before,
&.is-active::before {
left: 16px;
}
......
......@@ -10,6 +10,10 @@
border: 0;
}
&.file-holder-bottom-radius {
border-radius: 0 0 $border-radius-small $border-radius-small;
}
&.readme-holder {
margin: $gl-padding 0;
......
......@@ -234,6 +234,7 @@ $container-text-max-width: 540px;
$gl-avatar-size: 40px;
$error-exclamation-point: $red-500;
$border-radius-default: 4px;
$border-radius-small: 2px;
$settings-icon-size: 18px;
$provider-btn-not-active-color: $blue-500;
$link-underline-blue: $blue-500;
......
......@@ -4,7 +4,7 @@
border-right: 1px solid $border-color;
border-left: 1px solid $border-color;
border-bottom: none;
border-radius: 2px;
border-radius: $border-radius-small $border-radius-small 0 0;
background: $gray-normal;
}
......
......@@ -117,7 +117,7 @@
}
.right-sidebar {
a,
a:not(.btn-retry),
.btn-link {
color: inherit;
}
......@@ -459,7 +459,7 @@
}
}
a {
a:not(.btn-retry) {
&:hover {
color: $md-link-color;
text-decoration: none;
......
......@@ -7,6 +7,9 @@ module Gcp
belongs_to :user
belongs_to :service
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
default_value_for :gcp_cluster_zone, 'us-central1-a'
default_value_for :gcp_cluster_size, 3
default_value_for :gcp_machine_type, 'n1-standard-4'
......
......@@ -404,7 +404,7 @@ class MergeRequest < ActiveRecord::Base
end
def merge_ongoing?
!!merge_jid && !merged?
!!merge_jid && !merged? && Gitlab::SidekiqStatus.running?(merge_jid)
end
def closed_without_fork?
......
......@@ -28,6 +28,8 @@ module Ci
attributes.push([:user, current_user])
build.retried = true
Ci::Build.transaction do
# mark all other builds of that name as retried
build.pipeline.builds.latest
......
......@@ -3,7 +3,7 @@ module MergeRequests
# Adds a todo to the parent merge_request when a CI build fails
#
def execute(commit_status)
return if commit_status.allow_failure?
return if commit_status.allow_failure? || commit_status.retried?
commit_status_merge_requests(commit_status) do |merge_request|
todo_service.merge_request_build_failed(merge_request)
......
......@@ -15,10 +15,6 @@
%tr
%th
%th Global Shortcuts
%tr
%td.shortcut
.key n
%td Main Navigation
%tr
%td.shortcut
.key s
......
- action = current_action?(:edit) || current_action?(:update) ? 'edit' : 'create'
.file-holder.file.append-bottom-default
.file-holder-bottom-radius.file-holder.file.append-bottom-default
.js-file-title.file-title.clearfix{ data: { current_action: action } }
.editor-ref
= icon('code-fork')
......
......@@ -4,8 +4,10 @@
.sidebar-container
.blocks-container
.block
%strong
%strong.prepend-top-10
= @build.name
- if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_job_path(@project.namespace, @project, @build), class: 'js-retry-button pull-right btn btn-inverted-secondary btn-retry visible-md-block visible-lg-block', method: :post
%a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-build-toggle{ href: "#", 'aria-label': 'Toggle Sidebar', role: 'button' }
= icon('angle-double-right')
......
......@@ -54,6 +54,10 @@
= f.label :visibility_level, class: 'label-light' do #the label here seems wrong
Import project from
.import-buttons
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
%div
- if github_import_enabled?
= link_to new_import_github_path, class: 'btn import_github' do
......@@ -87,10 +91,6 @@
- if git_import_enabled?
%button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL')
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
.col-lg-12
.js-toggle-content.hide.toggle-import-form
%hr
......
......@@ -28,6 +28,7 @@ class ChangelogOptionParser
Type.new('deprecated', 'New deprecation'),
Type.new('removed', 'Feature removal'),
Type.new('security', 'Security fix'),
Type.new('performance', 'Performance improvement'),
Type.new('other', 'Other')
].freeze
TYPES_OFFSET = 1
......
---
title: Move retry button in job page to sidebar
merge_request:
author:
type: fixed
---
title: Replace WikiPage::CreateService calls with wiki_page factory in specs
merge_request: 14850
author: Jacopo Beschi @jacopo-beschi
type: changed
---
title: Removed extra border radius from .file-editor and .file-holder when editing
a file
merge_request: 14803
author: Rachel Pipkin
type: fixed
---
title: Don't create build failed todos when the job is automatically retried
merge_request:
author:
type: fixed
---
title: Make usage ping scheduling more robust
merge_request:
author:
type: fixed
---
title: Make "merge ongoing" check more consistent
merge_request:
author:
type: fixed
---
title: 14830 Move GitLab export option to top of import list when creating a new project
merge_request:
author:
type: changed
---
title: Fix diff parser so it tolerates to diff special markers in the content
merge_request:
author:
type: fixed
---
title: Allow boards as top level route
merge_request:
author:
type: fixed
---
title: Fix 404 errors in API caused when the branch name had a dot
merge_request: 14462
author: gvieira37
type: fixed
---
title: Fix alignment for indeterminate marker in dropdowns
merge_request: 14809
author:
type: fixed
---
title: Add Performance improvement as category on the changelog
merge_request:
author:
type: performance
......@@ -138,12 +138,14 @@ class Settings < Settingslogic
URI.parse(url_without_path).host
end
# Random cron time every Sunday to load balance usage pings
def cron_random_weekly_time
# Runs every minute in a random ten-minute period on Sundays, to balance the
# load on the server receiving these pings. The usage ping is safe to run
# multiple times because of a 24 hour exclusive lock.
def cron_for_usage_ping
hour = rand(24)
minute = rand(60)
minute = rand(6)
"#{minute} #{hour} * * 0"
"#{minute}0-#{minute}9 #{hour} * * 0"
end
end
end
......@@ -473,7 +475,7 @@ Settings.cron_jobs['stuck_import_jobs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['stuck_import_jobs_worker']['cron'] ||= '15 * * * *'
Settings.cron_jobs['stuck_import_jobs_worker']['job_class'] = 'StuckImportJobsWorker'
Settings.cron_jobs['gitlab_usage_ping_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['gitlab_usage_ping_worker']['cron'] ||= Settings.__send__(:cron_random_weekly_time)
Settings.cron_jobs['gitlab_usage_ping_worker']['cron'] ||= Settings.__send__(:cron_for_usage_ping)
Settings.cron_jobs['gitlab_usage_ping_worker']['job_class'] = 'GitlabUsagePingWorker'
Settings.cron_jobs['schedule_update_user_activity_worker'] ||= Settingslogic.new({})
......
......@@ -59,6 +59,19 @@ Rails.application.routes.draw do
get 'readiness' => 'health#readiness'
resources :metrics, only: [:index]
mount Peek::Railtie => '/peek'
# Boards resources shared between group and projects
resources :boards, only: [] do
resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do
collection do
post :generate
end
resources :issues, only: [:index, :create, :update]
end
resources :issues, module: :boards, only: [:index, :update]
end
end
# Koding route
......@@ -89,19 +102,6 @@ Rails.application.routes.draw do
# Notification settings
resources :notification_settings, only: [:create, :update]
# Boards resources shared between group and projects
resources :boards do
resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do
collection do
post :generate
end
resources :issues, only: [:index, :create, :update]
end
resources :issues, module: :boards, only: [:index, :update]
end
draw :google_api
draw :import
draw :uploads
......
......@@ -248,7 +248,7 @@ var config = {
from: path.join(ROOT_PATH, `node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`),
to: 'monaco-editor/vs',
transform: function(content, path) {
if (/\.js$/.test(path) && !/worker/i.test(path)) {
if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
return (
'(function(){\n' +
'var define = this.define, require = this.require;\n' +
......
......@@ -85,7 +85,7 @@ GET /projects/:id/repository/blobs/:sha
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (required) - The commit or branch name
- `sha` (required) - The blob SHA
## Raw blob content
......
This diff is collapsed.
......@@ -42,7 +42,7 @@ digging into specific reference guides.
- **The permissions model** - Learn about the access levels a user can have for
performing certain CI actions
- [User permissions](../user/permissions.md#gitlab-ci)
- [Jobs permissions](../user/permissions.md#jobs-permissions)
- [Job permissions](../user/permissions.md#job-permissions)
## Auto DevOps
......
......@@ -17,7 +17,7 @@
[Project Templates](https://gitlab.com/gitlab-org/project-templates):
this will kickstart your repository code and CI automatically.
Otherwise, if you have a project in a different repository, you can [import it] by
clicking an **Import project from** button provided this is enabled in
clicking on the **Import project** tab, provided this is enabled in
your GitLab instance. Ask your administrator if not.
1. Provide the following information:
......
......@@ -123,8 +123,8 @@ Existing users using GitLab with MySQL/MariaDB are advised to
### PostgreSQL Requirements
As of GitLab 9.3, PostgreSQL 9.2 or newer is required, and earlier versions are
not supported. We highly recommend users to use at least PostgreSQL 9.6 as this
As of GitLab 10.0, PostgreSQL 9.6 or newer is required, and earlier versions are
not supported. We highly recommend users to use PostgreSQL 9.6 as this
is the PostgreSQL version used for development and testing.
Users using PostgreSQL must ensure the `pg_trgm` extension is loaded into every
......
......@@ -19,7 +19,7 @@ When not specified, the default issue closing pattern as shown below will be
used:
```bash
((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)
((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)
```
Note that `%{issue_ref}` is a complex regular expression defined inside GitLab's
......@@ -34,6 +34,7 @@ This translates to the following keywords:
- Close, Closes, Closed, Closing, close, closes, closed, closing
- Fix, Fixes, Fixed, Fixing, fix, fixes, fixed, fixing
- Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
- Implement, Implements, Implemented, Implementing, implement, implements, implemented, implementing
---
......
......@@ -4,6 +4,10 @@ module API
LOG_FILENAME = Rails.root.join("log", "api_json.log")
NO_SLASH_URL_PART_REGEX = %r{[^/]+}
PROJECT_ENDPOINT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze
COMMIT_ENDPOINT_REQUIREMENTS = PROJECT_ENDPOINT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze
use GrapeLogging::Middleware::RequestLogger,
logger: Logger.new(LOG_FILENAME),
formatter: Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new,
......@@ -105,9 +109,6 @@ module API
helpers ::API::Helpers
helpers ::API::Helpers::CommonHelpers
NO_SLASH_URL_PART_REGEX = %r{[^/]+}
PROJECT_ENDPOINT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze
# Keep in alphabetical order
mount ::API::AccessRequests
mount ::API::AwardEmoji
......
......@@ -4,8 +4,6 @@ module API
class Commits < Grape::API
include PaginationParams
COMMIT_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(sha: API::NO_SLASH_URL_PART_REGEX)
before { authorize! :download_code, user_project }
params do
......@@ -85,7 +83,7 @@ module API
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
get ':id/repository/commits/:sha', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -99,7 +97,7 @@ module API
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha/diff', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -115,7 +113,7 @@ module API
use :pagination
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha/comments', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -132,7 +130,7 @@ module API
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked'
requires :branch, type: String, desc: 'The name of the branch'
end
post ':id/repository/commits/:sha/cherry_pick', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
commit = user_project.commit(params[:sha])
......@@ -169,7 +167,7 @@ module API
requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
end
end
post ':id/repository/commits/:sha/comments', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......
......@@ -57,7 +57,7 @@ module API
desc 'Get raw blob contents from the repository'
params do
requires :sha, type: String, desc: 'The commit, branch name, or tag name'
requires :sha, type: String, desc: 'The commit hash'
end
get ':id/repository/blobs/:sha/raw' do
assign_blob_vars!
......@@ -67,7 +67,7 @@ module API
desc 'Get a blob from the repository'
params do
requires :sha, type: String, desc: 'The commit, branch name, or tag name'
requires :sha, type: String, desc: 'The commit hash'
end
get ':id/repository/blobs/:sha' do
assign_blob_vars!
......
......@@ -8,7 +8,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do
params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
......
......@@ -11,7 +11,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: { id: %r{[^/]+} } do
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a project repository commits' do
success ::API::Entities::Commit
end
......@@ -72,7 +72,7 @@ module API
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ":id/repository/commits/:sha" do
get ":id/repository/commits/:sha", requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! "Commit" unless commit
......@@ -86,7 +86,7 @@ module API
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ":id/repository/commits/:sha/diff" do
get ":id/repository/commits/:sha/diff", requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! "Commit" unless commit
......@@ -102,7 +102,7 @@ module API
use :pagination
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha/comments' do
get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -119,7 +119,7 @@ module API
requires :sha, type: String, desc: 'A commit sha to be cherry picked'
requires :branch, type: String, desc: 'The name of the branch'
end
post ':id/repository/commits/:sha/cherry_pick' do
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
commit = user_project.commit(params[:sha])
......@@ -156,7 +156,7 @@ module API
requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
end
end
post ':id/repository/commits/:sha/comments' do
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......
......@@ -8,7 +8,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: { id: %r{[^/]+} } do
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
......@@ -43,7 +43,7 @@ module API
requires :sha, type: String, desc: 'The commit, branch name, or tag name'
requires :filepath, type: String, desc: 'The path to the file to display'
end
get [":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob"] do
get [":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob"], requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
repo = user_project.repository
commit = repo.commit(params[:sha])
not_found! "Commit" unless commit
......@@ -56,7 +56,7 @@ module API
params do
requires :sha, type: String, desc: 'The commit, branch name, or tag name'
end
get ':id/repository/raw_blobs/:sha' do
get ':id/repository/raw_blobs/:sha', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
repo = user_project.repository
begin
blob = Gitlab::Git::Blob.raw(repo, params[:sha])
......
......@@ -17,7 +17,9 @@ module Gitlab
# without having to instantiate all the others that come after it.
Enumerator.new do |yielder|
@lines.each do |line|
next if filename?(line)
# We're expecting a filename parameter only in a meta-part of the diff content
# when type is defined then we're already in a content-part
next if filename?(line) && type.nil?
full_line = line.delete("\n")
......
......@@ -3,9 +3,17 @@ module Gitlab
class OperationService
include Gitlab::Git::Popen
WithBranchResult = Struct.new(:newrev, :repo_created, :branch_created) do
BranchUpdate = Struct.new(:newrev, :repo_created, :branch_created) do
alias_method :repo_created?, :repo_created
alias_method :branch_created?, :branch_created
def self.from_gitaly(branch_update)
new(
branch_update.commit_id,
branch_update.repo_created,
branch_update.branch_created
)
end
end
attr_reader :user, :repository
......@@ -117,7 +125,7 @@ module Gitlab
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
update_ref_in_hooks(ref, newrev, oldrev)
WithBranchResult.new(newrev, was_empty, was_empty || Gitlab::Git.blank_ref?(oldrev))
BranchUpdate.new(newrev, was_empty, was_empty || Gitlab::Git.blank_ref?(oldrev))
end
def find_oldrev_from_branch(newrev, branch)
......
......@@ -704,7 +704,17 @@ module Gitlab
tags.find { |tag| tag.name == name }
end
def merge(user, source_sha, target_branch, message)
def merge(user, source_sha, target_branch, message, &block)
gitaly_migrate(:operation_user_merge_branch) do |is_enabled|
if is_enabled
gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
else
rugged_merge(user, source_sha, target_branch, message, &block)
end
end
end
def rugged_merge(user, source_sha, target_branch, message)
committer = Gitlab::Git.committer_hash(email: user.email, name: user.name)
OperationService.new(user, self).with_branch(target_branch) do |start_commit|
......
......@@ -70,15 +70,27 @@ module Gitlab
# All Gitaly RPC call sites should use GitalyClient.call. This method
# makes sure that per-request authentication headers are set.
#
# This method optionally takes a block which receives the keyword
# arguments hash 'kwargs' that will be passed to gRPC. This allows the
# caller to modify or augment the keyword arguments. The block must
# return a hash.
#
# For example:
#
# GitalyClient.call(storage, service, rpc, request) do |kwargs|
# kwargs.merge(deadline: Time.now + 10)
# end
#
def self.call(storage, service, rpc, request)
enforce_gitaly_request_limits(:call)
metadata = request_metadata(storage)
metadata = yield(metadata) if block_given?
stub(service, storage).__send__(rpc, request, metadata) # rubocop:disable GitlabSecurity/PublicSend
kwargs = request_kwargs(storage)
kwargs = yield(kwargs) if block_given?
stub(service, storage).__send__(rpc, request, kwargs) # rubocop:disable GitlabSecurity/PublicSend
end
def self.request_metadata(storage)
def self.request_kwargs(storage)
encoded_token = Base64.strict_encode64(token(storage).to_s)
metadata = {
'authorization' => "Bearer #{encoded_token}",
......
......@@ -74,6 +74,37 @@ module Gitlab
raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
end
end
def user_merge_branch(user, source_sha, target_branch, message)
request_enum = QueueEnumerator.new
response_enum = GitalyClient.call(
@repository.storage,
:operation_service,
:user_merge_branch,
request_enum.each
)
request_enum.push(
Gitaly::UserMergeBranchRequest.new(
repository: @gitaly_repo,
user: Util.gitaly_user(user),
commit_id: source_sha,
branch: GitalyClient.encode(target_branch),
message: GitalyClient.encode(message)
)
)
yield response_enum.next.commit_id
request_enum.push(Gitaly::UserMergeBranchRequest.new(apply: true))
branch_update = response_enum.next.branch_update
raise Gitlab::Git::CommitError.new('failed to apply merge to branch') unless branch_update.commit_id.present?
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
ensure
request_enum.close
end
end
end
end
module Gitlab
module GitalyClient
class QueueEnumerator
def initialize
@queue = Queue.new
end
def push(elem)
@queue << elem
end
def close
push(:close)
end
def each
return enum_for(:each) unless block_given?
loop do
elem = @queue.pop
break if elem == :close
yield elem
end
end
end
end
end
......@@ -26,7 +26,6 @@ module Gitlab
apple-touch-icon.png
assets
autocomplete
boards
ci
dashboard
deploy.html
......
......@@ -51,6 +51,13 @@ module Gitlab
self.num_running(job_ids).zero?
end
# Returns true if the given job is running
#
# job_id - The Sidekiq job ID to check.
def self.running?(job_id)
num_running([job_id]) > 0
end
# Returns the number of jobs that are running.
#
# job_ids - The Sidekiq job IDs to check.
......
......@@ -45,6 +45,7 @@ module Gitlab
usage_data
end
# rubocop:disable Metrics/AbcSize
def system_usage_data
{
counts: {
......@@ -64,6 +65,8 @@ module Gitlab
environments: ::Environment.count,
gcp_clusters: ::Gcp::Cluster.count,
geo_nodes: GeoNode.count,
gcp_clusters_enabled: ::Gcp::Cluster.enabled.count,
gcp_clusters_disabled: ::Gcp::Cluster.disabled.count,
in_review_folder: ::Environment.in_review_folder.count,
groups: Group.count,
issues: Issue.count,
......
......@@ -18,6 +18,7 @@ module QA
# Support files
#
autoload :Actable, 'qa/scenario/actable'
autoload :Entrypoint, 'qa/scenario/entrypoint'
autoload :Template, 'qa/scenario/template'
##
......@@ -25,6 +26,10 @@ module QA
#
module Test
autoload :Instance, 'qa/scenario/test/instance'
module Integration
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
end
end
##
......
module QA
module Scenario
##
# Base class for running the suite against any GitLab instance,
# including staging and on-premises installation.
#
class Entrypoint < Template
def self.tags(*tags)
@tags = tags
end
def self.get_tags
@tags
end
def perform(address, *files)
Specs::Config.perform do |specs|
specs.address = address
end
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
Specs::Runner.perform do |specs|
specs.rspec(
tty: true,
tags: self.class.get_tags,
files: files.any? ? files : 'qa/specs/features'
)
end
end
end
end
end
......@@ -5,21 +5,8 @@ module QA
# Run test suite against any GitLab instance,
# including staging and on-premises installation.
#
class Instance < Scenario::Template
def perform(address, *files)
Specs::Config.perform do |specs|
specs.address = address
end
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
Specs::Runner.perform do |specs|
specs.rspec('--tty', files.any? ? files : 'qa/specs/features')
end
end
class Instance < Entrypoint
tags :core
end
end
end
......
module QA
module Scenario
module Test
module Integration
##
# Run test suite against any GitLab instance where mattermost is enabled,
# including staging and on-premises installation.
#
class Mattermost < Scenario::Entrypoint
tags :core, :mattermost
end
end
end
end
end
module QA
feature 'standard root login' do
feature 'standard root login', :core do
scenario 'user logs in using credentials' do
Page::Main::Entry.act { sign_in_using_credentials }
......
module QA
feature 'create a new group', :mattermost do
scenario 'creating a group with a mattermost team' do
Page::Main::Entry.act { sign_in_using_credentials }
Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
page.go_to_new_group
expect(page).to have_content(
/Create a Mattermost team for this group/
)
end
end
end
end
module QA
feature 'create a new project' do
feature 'create a new project', :core do
scenario 'user creates a new project' do
Page::Main::Entry.act { sign_in_using_credentials }
......
module QA
feature 'clone code from the repository' do
feature 'clone code from the repository', :core do
context 'with regular account over http' do
given(:location) do
Page::Project::Show.act do
......
module QA
feature 'push code to repository' do
feature 'push code to repository', :core do
context 'with regular account over http' do
scenario 'user pushes code to the repository' do
Page::Main::Entry.act { sign_in_using_credentials }
......
......@@ -5,7 +5,14 @@ module QA
class Runner
include Scenario::Actable
def rspec(*args)
def rspec(tty: false, tags: [], files: ['qa/specs/features'])
args = []
args << '--tty' if tty
tags.to_a.each do |tag|
args << ['-t', tag.to_s]
end
args << files
RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status|
abort if status.nonzero?
end
......
......@@ -84,7 +84,7 @@ describe 'bin/changelog' do
expect do
expect do
expect { described_class.read_type }.to raise_error(SystemExit)
end.to output("Invalid category index, please select an index between 1 and 7\n").to_stderr
end.to output("Invalid category index, please select an index between 1 and 8\n").to_stderr
end.to output.to_stdout
end
end
......
......@@ -14,7 +14,6 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
background do
project.team << [user, :master]
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
sign_in(user)
......
......@@ -3,9 +3,7 @@ require 'spec_helper'
feature 'Wiki shortcuts', :js do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:wiki_page) do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
end
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: 'Home page' }) }
before do
sign_in(user)
......
......@@ -3,14 +3,7 @@ require 'spec_helper'
describe 'Projects > Wiki > User views Git access wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:wiki_page) do
WikiPages::CreateService.new(
project,
user,
title: 'home',
content: '[some link](other-page)'
).execute
end
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' }) }
before do
sign_in(user)
......
......@@ -18,12 +18,7 @@ describe 'Projects > Wiki > User views wiki in project page' do
context 'when wiki homepage contains a link' do
before do
WikiPages::CreateService.new(
project,
user,
title: 'home',
content: '[some link](other-page)'
).execute
create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' })
end
it 'displays the correct URL for the link' do
......
No preview for this file type
......@@ -2,6 +2,7 @@ import flash, {
createFlashEl,
createAction,
hideFlash,
removeFlashClickListener,
} from '~/flash';
describe('Flash', () => {
......@@ -266,4 +267,24 @@ describe('Flash', () => {
});
});
});
describe('removeFlashClickListener', () => {
beforeEach(() => {
document.body.innerHTML += '<div class="flash-container"><div class="flash"></div></div>';
});
it('removes global flash on click', (done) => {
const flashEl = document.querySelector('.flash');
removeFlashClickListener(flashEl, false);
flashEl.parentNode.click();
setTimeout(() => {
expect(document.querySelector('.flash')).toBeNull();
done();
});
});
});
});
......@@ -30,7 +30,6 @@ describe('Job details header', () => {
email: 'foo@bar.com',
avatar_url: 'link',
},
retry_path: 'path',
new_issue_path: 'path',
},
isLoading: false,
......@@ -49,12 +48,6 @@ describe('Job details header', () => {
).toEqual('failed Job #123 triggered 3 weeks ago by Foo');
});
it('should render retry link', () => {
expect(
vm.$el.querySelector('.js-retry-button').getAttribute('href'),
).toEqual(props.job.retry_path);
});
it('should render new issue link', () => {
expect(
vm.$el.querySelector('.js-new-issue').getAttribute('href'),
......
......@@ -474,7 +474,11 @@ describe('common_utils', () => {
});
it('should return the svg for a linked icon', () => {
expect(gl.utils.spriteIcon('test')).toEqual('<svg><use xlink:href="icons.svg#test" /></svg>');
expect(gl.utils.spriteIcon('test')).toEqual('<svg ><use xlink:href="icons.svg#test" /></svg>');
});
it('should set svg className when passed', () => {
expect(gl.utils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
});
});
});
......
......@@ -143,4 +143,21 @@ eos
it { expect(parser.parse([])).to eq([]) }
it { expect(parser.parse(nil)).to eq([]) }
end
describe 'tolerates special diff markers in a content' do
it "counts lines correctly" do
diff = <<~END
--- a/test
+++ b/test
@@ -1,2 +1,2 @@
+ipsum
+++ b
-ipsum
END
lines = parser.parse(diff.lines).to_a
expect(lines.size).to eq(3)
end
end
end
......@@ -1525,6 +1525,45 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' }
let(:user) { build(:user) }
let(:target_branch) { 'test-merge-target-branch' }
before do
repository.create_branch(target_branch, '6d394385cf567f80a8fd85055db1ab4c5295806f')
end
after do
FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH)
ensure_seeds
end
shared_examples '#merge' do
it 'can perform a merge' do
merge_commit_id = nil
result = repository.merge(user, source_sha, target_branch, 'Test merge') do |commit_id|
merge_commit_id = commit_id
end
expect(result.newrev).to eq(merge_commit_id)
expect(result.repo_created).to eq(false)
expect(result.branch_created).to eq(false)
end
end
context 'with gitaly' do
it_behaves_like '#merge'
end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#merge'
end
end
def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
rugged = repository.rugged
......
......@@ -39,6 +39,18 @@ describe Gitlab::SidekiqStatus do
end
end
describe '.running?', :clean_gitlab_redis_shared_state do
it 'returns true if job is running' do
described_class.set('123')
expect(described_class.running?('123')).to be(true)
end
it 'returns false if job is not found' do
expect(described_class.running?('123')).to be(false)
end
end
describe '.num_running', :clean_gitlab_redis_shared_state do
it 'returns 0 if all jobs have been completed' do
expect(described_class.num_running(%w(123))).to eq(0)
......
......@@ -73,6 +73,8 @@ describe Gitlab::UsageData do
environments
gcp_clusters
geo_nodes
gcp_clusters_enabled
gcp_clusters_disabled
in_review_folder
groups
issues
......
......@@ -37,7 +37,7 @@ describe BlobViewer::Readme do
context 'when the wiki is not empty' do
before do
WikiPages::CreateService.new(project, project.owner, title: 'home', content: 'Home page').execute
create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: 'Home page' })
end
it 'returns nil' do
......
......@@ -1762,19 +1762,34 @@ describe Ci::Build do
end
describe 'state transition when build fails' do
let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user) }
before do
allow(MergeRequests::AddTodoWhenBuildFailsService).to receive(:new).and_return(service)
allow(service).to receive(:close)
end
context 'when build is configured to be retried' do
subject { create(:ci_build, :running, options: { retry: 3 }) }
subject { create(:ci_build, :running, options: { retry: 3 }, project: project, user: user) }
it 'retries builds and assigns a same user to it' do
it 'retries build and assigns the same user to it' do
expect(described_class).to receive(:retry)
.with(subject, subject.user)
.with(subject, user)
subject.drop!
end
it 'does not try to create a todo' do
project.add_developer(user)
expect(service).not_to receive(:commit_status_merge_requests)
subject.drop!
end
end
context 'when build is not configured to be retried' do
subject { create(:ci_build, :running) }
subject { create(:ci_build, :running, project: project, user: user) }
it 'does not retry build' do
expect(described_class).not_to receive(:retry)
......@@ -1789,6 +1804,14 @@ describe Ci::Build do
subject.drop!
end
it 'creates a todo' do
project.add_developer(user)
expect(service).to receive(:commit_status_merge_requests)
subject.drop!
end
end
end
end
......@@ -7,6 +7,30 @@ describe Gcp::Cluster do
it { is_expected.to validate_presence_of(:gcp_cluster_zone) }
describe '.enabled' do
subject { described_class.enabled }
let!(:cluster) { create(:gcp_cluster, enabled: true) }
before do
create(:gcp_cluster, enabled: false)
end
it { is_expected.to contain_exactly(cluster) }
end
describe '.disabled' do
subject { described_class.disabled }
let!(:cluster) { create(:gcp_cluster, enabled: false) }
before do
create(:gcp_cluster, enabled: true)
end
it { is_expected.to contain_exactly(cluster) }
end
describe '#default_value_for' do
let(:cluster) { described_class.new }
......
......@@ -1887,11 +1887,31 @@ describe MergeRequest do
end
describe '#merge_ongoing?' do
it 'returns true when merge_id is present and MR is not merged' do
it 'returns true when merge_id, MR is not merged and it has no running job' do
merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo')
allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { true }
expect(merge_request.merge_ongoing?).to be(true)
end
it 'returns false when merge_jid is nil' do
merge_request = build_stubbed(:merge_request, state: :open, merge_jid: nil)
expect(merge_request.merge_ongoing?).to be(false)
end
it 'returns false if MR is merged' do
merge_request = build_stubbed(:merge_request, state: :merged, merge_jid: 'foo')
expect(merge_request.merge_ongoing?).to be(false)
end
it 'returns false if there is no merge job running' do
merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo')
allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { false }
expect(merge_request.merge_ongoing?).to be(false)
end
end
describe "#closed_without_fork?" do
......
......@@ -108,12 +108,8 @@ describe MicrosoftTeamsService do
message: "user created page: Awesome wiki_page"
}
end
let(:wiki_page_sample_data) do
service = WikiPages::CreateService.new(project, user, opts)
wiki_page = service.execute
Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create')
end
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
let(:wiki_page_sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
it "calls Microsoft Teams API" do
chat_service.execute(wiki_page_sample_data)
......
......@@ -1286,6 +1286,7 @@ describe Repository do
let(:message) { 'Test \r\n\r\n message' }
shared_examples '#merge' do
it 'merges the code and returns the commit id' do
expect(merge_commit).to be_present
expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
......@@ -1302,6 +1303,15 @@ describe Repository do
expect(repository.commit(merge_commit_id).message).to eq(message.delete("\r"))
end
end
context 'with gitaly' do
it_behaves_like '#merge'
end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#merge'
end
def merge(repository, user, merge_request, message)
repository.merge(user, merge_request.diff_head_sha, merge_request, message)
......
......@@ -97,10 +97,11 @@ describe API::V3::Repositories do
end
end
{
'blobs/:sha' => 'blobs/master',
'commits/:sha/blob' => 'commits/master/blob'
}.each do |desc_path, example_path|
[
['blobs/:sha', 'blobs/master'],
['blobs/:sha', 'blobs/v1.1.0'],
['commits/:sha/blob', 'commits/master/blob']
].each do |desc_path, example_path|
describe "GET /projects/:id/repository/#{desc_path}" do
let(:route) { "/projects/#{project.id}/repository/#{example_path}?filepath=README.md" }
shared_examples_for 'repository blob' do
......@@ -110,7 +111,7 @@ describe API::V3::Repositories do
end
context 'when sha does not exist' do
it_behaves_like '404 response' do
let(:request) { get v3_api(route.sub('master', 'invalid_branch_name'), current_user) }
let(:request) { get v3_api("/projects/#{project.id}/repository/#{desc_path.sub(':sha', 'invalid_branch_name')}?filepath=README.md", current_user) }
let(:message) { '404 Commit Not Found' }
end
end
......
......@@ -161,8 +161,9 @@ describe Ci::RetryBuildService do
expect(new_build).to be_created
end
it 'does mark old build as retried' do
it 'does mark old build as retried in the database and on the instance' do
expect(new_build).to be_latest
expect(build).to be_retried
expect(build.reload).to be_retried
end
end
......
......@@ -52,6 +52,11 @@ describe Projects::UpdatePagesService do
expect(project.pages_deployed?).to be_falsey
expect(execute).to eq(:success)
expect(project.pages_deployed?).to be_truthy
# Check that all expected files are extracted
%w[index.html zero .hidden/file].each do |filename|
expect(File.exist?(File.join(project.public_pages_path, filename))).to be_truthy
end
end
it 'limits pages size' do
......
......@@ -76,8 +76,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
message: "user created page: Awesome wiki_page"
}
wiki_page_service = WikiPages::CreateService.new(project, user, opts)
@wiki_page = wiki_page_service.execute
@wiki_page = create(:wiki_page, wiki: project.wiki, attrs: opts)
@wiki_page_sample_data = Gitlab::DataBuilder::WikiPage.build(@wiki_page, user, 'create')
end
......
......@@ -4148,9 +4148,9 @@ moment@2.x:
version "2.17.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
monaco-editor@0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.8.3.tgz#523bdf2d1524db2c2dfc3cae0a7b6edc48d6dea6"
monaco-editor@0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.10.0.tgz#6604932585fe9c1f993f000a503d0d20fbe5896a"
mousetrap@^1.4.6:
version "1.4.6"
......@@ -6405,9 +6405,9 @@ vue@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.6.tgz#451714b394dd6d4eae7b773c40c2034a59621aed"
vuex@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-2.3.1.tgz#cde8e997c1f9957719bc7dea154f9aa691d981a6"
vuex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.0.tgz#98b4b5c4954b1c1c1f5b29fa0476a23580315814"
watchpack@^1.4.0:
version "1.4.0"
......
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