Skip to content
Snippets Groups Projects
Commit fca89bb7 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 76e9fc7b
No related branches found
No related tags found
No related merge requests found
Showing
with 191 additions and 45 deletions
Loading
Loading
@@ -84,7 +84,6 @@ export default () => {
rootPath: $boardApp.dataset.rootPath,
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
detailIssue: boardsStore.detail,
defaultAvatar: $boardApp.dataset.defaultAvatar,
},
computed: {
detailIssueVisible() {
Loading
Loading
@@ -130,13 +129,10 @@ export default () => {
position = -1;
}
 
boardsStore.addList(
{
...listObj,
position,
},
this.defaultAvatar,
);
boardsStore.addList({
...listObj,
position,
});
});
 
boardsStore.addBlankState();
Loading
Loading
export default class ListAssignee {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.id = obj.id;
this.name = obj.name;
this.username = obj.username;
this.avatar = obj.avatar_url || obj.avatar || defaultAvatar;
this.avatar = obj.avatar_url || obj.avatar || gon.default_avatar_url;
this.path = obj.path;
this.state = obj.state;
this.webUrl = obj.web_url || obj.webUrl;
Loading
Loading
Loading
Loading
@@ -10,7 +10,7 @@ import IssueProject from './project';
import boardsStore from '../stores/boards_store';
 
class ListIssue {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.subscribed = obj.subscribed;
this.labels = [];
this.assignees = [];
Loading
Loading
@@ -22,11 +22,11 @@ class ListIssue {
this.closed = obj.closed;
this.isLoading = {};
 
this.refreshData(obj, defaultAvatar);
this.refreshData(obj);
}
 
refreshData(obj, defaultAvatar) {
boardsStore.refreshIssueData(this, obj, defaultAvatar);
refreshData(obj) {
boardsStore.refreshIssueData(this, obj);
}
 
addLabel(label) {
Loading
Loading
Loading
Loading
@@ -36,7 +36,7 @@ const TYPES = {
};
 
class List {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
Loading
Loading
@@ -55,7 +55,6 @@ class List {
this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count')
? obj.max_issue_count
: 0;
this.defaultAvatar = defaultAvatar;
 
if (obj.label) {
this.label = new ListLabel(obj.label);
Loading
Loading
@@ -156,7 +155,7 @@ class List {
 
createIssues(data) {
data.forEach(issueObj => {
this.addIssue(new ListIssue(issueObj, this.defaultAvatar));
this.addIssue(new ListIssue(issueObj));
});
}
 
Loading
Loading
Loading
Loading
@@ -74,8 +74,8 @@ const boardsStore = {
showPage(page) {
this.state.currentPage = page;
},
addList(listObj, defaultAvatar) {
const list = new List(listObj, defaultAvatar);
addList(listObj) {
const list = new List(listObj);
this.state.lists = _.sortBy([...this.state.lists, list], 'position');
 
return list;
Loading
Loading
@@ -602,7 +602,7 @@ const boardsStore = {
clearMultiSelect() {
this.multiSelect.list = [];
},
refreshIssueData(issue, obj, defaultAvatar) {
refreshIssueData(issue, obj) {
issue.id = obj.id;
issue.iid = obj.iid;
issue.title = obj.title;
Loading
Loading
@@ -631,7 +631,7 @@ const boardsStore = {
}
 
if (obj.assignees) {
issue.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar));
issue.assignees = obj.assignees.map(a => new ListAssignee(a));
}
},
};
Loading
Loading
<script>
import ItemButton from './button.vue';
import { isTextFile } from '~/ide/utils';
 
export default {
components: {
Loading
Loading
@@ -23,29 +24,11 @@ export default {
},
},
methods: {
isText(content, fileType) {
const knownBinaryFileTypes = ['image/'];
const knownTextFileTypes = ['text/'];
const isKnownBinaryFileType = knownBinaryFileTypes.find(type => fileType.includes(type));
const isKnownTextFileType = knownTextFileTypes.find(type => fileType.includes(type));
const asciiRegex = /^[ -~\t\n\r]+$/; // tests whether a string contains ascii characters only (ranges from space to tilde, tabs and new lines)
if (isKnownBinaryFileType) {
return false;
}
if (isKnownTextFileType) {
return true;
}
// if it's not a known file type, determine the type by evaluating the file contents
return asciiRegex.test(content);
},
createFile(target, file) {
const { name } = file;
const encodedContent = target.result.split('base64,')[1];
const rawContent = encodedContent ? atob(encodedContent) : '';
const isText = this.isText(rawContent, file.type);
const isText = isTextFile(rawContent, file.type, name);
 
const emitCreateEvent = content =>
this.$emit('create', {
Loading
Loading
import { commitItemIconMap } from './constants';
import { languages } from 'monaco-editor';
import { flatten } from 'lodash';
const toLowerCase = x => x.toLowerCase();
const monacoLanguages = languages.getLanguages();
const monacoExtensions = new Set(
flatten(monacoLanguages.map(lang => lang.extensions?.map(toLowerCase) || [])),
);
const monacoMimetypes = new Set(
flatten(monacoLanguages.map(lang => lang.mimetypes?.map(toLowerCase) || [])),
);
const monacoFilenames = new Set(
flatten(monacoLanguages.map(lang => lang.filenames?.map(toLowerCase) || [])),
);
const KNOWN_TYPES = [
{
isText: false,
isMatch(mimeType) {
return mimeType.toLowerCase().includes('image/');
},
},
{
isText: true,
isMatch(mimeType) {
return mimeType.toLowerCase().includes('text/');
},
},
{
isText: true,
isMatch(mimeType, fileName) {
const fileExtension = fileName.includes('.') ? `.${fileName.split('.').pop()}` : '';
return (
monacoExtensions.has(fileExtension.toLowerCase()) ||
monacoMimetypes.has(mimeType.toLowerCase()) ||
monacoFilenames.has(fileName.toLowerCase())
);
},
},
];
export function isTextFile(content, mimeType, fileName) {
const knownType = KNOWN_TYPES.find(type => type.isMatch(mimeType, fileName));
if (knownType) return knownType.isText;
// does the string contain ascii characters only (ranges from space to tilde, tabs and new lines)
const asciiRegex = /^[ -~\t\n\r]+$/;
// for unknown types, determine the type by evaluating the file contents
return asciiRegex.test(content);
}
 
export const getCommitIconMap = file => {
if (file.deleted) {
Loading
Loading
Loading
Loading
@@ -11,7 +11,7 @@ import {
GlTooltip,
GlTooltipDirective,
} from '@gitlab/ui';
import { __ } from '~/locale';
import { __, n__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import MonitorTimeSeriesChart from './charts/time_series.vue';
import MonitorAnomalyChart from './charts/anomaly.vue';
Loading
Loading
@@ -120,6 +120,12 @@ export default {
!this.isPanelType('stacked-column')
);
},
editCustomMetricLink() {
return this.graphData?.metrics[0].edit_path;
},
editCustomMetricLinkText() {
return n__('Metrics|Edit metric', 'Metrics|Edit metrics', this.graphData.metrics.length);
},
},
mounted() {
this.refreshTitleTooltip();
Loading
Loading
@@ -195,7 +201,13 @@ export default {
<template slot="button-content">
<icon name="ellipsis_v" class="text-secondary" />
</template>
<gl-dropdown-item
v-if="editCustomMetricLink"
ref="editMetricLink"
:href="editCustomMetricLink"
>
{{ editCustomMetricLinkText }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="logsPathWithTimeRange"
ref="viewLogsLink"
Loading
Loading
Loading
Loading
@@ -15,7 +15,6 @@ module BoardsHelper
root_path: root_path,
full_path: full_path,
bulk_update_path: @bulk_issues_path,
default_avatar: image_path(default_avatar),
time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s,
recent_boards_endpoint: recent_boards_path
}
Loading
Loading
Loading
Loading
@@ -95,6 +95,7 @@ class GroupPolicy < BasePolicy
enable :admin_cluster
enable :destroy_deploy_token
enable :read_deploy_token
enable :create_deploy_token
end
 
rule { owner }.policy do
Loading
Loading
Loading
Loading
@@ -7,8 +7,13 @@ module Projects
class LfsDownloadLinkListService < BaseService
DOWNLOAD_ACTION = 'download'
 
# This could be different per server, but it seems like a reasonable value to start with.
# https://github.com/git-lfs/git-lfs/issues/419
REQUEST_BATCH_SIZE = 100
DownloadLinksError = Class.new(StandardError)
DownloadLinkNotFound = Class.new(StandardError)
DownloadLinksRequestEntityTooLargeError = Class.new(StandardError)
 
attr_reader :remote_uri
 
Loading
Loading
@@ -25,16 +30,39 @@ module Projects
def execute(oids)
return [] unless project&.lfs_enabled? && remote_uri && oids.present?
 
get_download_links(oids)
get_download_links_in_batches(oids)
end
 
private
 
def get_download_links_in_batches(oids, batch_size = REQUEST_BATCH_SIZE)
download_links = []
oids.each_slice(batch_size) do |batch|
download_links += get_download_links(batch)
end
download_links
rescue DownloadLinksRequestEntityTooLargeError => e
# Log this exceptions to see how open it happens
Gitlab::ErrorTracking
.track_exception(e, project_id: project&.id, batch_size: batch_size, oids_count: oids.count)
# Try again with a smaller batch
batch_size /= 2
retry if batch_size > REQUEST_BATCH_SIZE / 3
raise DownloadLinksError, 'Unable to download due to RequestEntityTooLarge errors'
end
def get_download_links(oids)
response = Gitlab::HTTP.post(remote_uri,
body: request_body(oids),
headers: headers)
 
raise DownloadLinksRequestEntityTooLargeError if response.request_entity_too_large?
raise DownloadLinksError, response.message unless response.success?
 
# Since the LFS Batch API may return a Content-Ttpe of
Loading
Loading
---
title: Optimize services usage counters using batch counters
merge_request: 26973
author:
type: performance
---
title: Fix embeds so that a chart appears only once
merge_request: 26997
author:
type: fixed
---
title: Add api endpoint for creating group deploy tokens
merge_request: 25629
author:
type: added
---
title: Fix issues with non-ASCII plain text files being incorrectly uploaded as binary
in the Web IDE
merge_request: 26360
author:
type: fixed
---
title: Batch processing LFS objects downloads
merge_request: 26434
author:
type: changed
---
title: Add edit custom metric link to metrics dashboard
merge_request: 26511
author:
type: changed
---
title: Add airgap support to Dependency Scanning template
merge_request: 26145
author:
type: changed
Loading
Loading
@@ -68,7 +68,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
 
namespace :settings do
get :members, to: redirect("%{namespace_id}/%{project_id}/project_members")
get :members, to: redirect("%{namespace_id}/%{project_id}/-/project_members")
 
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
post :reset_cache
Loading
Loading
Loading
Loading
@@ -156,6 +156,45 @@ Example response:
]
```
 
### Create a group deploy token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21811) in GitLab 12.9.
Creates a new deploy token for a group.
```
POST /groups/:id/deploy_tokens
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | New deploy token's name |
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository` or `read_registry`. |
Example request:
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "My deploy token", "expires_at": "2021-01-01", "username": "custom-user", "scopes": ["read_repository"]}' "https://gitlab.example.com/api/v4/groups/5/deploy_tokens/"
```
Example response:
```json
{
"id": 1,
"name": "My deploy token",
"username": "custom-user",
"expires_at": "2021-01-01T00:00:00.000Z",
"token": "jMRvtPNxrn3crTAGukpZ",
"scopes": [
"read_registry"
]
}
```
### Delete a group deploy token
 
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21811) in GitLab 12.9.
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment