Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
05f4b2fb
G
gitlab-foss
项目概览
李少辉-开发者
/
gitlab-foss
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gitlab-foss
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
05f4b2fb
编写于
10月 01, 2019
作者:
G
GitLab Bot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add latest changes from gitlab-org/gitlab@master
上级
9e27f0d9
变更
40
隐藏空白更改
内联
并排
Showing
40 changed file
with
749 addition
and
116 deletion
+749
-116
.gitlab-ci.yml
.gitlab-ci.yml
+3
-1
.gitlab/CODEOWNERS
.gitlab/CODEOWNERS
+2
-2
.gitlab/ci/global.gitlab-ci.yml
.gitlab/ci/global.gitlab-ci.yml
+6
-0
.gitlab/ci/notifications.gitlab-ci.yml
.gitlab/ci/notifications.gitlab-ci.yml
+25
-0
.gitlab/ci/qa.gitlab-ci.yml
.gitlab/ci/qa.gitlab-ci.yml
+1
-5
app/assets/javascripts/jobs/store/mutations.js
app/assets/javascripts/jobs/store/mutations.js
+1
-3
app/assets/javascripts/jobs/store/state.js
app/assets/javascripts/jobs/store/state.js
+0
-1
app/assets/javascripts/jobs/store/utils.js
app/assets/javascripts/jobs/store/utils.js
+53
-53
app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
...ar/components/confidential/confidential_issue_sidebar.vue
+11
-2
app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
...ets/javascripts/vue_shared/components/recaptcha_modal.vue
+4
-0
app/serializers/evidences/author_entity.rb
app/serializers/evidences/author_entity.rb
+9
-0
app/serializers/evidences/issue_entity.rb
app/serializers/evidences/issue_entity.rb
+15
-0
app/serializers/evidences/milestone_entity.rb
app/serializers/evidences/milestone_entity.rb
+14
-0
app/serializers/evidences/project_entity.rb
app/serializers/evidences/project_entity.rb
+10
-0
app/serializers/evidences/release_entity.rb
app/serializers/evidences/release_entity.rb
+13
-0
app/serializers/evidences/release_serializer.rb
app/serializers/evidences/release_serializer.rb
+7
-0
changelogs/unreleased/11412-fix-security-dashboard-history-bug.yml
...s/unreleased/11412-fix-security-dashboard-history-bug.yml
+5
-0
changelogs/unreleased/14794-rake-gitlab-geo-check-does-not-give-the-actual-state.yml
...-rake-gitlab-geo-check-does-not-give-the-actual-state.yml
+5
-0
changelogs/unreleased/winh-confidential-issue-recaptcha.yml
changelogs/unreleased/winh-confidential-issue-recaptcha.yml
+5
-0
doc/administration/gitaly/index.md
doc/administration/gitaly/index.md
+5
-0
doc/administration/gitaly/praefect.md
doc/administration/gitaly/praefect.md
+93
-0
doc/development/pipelines.md
doc/development/pipelines.md
+6
-0
lib/gitlab/usage_data.rb
lib/gitlab/usage_data.rb
+18
-16
scripts/notify-slack
scripts/notify-slack
+14
-0
spec/fixtures/api/schemas/evidences/author.json
spec/fixtures/api/schemas/evidences/author.json
+14
-0
spec/fixtures/api/schemas/evidences/issue.json
spec/fixtures/api/schemas/evidences/issue.json
+26
-0
spec/fixtures/api/schemas/evidences/milestone.json
spec/fixtures/api/schemas/evidences/milestone.json
+27
-0
spec/fixtures/api/schemas/evidences/project.json
spec/fixtures/api/schemas/evidences/project.json
+16
-0
spec/fixtures/api/schemas/evidences/release.json
spec/fixtures/api/schemas/evidences/release.json
+25
-0
spec/frontend/jobs/store/mutations_spec.js
spec/frontend/jobs/store/mutations_spec.js
+1
-0
spec/frontend/jobs/store/utils_spec.js
spec/frontend/jobs/store/utils_spec.js
+85
-8
spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap
...bar/__snapshots__/confidential_issue_sidebar_spec.js.snap
+8
-0
spec/frontend/sidebar/confidential_issue_sidebar_spec.js
spec/frontend/sidebar/confidential_issue_sidebar_spec.js
+90
-8
spec/lib/gitlab/usage_data_spec.rb
spec/lib/gitlab/usage_data_spec.rb
+13
-17
spec/serializers/evidences/author_entity_spec.rb
spec/serializers/evidences/author_entity_spec.rb
+13
-0
spec/serializers/evidences/issue_entity_spec.rb
spec/serializers/evidences/issue_entity_spec.rb
+13
-0
spec/serializers/evidences/milestone_entity_spec.rb
spec/serializers/evidences/milestone_entity_spec.rb
+35
-0
spec/serializers/evidences/project_entity_spec.rb
spec/serializers/evidences/project_entity_spec.rb
+13
-0
spec/serializers/evidences/release_entity_spec.rb
spec/serializers/evidences/release_entity_spec.rb
+36
-0
spec/serializers/evidences/release_serializer_spec.rb
spec/serializers/evidences/release_serializer_spec.rb
+9
-0
未找到文件。
.gitlab-ci.yml
浏览文件 @
05f4b2fb
...
...
@@ -8,6 +8,7 @@ stages:
-
review
-
qa
-
post-test
-
notification
-
pages
variables
:
...
...
@@ -27,11 +28,12 @@ after_script:
-
date
include
:
-
local
:
.gitlab/ci/global.gitlab-ci.yml
-
local
:
.gitlab/ci/cng.gitlab-ci.yml
-
local
:
.gitlab/ci/docs.gitlab-ci.yml
-
local
:
.gitlab/ci/frontend.gitlab-ci.yml
-
local
:
.gitlab/ci/global.gitlab-ci.yml
-
local
:
.gitlab/ci/memory.gitlab-ci.yml
-
local
:
.gitlab/ci/notifications.gitlab-ci.yml
-
local
:
.gitlab/ci/pages.gitlab-ci.yml
-
local
:
.gitlab/ci/qa.gitlab-ci.yml
-
local
:
.gitlab/ci/reports.gitlab-ci.yml
...
...
.gitlab/CODEOWNERS
浏览文件 @
05f4b2fb
...
...
@@ -6,8 +6,8 @@
/doc/ @axil @marcia @eread @mikelewis
# Frontend maintainers should see everything in `app/assets/`
app/assets/ @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter
app/assets/ @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter
@wortschi
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter
@wortschi
# Database maintainers should review changes in `db/`
db/ @gitlab-org/maintainers/database
...
...
.gitlab/ci/global.gitlab-ci.yml
浏览文件 @
05f4b2fb
...
...
@@ -110,6 +110,12 @@
-
$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
kubernetes
:
active
.only-canonical-schedules
:
only
:
refs
:
-
schedules@gitlab-org/gitlab
-
schedules@gitlab-org/gitlab-foss
.use-pg9
:
services
:
-
name
:
postgres:9.6
...
...
.gitlab/ci/notifications.gitlab-ci.yml
0 → 100644
浏览文件 @
05f4b2fb
.notify
:
image
:
alpine
stage
:
notification
dependencies
:
[]
cache
:
{}
before_script
:
-
apk update && apk add git curl bash
schedule:package-and-qa:notify-success:
extends
:
-
.only-canonical-schedules
-
.notify
script
:
-
'
scripts/notify-slack
qa-master
":tada:
Scheduled
QA
against
`master`
passed!
:tada:
See
$CI_PIPELINE_URL."
ci_passing'
needs
:
[
"
schedule:package-and-qa"
]
when
:
on_success
schedule:package-and-qa:notify-failure:
extends
:
-
.only-canonical-schedules
-
.notify
script
:
-
'
scripts/notify-slack
qa-master
":skull_and_crossbones:
Scheduled
QA
against
`master`
failed!
:skull_and_crossbones:
See
$CI_PIPELINE_URL."
ci_failing'
needs
:
[
"
schedule:package-and-qa"
]
when
:
on_failure
.gitlab/ci/qa.gitlab-ci.yml
浏览文件 @
05f4b2fb
...
...
@@ -38,9 +38,5 @@ schedule:package-and-qa:
extends
:
-
.package-and-qa-base
-
.only-code-qa-changes
only
:
refs
:
-
schedules@gitlab-org/gitlab
-
schedules@gitlab-org/gitlab-foss
-
.only-canonical-schedules
needs
:
[
"
build-qa-image"
,
"
gitlab:assets:compile"
]
allow_failure
:
true
app/assets/javascripts/jobs/store/mutations.js
浏览文件 @
05f4b2fb
...
...
@@ -26,8 +26,7 @@ export default {
if
(
log
.
append
)
{
if
(
isNewJobLogActive
())
{
state
.
originalTrace
=
state
.
originalTrace
.
concat
(
log
.
trace
);
state
.
trace
=
updateIncrementalTrace
(
state
.
originalTrace
,
state
.
trace
,
log
.
lines
);
state
.
trace
=
updateIncrementalTrace
(
log
.
lines
,
state
.
trace
);
}
else
{
state
.
trace
+=
log
.
html
;
}
...
...
@@ -38,7 +37,6 @@ export default {
// html or size. We keep the old value otherwise these
// will be set to `undefined`
if
(
isNewJobLogActive
())
{
state
.
originalTrace
=
log
.
lines
||
state
.
trace
;
state
.
trace
=
logLinesParser
(
log
.
lines
)
||
state
.
trace
;
}
else
{
state
.
trace
=
log
.
html
||
state
.
trace
;
...
...
app/assets/javascripts/jobs/store/state.js
浏览文件 @
05f4b2fb
...
...
@@ -19,7 +19,6 @@ export default () => ({
isScrolledToBottomBeforeReceivingTrace
:
true
,
trace
:
isNewJobLogActive
()
?
[]
:
''
,
originalTrace
:
[],
isTraceComplete
:
false
,
traceSize
:
0
,
isTraceSizeVisible
:
false
,
...
...
app/assets/javascripts/jobs/store/utils.js
浏览文件 @
05f4b2fb
...
...
@@ -63,6 +63,30 @@ export const isCollapsibleSection = (acc = [], last = {}, section = {}) =>
!
section
.
section_duration
&&
section
.
section
===
last
.
line
.
section
;
/**
* Returns the lineNumber of the last line in
* a parsed log
*
* @param Array acc
* @returns Number
*/
export
const
getIncrementalLineNumber
=
acc
=>
{
let
lineNumberValue
;
const
lastIndex
=
acc
.
length
-
1
;
const
lastElement
=
acc
[
lastIndex
];
const
nestedLines
=
lastElement
.
lines
;
if
(
lastElement
.
isHeader
&&
!
nestedLines
.
length
&&
lastElement
.
line
)
{
lineNumberValue
=
lastElement
.
line
.
lineNumber
;
}
else
if
(
lastElement
.
isHeader
&&
nestedLines
.
length
)
{
lineNumberValue
=
nestedLines
[
nestedLines
.
length
-
1
].
lineNumber
;
}
else
{
lineNumberValue
=
lastElement
.
lineNumber
;
}
return
lineNumberValue
===
0
?
1
:
lineNumberValue
+
1
;
};
/**
* Parses the job log content into a structure usable by the template
*
...
...
@@ -75,32 +99,35 @@ export const isCollapsibleSection = (acc = [], last = {}, section = {}) =>
* - adds the index as lineNumber
*
* @param Array lines
* @param Number lineNumberStart
* @param Array accumulator
* @returns Array parsed log lines
*/
export
const
logLinesParser
=
(
lines
=
[],
lineNumberStart
,
accumulator
=
[])
=>
lines
.
reduce
((
acc
,
line
,
index
)
=>
{
const
lineNumber
=
lineNumberStart
?
lineNumberStart
+
index
:
index
;
const
last
=
acc
[
acc
.
length
-
1
];
// If the object is an header, we parse it into another structure
if
(
line
.
section_header
)
{
acc
.
push
(
parseHeaderLine
(
line
,
lineNumber
));
}
else
if
(
isCollapsibleSection
(
acc
,
last
,
line
))
{
// if the object belongs to a nested section, we append it to the new `lines` array of the
// previously formated header
last
.
lines
.
push
(
parseLine
(
line
,
lineNumber
));
}
else
if
(
line
.
section_duration
)
{
// if the line has section_duration, we look for the correct header to add it
addDurationToHeader
(
acc
,
line
);
}
else
{
// otherwise it's a regular line
acc
.
push
(
parseLine
(
line
,
lineNumber
));
}
export
const
logLinesParser
=
(
lines
=
[],
accumulator
=
[])
=>
lines
.
reduce
(
(
acc
,
line
,
index
)
=>
{
const
lineNumber
=
accumulator
.
length
>
0
?
getIncrementalLineNumber
(
acc
)
:
index
;
const
last
=
acc
[
acc
.
length
-
1
];
// If the object is an header, we parse it into another structure
if
(
line
.
section_header
)
{
acc
.
push
(
parseHeaderLine
(
line
,
lineNumber
));
}
else
if
(
isCollapsibleSection
(
acc
,
last
,
line
))
{
// if the object belongs to a nested section, we append it to the new `lines` array of the
// previously formated header
last
.
lines
.
push
(
parseLine
(
line
,
lineNumber
));
}
else
if
(
line
.
section_duration
)
{
// if the line has section_duration, we look for the correct header to add it
addDurationToHeader
(
acc
,
line
);
}
else
{
// otherwise it's a regular line
acc
.
push
(
parseLine
(
line
,
lineNumber
));
}
return
acc
;
},
accumulator
);
return
acc
;
},
[...
accumulator
],
);
/**
* Finds the repeated offset, removes the old one
...
...
@@ -113,7 +140,7 @@ export const logLinesParser = (lines = [], lineNumberStart, accumulator = []) =>
* @returns Array
*
*/
export
const
findOffsetAndRemove
=
(
newLog
,
oldParsed
)
=>
{
export
const
findOffsetAndRemove
=
(
newLog
=
[],
oldParsed
=
[]
)
=>
{
const
cloneOldLog
=
[...
oldParsed
];
const
lastIndex
=
cloneOldLog
.
length
-
1
;
const
last
=
cloneOldLog
[
lastIndex
];
...
...
@@ -140,40 +167,13 @@ export const findOffsetAndRemove = (newLog, oldParsed) => {
* We need to check if that is the case by looking for the offset property
* before parsing the incremental part
*
* @param array originalTrace
* @param array oldLog
* @param array newLog
*/
export
const
updateIncrementalTrace
=
(
originalTrace
=
[],
oldLog
=
[],
newLog
=
[])
=>
{
const
firstLine
=
newLog
[
0
];
const
firstLineOffset
=
firstLine
.
offset
;
export
const
updateIncrementalTrace
=
(
newLog
,
oldParsed
=
[])
=>
{
const
parsedLog
=
findOffsetAndRemove
(
newLog
,
oldParsed
);
// We are going to return a new array,
// let's make a shallow copy to make sure we
// are not updating the state outside of a mutation first.
const
cloneOldLog
=
[...
oldLog
];
const
lastIndex
=
cloneOldLog
.
length
-
1
;
const
lastLine
=
cloneOldLog
[
lastIndex
];
// The last line may be inside a collpasible section
// If it is, we use the not parsed saved log, remove the last element
// and parse the first received part togheter with the incremental log
if
(
lastLine
.
isHeader
&&
(
lastLine
.
line
.
offset
===
firstLineOffset
||
(
lastLine
.
lines
.
length
&&
lastLine
.
lines
[
lastLine
.
lines
.
length
-
1
].
offset
===
firstLineOffset
))
)
{
const
cloneOriginal
=
[...
originalTrace
];
cloneOriginal
.
splice
(
cloneOriginal
.
length
-
1
);
return
logLinesParser
(
cloneOriginal
.
concat
(
newLog
));
}
else
if
(
lastLine
.
offset
===
firstLineOffset
)
{
cloneOldLog
.
splice
(
lastIndex
);
return
cloneOldLog
.
concat
(
logLinesParser
(
newLog
,
cloneOldLog
.
length
));
}
// there are no matches, let's parse the new log and return them together
return
cloneOldLog
.
concat
(
logLinesParser
(
newLog
,
cloneOldLog
.
length
));
return
logLinesParser
(
newLog
,
parsedLog
);
};
export
const
isNewJobLogActive
=
()
=>
gon
&&
gon
.
features
&&
gon
.
features
.
jobLogJson
;
app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
浏览文件 @
05f4b2fb
...
...
@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
eventHub
from
'
~/sidebar/event_hub
'
;
import
editForm
from
'
./edit_form.vue
'
;
import
recaptchaModalImplementor
from
'
~/vue_shared/mixins/recaptcha_modal_implementor
'
;
export
default
{
components
:
{
...
...
@@ -14,6 +15,7 @@ export default {
directives
:
{
tooltip
,
},
mixins
:
[
recaptchaModalImplementor
],
props
:
{
isConfidential
:
{
required
:
true
,
...
...
@@ -54,9 +56,14 @@ export default {
updateConfidentialAttribute
(
confidential
)
{
this
.
service
.
update
(
'
issue
'
,
{
confidential
})
.
then
(({
data
})
=>
this
.
checkForSpam
(
data
))
.
then
(()
=>
window
.
location
.
reload
())
.
catch
(()
=>
{
Flash
(
__
(
'
Something went wrong trying to change the confidentiality of this issue
'
));
.
catch
(
error
=>
{
if
(
error
.
name
===
'
SpamError
'
)
{
this
.
openRecaptcha
();
}
else
{
Flash
(
__
(
'
Something went wrong trying to change the confidentiality of this issue
'
));
}
});
},
},
...
...
@@ -112,5 +119,7 @@ export default {
{{
__
(
'
This issue is confidential
'
)
}}
</div>
</div>
<recaptcha-modal
v-if=
"showRecaptcha"
:html=
"recaptchaHTML"
@
close=
"closeRecaptcha"
/>
</div>
</
template
>
app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
浏览文件 @
05f4b2fb
...
...
@@ -32,6 +32,10 @@ export default {
mounted
()
{
eventHub
.
$on
(
'
submit
'
,
this
.
submit
);
if
(
this
.
html
)
{
this
.
appendRecaptchaScript
();
}
},
beforeDestroy
()
{
...
...
app/serializers/evidences/author_entity.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
AuthorEntity
<
Grape
::
Entity
expose
:id
expose
:name
expose
:email
end
end
app/serializers/evidences/issue_entity.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
IssueEntity
<
Grape
::
Entity
expose
:id
expose
:title
expose
:description
expose
:author
,
using:
AuthorEntity
expose
:state
expose
:iid
expose
:confidential
expose
:created_at
expose
:due_date
end
end
app/serializers/evidences/milestone_entity.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
MilestoneEntity
<
Grape
::
Entity
expose
:id
expose
:title
expose
:description
expose
:state
expose
:iid
expose
:created_at
expose
:due_date
expose
:issues
,
using:
IssueEntity
end
end
app/serializers/evidences/project_entity.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
ProjectEntity
<
Grape
::
Entity
expose
:id
expose
:name
expose
:description
expose
:created_at
end
end
app/serializers/evidences/release_entity.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
ReleaseEntity
<
Grape
::
Entity
expose
:id
expose
:tag
,
as: :tag_name
expose
:name
expose
:description
expose
:created_at
expose
:project
,
using:
ProjectEntity
expose
:milestones
,
using:
MilestoneEntity
end
end
app/serializers/evidences/release_serializer.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
module
Evidences
class
ReleaseSerializer
<
BaseSerializer
entity
ReleaseEntity
end
end
changelogs/unreleased/11412-fix-security-dashboard-history-bug.yml
0 → 100644
浏览文件 @
05f4b2fb
---
title
:
Fix routing bugs in security dashboards
merge_request
:
16738
author
:
type
:
fixed
changelogs/unreleased/14794-rake-gitlab-geo-check-does-not-give-the-actual-state.yml
0 → 100644
浏览文件 @
05f4b2fb
---
title
:
'
Geo:
Invalidate
cache
after
refreshing
foreign
tables'
merge_request
:
17885
author
:
type
:
fixed
changelogs/unreleased/winh-confidential-issue-recaptcha.yml
0 → 100644
浏览文件 @
05f4b2fb
---
title
:
Display reCAPTCHA modal when making issue public
merge_request
:
17553
author
:
type
:
fixed
doc/administration/gitaly/index.md
浏览文件 @
05f4b2fb
...
...
@@ -850,3 +850,8 @@ To remove the proxy setting, run the following commands (depending on which vari
unset
http_proxy
unset
https_proxy
```
### Praefect
Praefect is an experimental daemon that allows for replication of the Git data.
It can be setup with omnibus,
[
as explained here
](
./praefect.md
)
.
doc/administration/gitaly/praefect.md
0 → 100644
浏览文件 @
05f4b2fb
# Praefect
NOTE:
**Note:**
Praefect is an experimental service, and for testing purposes only at
this time.
## Omnibus
### Architecture
For this document, the following network topology is assumed:
```
mermaid
graph TB
GitLab --> Gitaly;
GitLab --> Praefect;
Praefect --> Preafect-Git-1;
Praefect --> Preafect-Git-2;
Praefect --> Preafect-Git-3;
```
Where
`GitLab`
is the collection of clients that can request Git operations.
`Gitaly`
is a Gitaly server before using Praefect. The Praefect node has two
storage nodes attached. Praefect itself doesn't storage data, but connects to
three Gitaly nodes,
`Praefect-Git-1`
,
`Praefect-Git-2`
, and
`Praefect-Git-3`
.
There should be no knowledge other than with Praefect about the existence of
the
`Praefect-Git-X`
nodes.
### Enable the daemon
Praefect is expected to run on their own host, this means that no other service
other than the support services run on this machine.
Praefect is disabled by default, to enable praefect uncomment the following line
and set it to
`true`
:
`# praefect['enable'] = false'`
```
ruby
praefect
[
'enable'
]
=
true
```
By default praefect will listen on port
`:2305`
. It's recommended to enable
prometheus to expose metrics. Uncomment the line so it looks like:
```
ruby
praefect
[
'prometheus_listen_addr'
]
=
"localhost:9652"
```
Preafect needs at least one storage to store the Git data on. This node should
run Gitaly and should not be listed as storage for GitLab itself, that is, the
only way it receives traffic is through Praefect and it's not listed in the
`git_data_dirs`
on any
`gitlab.rb`
in your GitLab cluster.
To set the nodes as depicted in the diagram above, the configuration should look
like:
```
ruby
praefect
[
'storage_nodes'
]
=
[
{
'storage'
=>
'praefect-git-1'
,
'address'
=>
'tcp://praefect-git-1.internal'
,
'primary'
=>
true
}
{
'storage'
=>
'praefect-git-2'
,
'address'
=>
'tcp://praefect-git-2.internal'
},
{
'storage'
=>
'praefect-git-3'
,
'address'
=>
'tcp://praefect-git-3.internal'
}
]
```
Save the file, and run
`gitlab-ctl reconfigure`
. To test if Praefect is running,
you could run
`gitlab-ctl status`
which should list praefect as being up.
### Enable Preafect as storage backend in GitLab
When Praefect is running, it should be exposed as a storage to GitLab. This
is done through setting the
`git_data_dirs`
. Assuming the default storage
configuration is used, there would be two storages available to GitLab:
```
ruby
git_data_dirs
({
"default"
=>
{
"gitaly_address"
=>
"tcp://gitaly.internal"
},
"praefect"
=>
{
"gitaly_address"
=>
"tcp://praefect.internal:2305"
}
})
```
Restart GitLab using
`gitlab-ctl restart`
on the GitLab node.
doc/development/pipelines.md
浏览文件 @
05f4b2fb
...
...
@@ -27,6 +27,7 @@ The current stages are:
-
`review`
: This stage includes jobs that deploy the GitLab and Docs Review Apps.
-
`qa`
: This stage includes jobs that perform QA tasks against the Review App
that is deployed in the previous stage.
-
`notification`
: This stage includes jobs that sends notifications about pipeline status.
-
`post-test`
: This stage includes jobs that build reports or gather data from
the previous stages' jobs (e.g. coverage, Knapsack metadata etc.).
-
`pages`
: This stage includes a job that deploys the various reports as
...
...
@@ -191,6 +192,11 @@ subgraph "`qa` stage"
dast -.-> |depends on| G;
end
subgraph "`notification` stage"
NOTIFICATION1["schedule:package-and-qa:notify-success<br>(on_success)"] -.-> |needs| P;
NOTIFICATION2["schedule:package-and-qa:notify-failure<br>(on_failure)"] -.-> |needs| P;
end
subgraph "`post-test` stage"
M
end
...
...
lib/gitlab/usage_data.rb
浏览文件 @
05f4b2fb
...
...
@@ -37,7 +37,7 @@ module Gitlab
usage_data
end
# rubocop:disable Metrics/AbcSize
# rubocop:
disable Metrics/AbcSize
# rubocop: disable CodeReuse/ActiveRecord
def
system_usage_data
{
...
...
@@ -96,14 +96,16 @@ module Gitlab
todos:
count
(
Todo
),
uploads:
count
(
Upload
),
web_hooks:
count
(
WebHook
)
}.
merge
(
services_usage
)
.
merge
(
approximate_counts
)
.
merge
(
usage_counters
)
}.
tap
do
|
data
|
data
[
:counts
][
:user_preferences
]
=
user_preferences_usage
end
}.
merge
(
services_usage
,
approximate_counts
,
usage_counters
,
user_preferences_usage
)
}
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: enable Metrics/AbcSize
def
cycle_analytics_usage_data
Gitlab
::
CycleAnalytics
::
UsageData
.
new
.
to_json
...
...
@@ -137,15 +139,15 @@ module Gitlab
# @return [Array<#totals>] An array of objects that respond to `#totals`
def
usage_data_counters
[
Gitlab
::
UsageDataCounters
::
WikiPageCounter
,
Gitlab
::
UsageDataCounters
::
WebIdeCounter
,
Gitlab
::
UsageDataCounters
::
NoteCounter
,
Gitlab
::
UsageDataCounters
::
SnippetCounter
,
Gitlab
::
UsageDataCounters
::
SearchCounter
,
Gitlab
::
UsageDataCounters
::
CycleAnalyticsCounter
,
Gitlab
::
UsageDataCounters
::
ProductivityAnalyticsCounter
,
Gitlab
::
UsageDataCounters
::
SourceCodeCounter
,
Gitlab
::
UsageDataCounters
::
MergeRequestCounter
Gitlab
::
UsageDataCounters
::
WikiPageCounter
,
Gitlab
::
UsageDataCounters
::
WebIdeCounter
,
Gitlab
::
UsageDataCounters
::
NoteCounter
,
Gitlab
::
UsageDataCounters
::
SnippetCounter
,
Gitlab
::
UsageDataCounters
::
SearchCounter
,
Gitlab
::
UsageDataCounters
::
CycleAnalyticsCounter
,
Gitlab
::
UsageDataCounters
::
ProductivityAnalyticsCounter
,
Gitlab
::
UsageDataCounters
::
SourceCodeCounter
,
Gitlab
::
UsageDataCounters
::
MergeRequestCounter
]
end
...
...
scripts/notify-slack
0 → 100755
浏览文件 @
05f4b2fb
#!/bin/bash
# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set).
# ICON_EMOJI needs to be set to an icon emoji name (without the `:` around it).
CHANNEL
=
$1
MSG
=
$2
ICON_EMOJI
=
$3
if
[
-z
"
$CHANNEL
"
]
||
[
-z
"
$CI_SLACK_WEBHOOK_URL
"
]
||
[
-z
"
$MSG
"
]
||
[
-z
"
$ICON_EMOJI
"
]
;
then
echo
"Missing argument(s) - Use:
$0
channel message icon_emoji"
echo
"and set CI_SLACK_WEBHOOK_URL environment variable."
else
curl
-X
POST
--data-urlencode
'payload={"channel": "#'
"
$CHANNEL
"
'", "username": "GitLab QA Bot", "text": "'
"
$MSG
"
'", "icon_emoji": "'
":
$ICON_EMOJI
:"
'"}'
"
$CI_SLACK_WEBHOOK_URL
"
fi
spec/fixtures/api/schemas/evidences/author.json
0 → 100644
浏览文件 @
05f4b2fb
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"name"
,
"email"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"name"
:
{
"type"
:
"string"
},
"email"
:
{
"type"
:
"string"
}
},
"additionalProperties"
:
false
}
spec/fixtures/api/schemas/evidences/issue.json
0 → 100644
浏览文件 @
05f4b2fb
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"title"
,
"description"
,
"author"
,
"state"
,
"iid"
,
"confidential"
,
"created_at"
,
"due_date"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
"string"
},
"author"
:
{
"$ref"
:
"author.json"
},
"state"
:
{
"type"
:
"string"
},
"iid"
:
{
"type"
:
"integer"
},
"confidential"
:
{
"type"
:
"boolean"
},
"created_at"
:
{
"type"
:
"date"
},
"due_date"
:
{
"type"
:
"date"
}
},
"additionalProperties"
:
false
}
spec/fixtures/api/schemas/evidences/milestone.json
0 → 100644
浏览文件 @
05f4b2fb
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"title"
,
"description"
,
"state"
,
"iid"
,
"created_at"
,
"due_date"
,
"issues"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
"string"
},
"state"
:
{
"type"
:
"string"
},
"iid"
:
{
"type"
:
"integer"
},
"created_at"
:
{
"type"
:
"date"
},
"due_date"
:
{
"type"
:
"date"
},
"issues"
:
{
"type"
:
"array"
,
"items"
:
{
"$ref"
:
"issue.json"
}
}
},
"additionalProperties"
:
false
}
spec/fixtures/api/schemas/evidences/project.json
0 → 100644
浏览文件 @
05f4b2fb
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"name"
,
"description"
,
"created_at"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"name"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
"string"
},
"created_at"
:
{
"type"
:
"date"
}
},
"additionalProperties"
:
false
}
spec/fixtures/api/schemas/evidences/release.json
0 → 100644
浏览文件 @
05f4b2fb
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"tag"
,
"name"
,
"description"
,
"created_at"
,
"project"
,
"milestones"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"tag"
:
{
"type"
:
"string"
},
"name"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
"string"
},
"created_at"
:
{
"type"
:
"date"
},
"project"
:
{
"$ref"
:
"project.json"
},
"milestones"
:
{
"type"
:
"array"
,
"items"
:
{
"$ref"
:
"milestone.json"
}
}
},
"additionalProperties"
:
false
}
spec/frontend/jobs/store/mutations_spec.js
浏览文件 @
05f4b2fb
...
...
@@ -73,6 +73,7 @@ describe('Jobs Store Mutations', () => {
html
,
size
:
511846
,
complete
:
true
,
lines
:
[],
});
expect
(
stateCopy
.
trace
).
toEqual
(
html
);
...
...
spec/frontend/jobs/store/utils_spec.js
浏览文件 @
05f4b2fb
...
...
@@ -6,6 +6,7 @@ import {
addDurationToHeader
,
isCollapsibleSection
,
findOffsetAndRemove
,
getIncrementalLineNumber
,
}
from
'
~/jobs/store/utils
'
;
import
{
utilsMockData
,
...
...
@@ -292,11 +293,91 @@ describe('Jobs Store Utils', () => {
});
});
describe
(
'
getIncrementalLineNumber
'
,
()
=>
{
describe
(
'
when last line is 0
'
,
()
=>
{
it
(
'
returns 1
'
,
()
=>
{
const
log
=
[
{
content
:
[],
lineNumber
:
0
,
},
];
expect
(
getIncrementalLineNumber
(
log
)).
toEqual
(
1
);
});
});
describe
(
'
with unnested line
'
,
()
=>
{
it
(
'
returns the lineNumber of the last item in the array
'
,
()
=>
{
const
log
=
[
{
content
:
[],
lineNumber
:
10
,
},
{
content
:
[],
lineNumber
:
101
,
},
];
expect
(
getIncrementalLineNumber
(
log
)).
toEqual
(
102
);
});
});
describe
(
'
when last line is the header section
'
,
()
=>
{
it
(
'
returns the lineNumber of the last item in the array
'
,
()
=>
{
const
log
=
[
{
content
:
[],
lineNumber
:
10
,
},
{
isHeader
:
true
,
line
:
{
lineNumber
:
101
,
content
:
[],
},
lines
:
[],
},
];
expect
(
getIncrementalLineNumber
(
log
)).
toEqual
(
102
);
});
});
describe
(
'
when last line is a nested line
'
,
()
=>
{
it
(
'
returns the lineNumber of the last item in the nested array
'
,
()
=>
{
const
log
=
[
{
content
:
[],
lineNumber
:
10
,
},
{
isHeader
:
true
,
line
:
{
lineNumber
:
101
,
content
:
[],
},
lines
:
[
{
lineNumber
:
102
,
content
:
[],
},
{
lineNumber
:
103
,
content
:
[]
},
],
},
];
expect
(
getIncrementalLineNumber
(
log
)).
toEqual
(
104
);
});
});
});
describe
(
'
updateIncrementalTrace
'
,
()
=>
{
describe
(
'
without repeated section
'
,
()
=>
{
it
(
'
concats and parses both arrays
'
,
()
=>
{
const
oldLog
=
logLinesParser
(
originalTrace
);
const
result
=
updateIncrementalTrace
(
originalTrace
,
oldLog
,
regularIncremental
);
const
result
=
updateIncrementalTrace
(
regularIncremental
,
oldLog
);
expect
(
result
).
toEqual
([
{
...
...
@@ -324,7 +405,7 @@ describe('Jobs Store Utils', () => {
describe
(
'
with regular line repeated offset
'
,
()
=>
{
it
(
'
updates the last line and formats with the incremental part
'
,
()
=>
{
const
oldLog
=
logLinesParser
(
originalTrace
);
const
result
=
updateIncrementalTrace
(
originalTrace
,
oldLog
,
regularIncrementalRepeated
);
const
result
=
updateIncrementalTrace
(
regularIncrementalRepeated
,
oldLog
);
expect
(
result
).
toEqual
([
{
...
...
@@ -343,7 +424,7 @@ describe('Jobs Store Utils', () => {
describe
(
'
with header line repeated
'
,
()
=>
{
it
(
'
updates the header line and formats with the incremental part
'
,
()
=>
{
const
oldLog
=
logLinesParser
(
headerTrace
);
const
result
=
updateIncrementalTrace
(
headerTrace
,
oldLog
,
headerTraceIncremental
);
const
result
=
updateIncrementalTrace
(
headerTrace
Incremental
,
oldLog
);
expect
(
result
).
toEqual
([
{
...
...
@@ -369,11 +450,7 @@ describe('Jobs Store Utils', () => {
describe
(
'
with collapsible line repeated
'
,
()
=>
{
it
(
'
updates the collapsible line and formats with the incremental part
'
,
()
=>
{
const
oldLog
=
logLinesParser
(
collapsibleTrace
);
const
result
=
updateIncrementalTrace
(
collapsibleTrace
,
oldLog
,
collapsibleTraceIncremental
,
);
const
result
=
updateIncrementalTrace
(
collapsibleTraceIncremental
,
oldLog
);
expect
(
result
).
toEqual
([
{
...
...
spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap
浏览文件 @
05f4b2fb
...
...
@@ -49,6 +49,8 @@ exports[`Confidential Issue Sidebar Block renders for isConfidential = false and
</div>
</div>
<!---->
</div>
`;
...
...
@@ -111,6 +113,8 @@ exports[`Confidential Issue Sidebar Block renders for isConfidential = false and
</div>
</div>
<!---->
</div>
`;
...
...
@@ -163,6 +167,8 @@ exports[`Confidential Issue Sidebar Block renders for isConfidential = true and
</div>
</div>
<!---->
</div>
`;
...
...
@@ -225,5 +231,7 @@ exports[`Confidential Issue Sidebar Block renders for isConfidential = true and
</div>
</div>
<!---->
</div>
`;
spec/frontend/sidebar/confidential_issue_sidebar_spec.js
浏览文件 @
05f4b2fb
...
...
@@ -2,15 +2,36 @@ import { shallowMount } from '@vue/test-utils';
import
ConfidentialIssueSidebar
from
'
~/sidebar/components/confidential/confidential_issue_sidebar.vue
'
;
import
{
mockTracking
,
triggerEvent
}
from
'
helpers/tracking_helper
'
;
import
EditForm
from
'
~/sidebar/components/confidential/edit_form.vue
'
;
import
SidebarService
from
'
~/sidebar/services/sidebar_service
'
;
import
createFlash
from
'
~/flash
'
;
import
RecaptchaModal
from
'
~/vue_shared/components/recaptcha_modal
'
;
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/sidebar/services/sidebar_service
'
);
describe
(
'
Confidential Issue Sidebar Block
'
,
()
=>
{
let
wrapper
;
const
createComponent
=
propsData
=>
{
const
service
=
{
update
:
()
=>
Promise
.
resolve
(
true
),
};
const
findRecaptchaModal
=
()
=>
wrapper
.
find
(
RecaptchaModal
);
const
triggerUpdateConfidentialAttribute
=
()
=>
{
wrapper
.
setData
({
edit
:
true
});
return
(
// wait for edit form to become visible
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
const
editForm
=
wrapper
.
find
(
EditForm
);
const
{
updateConfidentialAttribute
}
=
editForm
.
props
();
updateConfidentialAttribute
();
})
// wait for reCAPTCHA modal to render
.
then
(()
=>
wrapper
.
vm
.
$nextTick
())
);
};
const
createComponent
=
propsData
=>
{
const
service
=
new
SidebarService
();
wrapper
=
shallowMount
(
ConfidentialIssueSidebar
,
{
propsData
:
{
service
,
...
...
@@ -20,6 +41,15 @@ describe('Confidential Issue Sidebar Block', () => {
});
};
beforeEach
(()
=>
{
jest
.
clearAllMocks
();
jest
.
spyOn
(
window
.
location
,
'
reload
'
).
mockImplementation
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
.
each
`
isConfidential | isEditable
${
false
}
|
${
false
}
...
...
@@ -38,10 +68,6 @@ describe('Confidential Issue Sidebar Block', () => {
},
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
if editable
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
...
...
@@ -81,5 +107,61 @@ describe('Confidential Issue Sidebar Block', () => {
property
:
'
confidentiality
'
,
});
});
describe
(
'
for successful update
'
,
()
=>
{
beforeEach
(()
=>
{
SidebarService
.
prototype
.
update
.
mockResolvedValue
({
data
:
'
irrelevant
'
});
});
it
(
'
reloads the page
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
window
.
location
.
reload
).
toHaveBeenCalled
();
}));
it
(
'
does not show an error message
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
createFlash
).
not
.
toHaveBeenCalled
();
}));
});
describe
(
'
for update error
'
,
()
=>
{
beforeEach
(()
=>
{
SidebarService
.
prototype
.
update
.
mockRejectedValue
(
new
Error
(
'
updating failed!
'
));
});
it
(
'
does not reload the page
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
window
.
location
.
reload
).
not
.
toHaveBeenCalled
();
}));
it
(
'
shows an error message
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
}));
});
describe
(
'
for spam error
'
,
()
=>
{
beforeEach
(()
=>
{
SidebarService
.
prototype
.
update
.
mockRejectedValue
({
name
:
'
SpamError
'
});
});
it
(
'
does not reload the page
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
window
.
location
.
reload
).
not
.
toHaveBeenCalled
();
}));
it
(
'
does not show an error message
'
,
()
=>
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
createFlash
).
not
.
toHaveBeenCalled
();
}));
it
(
'
shows a reCAPTCHA modal
'
,
()
=>
{
expect
(
findRecaptchaModal
().
exists
()).
toBe
(
false
);
return
triggerUpdateConfidentialAttribute
().
then
(()
=>
{
expect
(
findRecaptchaModal
().
exists
()).
toBe
(
true
);
});
});
});
});
});
spec/lib/gitlab/usage_data_spec.rb
浏览文件 @
05f4b2fb
...
...
@@ -38,7 +38,7 @@ describe Gitlab::UsageData do
subject
{
described_class
.
data
}
it
'gathers usage data'
do
it
'gathers usage data'
,
:aggregate_failures
do
expect
(
subject
.
keys
).
to
include
(
*
%i(
active_user_count
counts
...
...
@@ -151,7 +151,8 @@ describe Gitlab::UsageData do
todos
uploads
web_hooks
user_preferences
user_preferences_group_overview_details
user_preferences_group_overview_security_dashboard
)
.
push
(
*
smau_keys
)
count_data
=
subject
[
:counts
]
...
...
@@ -163,7 +164,7 @@ describe Gitlab::UsageData do
expect
(
expected_keys
-
count_data
.
keys
).
to
be_empty
end
it
'gathers projects data correctly'
do
it
'gathers projects data correctly'
,
:aggregate_failures
do
count_data
=
subject
[
:counts
]
expect
(
count_data
[
:projects
]).
to
eq
(
4
)
...
...
@@ -209,11 +210,8 @@ describe Gitlab::UsageData do
describe
'the results of calling #totals on all objects in the array'
do
subject
{
described_class
.
usage_data_counters
.
map
(
&
:totals
)
}
it
do
is_expected
.
to
all
(
be_a
Hash
)
.
and
all
(
have_attributes
(
keys:
all
(
be_a
Symbol
),
values:
all
(
be_a
Integer
)))
end
it
{
is_expected
.
to
all
(
be_a
Hash
)
}
it
{
is_expected
.
to
all
(
have_attributes
(
keys:
all
(
be_a
Symbol
),
values:
all
(
be_a
Integer
)))
}
end
it
'does not have any conflicts'
do
...
...
@@ -226,7 +224,7 @@ describe Gitlab::UsageData do
describe
'#features_usage_data_ce'
do
subject
{
described_class
.
features_usage_data_ce
}
it
'gathers feature usage data'
do
it
'gathers feature usage data'
,
:aggregate_failures
do
expect
(
subject
[
:mattermost_enabled
]).
to
eq
(
Gitlab
.
config
.
mattermost
.
enabled
)
expect
(
subject
[
:signup_enabled
]).
to
eq
(
Gitlab
::
CurrentSettings
.
allow_signup?
)
expect
(
subject
[
:ldap_enabled
]).
to
eq
(
Gitlab
.
config
.
ldap
.
enabled
)
...
...
@@ -242,7 +240,7 @@ describe Gitlab::UsageData do
describe
'#components_usage_data'
do
subject
{
described_class
.
components_usage_data
}
it
'gathers components usage data'
do
it
'gathers components usage data'
,
:aggregate_failures
do
expect
(
subject
[
:gitlab_pages
][
:enabled
]).
to
eq
(
Gitlab
.
config
.
pages
.
enabled
)
expect
(
subject
[
:gitlab_pages
][
:version
]).
to
eq
(
Gitlab
::
Pages
::
VERSION
)
expect
(
subject
[
:git
][
:version
]).
to
eq
(
Gitlab
::
Git
.
version
)
...
...
@@ -258,7 +256,7 @@ describe Gitlab::UsageData do
describe
'#license_usage_data'
do
subject
{
described_class
.
license_usage_data
}
it
'gathers license data'
do
it
'gathers license data'
,
:aggregate_failures
do
expect
(
subject
[
:uuid
]).
to
eq
(
Gitlab
::
CurrentSettings
.
uuid
)
expect
(
subject
[
:version
]).
to
eq
(
Gitlab
::
VERSION
)
expect
(
subject
[
:installation_type
]).
to
eq
(
'gitlab-development-kit'
)
...
...
@@ -290,11 +288,11 @@ describe Gitlab::UsageData do
end
describe
'#approximate_counts'
do
it
'gets approximate counts for selected models'
do
it
'gets approximate counts for selected models'
,
:aggregate_failures
do
create
(
:label
)
expect
(
Gitlab
::
Database
::
Count
).
to
receive
(
:approximate_counts
)
.
with
(
described_class
::
APPROXIMATE_COUNT_MODELS
).
once
.
and_call_original
.
with
(
described_class
::
APPROXIMATE_COUNT_MODELS
).
once
.
and_call_original
counts
=
described_class
.
approximate_counts
.
values
...
...
@@ -302,14 +300,12 @@ describe Gitlab::UsageData do
expect
(
counts
.
any?
{
|
count
|
count
<
0
}).
to
be_falsey
end
it
'returns default values if counts can not be retrieved'
do
it
'returns default values if counts can not be retrieved'
,
:aggregate_failures
do
described_class
::
APPROXIMATE_COUNT_MODELS
.
map
do
|
model
|
model
.
name
.
underscore
.
pluralize
.
to_sym
end
expect
(
Gitlab
::
Database
::
Count
).
to
receive
(
:approximate_counts
)
.
and_return
({})
expect
(
Gitlab
::
Database
::
Count
).
to
receive
(
:approximate_counts
).
and_return
({})
expect
(
described_class
.
approximate_counts
.
values
.
uniq
).
to
eq
([
-
1
])
end
end
...
...
spec/serializers/evidences/author_entity_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
AuthorEntity
do
let
(
:entity
)
{
described_class
.
new
(
build
(
:author
))
}
subject
{
entity
.
as_json
}
it
'exposes the expected fields'
do
expect
(
subject
.
keys
).
to
contain_exactly
(
:id
,
:name
,
:email
)
end
end
spec/serializers/evidences/issue_entity_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
IssueEntity
do
let
(
:entity
)
{
described_class
.
new
(
build
(
:issue
))
}
subject
{
entity
.
as_json
}
it
'exposes the expected fields'
do
expect
(
subject
.
keys
).
to
contain_exactly
(
:id
,
:title
,
:description
,
:author
,
:state
,
:iid
,
:confidential
,
:created_at
,
:due_date
)
end
end
spec/serializers/evidences/milestone_entity_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
MilestoneEntity
do
let
(
:milestone
)
{
build
(
:milestone
)
}
let
(
:entity
)
{
described_class
.
new
(
milestone
)
}
subject
{
entity
.
as_json
}
it
'exposes the expected fields'
do
expect
(
subject
.
keys
).
to
contain_exactly
(
:id
,
:title
,
:description
,
:state
,
:iid
,
:created_at
,
:due_date
,
:issues
)
end
context
'when there issues linked to this milestone'
do
let
(
:issue_1
)
{
build
(
:issue
)
}
let
(
:issue_2
)
{
build
(
:issue
)
}
let
(
:milestone
)
{
build
(
:milestone
,
issues:
[
issue_1
,
issue_2
])
}
it
'exposes these issues'
do
expect
(
subject
[
:issues
]).
to
contain_exactly
(
Evidences
::
IssueEntity
.
new
(
issue_1
).
as_json
,
Evidences
::
IssueEntity
.
new
(
issue_2
).
as_json
)
end
end
context
'when the release has no milestone'
do
let
(
:milestone
)
{
build
(
:milestone
,
issues:
[])
}
it
'exposes an empty array for milestones'
do
expect
(
subject
[
:issues
]).
to
be_empty
end
end
end
spec/serializers/evidences/project_entity_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
ProjectEntity
do
let
(
:entity
)
{
described_class
.
new
(
build
(
:project
))
}
subject
{
entity
.
as_json
}
it
'exposes the expected fields'
do
expect
(
subject
.
keys
).
to
contain_exactly
(
:id
,
:name
,
:description
,
:created_at
)
end
end
spec/serializers/evidences/release_entity_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
ReleaseEntity
do
let
(
:release
)
{
build
(
:release
)
}
let
(
:entity
)
{
described_class
.
new
(
release
)
}
subject
{
entity
.
as_json
}
it
'exposes the expected fields'
do
expect
(
subject
.
keys
).
to
contain_exactly
(
:id
,
:tag_name
,
:name
,
:description
,
:created_at
,
:project
,
:milestones
)
end
context
'when the release has milestones'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:milestone_1
)
{
build
(
:milestone
,
project:
project
)
}
let
(
:milestone_2
)
{
build
(
:milestone
,
project:
project
)
}
let
(
:release
)
{
build
(
:release
,
project:
project
,
milestones:
[
milestone_1
,
milestone_2
])
}
it
'exposes these milestones'
do
expect
(
subject
[
:milestones
]).
to
contain_exactly
(
Evidences
::
MilestoneEntity
.
new
(
milestone_1
).
as_json
,
Evidences
::
MilestoneEntity
.
new
(
milestone_2
).
as_json
)
end
end
context
'when the release has no milestone'
do
let
(
:release
)
{
build
(
:release
,
milestones:
[])
}
it
'exposes an empty array for milestones'
do
expect
(
subject
[
:milestones
]).
to
be_empty
end
end
end
spec/serializers/evidences/release_serializer_spec.rb
0 → 100644
浏览文件 @
05f4b2fb
# frozen_string_literal: true
require
'spec_helper'
describe
Evidences
::
ReleaseSerializer
do
it
'represents an Evidence::ReleaseEntity entity'
do
expect
(
described_class
.
entity_class
).
to
eq
(
Evidences
::
ReleaseEntity
)
end
end
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录