Commit 83975914 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'artifacts-from-ref-and-build-name-api' into artifacts-from-ref-and-build-name

* artifacts-from-ref-and-build-name-api: (384 commits)
  Add API documentation for downloading the latest successful build
  Since it's too hard to use JOIN with Rails... feedback:
  Workaround MySQL with INNER JOIN:
  Just put setup directly in the test, feedback:
  Cleanup the use of let, feedback:
  Lose unneeded instance variables and variables, feedback:
  Add link to user profile to commit avatar (!5163)
  It's not longer than 80 chars
  Make sure there's a build
  Use the same logic, it should specify that it's not logged in
  Use 'when logging as guest' for context, feedback:
  Cleanup that a bit
  Refactor service settings view
  Fix a problem with processing a pipeline where stage only has manual actions
  Rename user2 to reporter_user
  Should check against `authorize_read_builds!`
  A CHANGELOG entry
  Don't show other actions of the same name
  Use limit parameter rather than hardcoded value
  Reuse those two methods
  ...
parents e3ce0230 4211f500
......@@ -148,7 +148,7 @@ spinach 9 10: *spinach-knapsack
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23
<<: *spinach-knapsack
<<: *ruby-23
rspec 0 20 ruby23: *rspec-knapsack-ruby23
rspec 1 20 ruby23: *rspec-knapsack-ruby23
rspec 2 20 ruby23: *rspec-knapsack-ruby23
......@@ -196,6 +196,7 @@ rake flog: *exec
rake flay: *exec
rake db:migrate:reset: *exec
license_finder: *exec
rake downtime_check: *exec
bundler:audit:
stage: test
......
......@@ -291,6 +291,10 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation:
Enabled: false
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
Style/MultilineTernaryOperator:
Enabled: true
# Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf:
Enabled: true
......
......@@ -226,10 +226,6 @@ Style/LineEndConcatenation:
Style/MethodCallParentheses:
Enabled: false
# Offense count: 3
Style/MultilineTernaryOperator:
Enabled: false
# Offense count: 62
# Cop supports --auto-correct.
Style/MutableConstant:
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Limit git rev-list output count to one in forced push check
v 8.10.0 (unreleased)
- Fix profile activity heatmap to show correct day name (eanplatter)
- Speed up ExternalWikiHelper#get_project_wiki_path
- Expose {should,force}_remove_source_branch (Ben Boeckel)
- Add the functionality to be able to rename a file. !5049 (tiagonbotelho)
- Disable PostgreSQL statement timeout during migrations
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
- Refresh the branch cache after `git gc` runs
- Allow to disable request access button on projects/groups
- Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects
- Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Add an API for downloading latest successful build from a particular branch or tag !5347
- Add link to profile to commit avatar !5163 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
......@@ -21,26 +30,36 @@ v 8.10.0 (unreleased)
- Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack
- Upgrade to Rails 4.2.7. !5236
- Extend exposed environment variables for CI builds
- Allow to pull code with deploy key from public projects
- Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts)
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
- Display tooltip for mentioned users and groups !5261 (winniehell)
- Allow build email service to be tested
- Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix GFM autocomplete not working on wiki pages
- Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates.
- Added Rake task for tracking deployments !5320
- Fix fetching LFS objects for private CI projects
- Add the new 2016 Emoji! Adds 72 new emoji including bacon, facepalm, and selfie. !5237
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown
- Redirects back to issue after clicking login link
- Eager load award emoji on notes
- Allow to define manual actions/builds on Pipelines and Environments
- Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design
- Issuable collapsed assignee tooltip is now the users name
- Fix compare view not changing code view rendering style
- Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view
......@@ -52,9 +71,11 @@ v 8.10.0 (unreleased)
- API: Expose `due_date` for issues (Robert Schilling)
- API: Todos !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
- API: Expose `developers_can_push` and `developers_can_merge` for branches !5208 (Robert Schilling)
- Add "Enabled Git access protocols" to Application Settings
- Diffs will create button/diff form on demand no on server side
- Reduce size of HTML used by diff comment forms
- Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
......@@ -65,6 +86,7 @@ v 8.10.0 (unreleased)
- Collapse large diffs by default (!4990)
- Fix mentioned users list on diff notes
- Fix creation of deployment on build that is retried, redeployed or rollback
- Don't parse Rinku returned value to DocFragment when it didn't change the original html string.
- Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork
- Fix stage status shown for pipelines
......@@ -77,6 +99,7 @@ v 8.10.0 (unreleased)
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
- Fix new snippet style bug (elliotec)
- Instrument Rinku usage
- Be explicit to define merge request discussion variables
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
......@@ -87,6 +110,7 @@ v 8.10.0 (unreleased)
- Don't render discussion notes when requesting diff tab through AJAX
- Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments
- Allow to setup event by channel on slack service
- More descriptive message for git hooks and file locks
- Aliases of award emoji should be stored as original name. !5060 (dixpac)
- Handle custom Git hook result in GitLab UI
......@@ -100,6 +124,7 @@ v 8.10.0 (unreleased)
- Add min value for project limit field on user's form !3622 (jastkand)
- Reset project pushes_since_gc when we enqueue the git gc call
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
- Collapsed diffs lines/size don't acumulate to overflow diffs.
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- Fix GitHub client requests when rate limit is disabled
......@@ -111,9 +136,17 @@ v 8.10.0 (unreleased)
- Fix last update timestamp on issues not preserved on gitlab.com and project imports
- Fix issues importing projects from EE to CE
- Fix creating group with space in group path
- Improve cron_jobs loading error messages !5318 / !5360
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- Limit the number of retries on error to 3 for exporting projects
- Allow empty repositories on project import/export
- Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska)
- Allow bulk (un)subscription from issues in issue index
- Fix MR diff encoding issues exporting GitLab projects
- Move builds settings out of project settings and rename Pipelines
- Add builds badge to Pipelines settings page
- Export and import avatar as part of project import/export
- Fix migration corrupting import data for old version upgrades
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
......
......@@ -52,7 +52,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.2'
gem 'gitlab_git', '~> 10.3.2'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -223,7 +223,7 @@ gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.6.1'
gem 'gemojione', '~> 2.6'
gem 'gemojione', '~> 3.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
......@@ -349,3 +349,6 @@ gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.0'
gem 'sys-filesystem', '~> 1.1.6'
# Secure headers for Content Security Policy
gem 'secure_headers', '~> 3.3'
......@@ -255,7 +255,7 @@ GEM
ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21)
gemojione (2.6.1)
gemojione (3.0.1)
json
get_process_mem (0.2.0)
gherkin-ruby (0.3.2)
......@@ -274,7 +274,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.2.3)
gitlab_git (10.3.2)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1)
rinku (2.0.0)
rotp (2.1.2)
rouge (2.0.3)
rouge (2.0.5)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
......@@ -645,6 +645,8 @@ GEM
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
secure_headers (3.3.2)
useragent
seed-fu (2.3.6)
activerecord (>= 3.1)
activesupport (>= 3.1)
......@@ -767,6 +769,7 @@ GEM
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.9.0)
useragent (0.16.7)
uuid (2.3.8)
macaddr (~> 1.0)
version_sorter (2.0.0)
......@@ -857,11 +860,11 @@ DEPENDENCIES
foreman (~> 0.78.0)
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6)
gemojione (~> 3.0)
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2)
gitlab_git (~> 10.3.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
......@@ -944,6 +947,7 @@ DEPENDENCIES
sass-rails (~> 5.0.0)
scss_lint (~> 0.47.0)
sdoc (~> 0.3.20)
secure_headers (~> 3.3)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
sentry-raven (~> 1.1.0)
......
# GitLab Maintenance Policy
GitLab is a fast moving and evolving project. We currently don't have the resources to support many releases concurrently. We support exactly one stable release at any given time.
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases:
`(Major).(Minor).(Patch)` in a [pragmatic way].
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: `(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e).
- **Major version**: Whenever there is something significant or any backwards
incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced
to the public API or a minor feature is introduced, or when a set of smaller
features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix
incorrect behavior.
- **Major version**: Whenever there is something significant or any backwards incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced to the public API or a minor feature is introduced, or when a set of smaller features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix incorrect behavior.
The current stable release will receive security patches and bug fixes
(eg. `8.9.0` -> `8.9.1`). Feature releases will mark the next supported stable
release where the minor version is increased numerically by increments of one
(eg. `8.9 -> 8.10`).
The current stable release will receive security patches and bug fixes (eg. `5.0` -> `5.0.1`). Feature releases will mark the next supported stable release where the minor version is increased numerically by increments of one (eg. `5.0 -> 5.1`).
Our current policy is to support one stable release at any given time, but for
medium-level security issues, we may consider [backporting to the previous two
monthly releases][rel-sec].
We encourage everyone to run the latest stable release to ensure that you can easily upgrade to the most secure and feature rich GitLab experience. In order to make sure you can easily run the most recent stable release, we are working hard to keep the update process simple and reliable.
We encourage everyone to run the latest stable release to ensure that you can
easily upgrade to the most secure and feature-rich GitLab experience. In order
to make sure you can easily run the most recent stable release, we are working
hard to keep the update process simple and reliable.
More information about the release procedures can be found in the doc/release directory.
More information about the release procedures can be found in our
[release-tools documentation][rel]. You may also want to read our
[Responsible Disclosure Policy][disclosure].
[rel-sec]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/security.md#backporting
[rel]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/
[disclosure]: https://about.gitlab.com/disclosure/
[pragmatic way]: https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e
app/assets/images/emoji.png

1000 KB | W: | H:

app/assets/images/emoji.png

1.04 MB | W: | H:

app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji@2x.png

2.38 MB | W: | H:

app/assets/images/emoji@2x.png

2.53 MB | W: | H:

app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -250,6 +250,8 @@ class GitLabDropdown
if self.options.clicked
self.options.clicked(selected, $el, e)
$el.trigger('blur')
# Finds an element inside wrapper element
getElement: (selector) ->
@dropdown.find selector
......
......@@ -85,12 +85,13 @@ class @IssuableBulkActions
getFormDataAsObject: ->
formData =
update:
state_event : @form.find('input[name="update[state_event]"]').val()
assignee_id : @form.find('input[name="update[assignee_id]"]').val()
milestone_id : @form.find('input[name="update[milestone_id]"]').val()
issues_ids : @form.find('input[name="update[issues_ids]"]').val()
add_label_ids : []
remove_label_ids : []
state_event : @form.find('input[name="update[state_event]"]').val()
assignee_id : @form.find('input[name="update[assignee_id]"]').val()
milestone_id : @form.find('input[name="update[milestone_id]"]').val()
issues_ids : @form.find('input[name="update[issues_ids]"]').val()
subscription_event : @form.find('input[name="update[subscription_event]"]').val()
add_label_ids : []
remove_label_ids : []
if @willUpdateLabels
@getLabelsToApply().map (id) ->
......
......@@ -2,7 +2,7 @@
w.gl ?= {}
w.gl.utils ?= {}
w.gl.utils.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
w.gl.utils.formatDate = (datetime) ->
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
......
$ ->
$(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name")
if name == "developers_can_push"
if name == "developers_can_push" || name == "developers_can_merge"
id = $(this).val()
checked = $(this).is(":checked")
can_push = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
type: "PATCH"
url: url
dataType: "json"
data:
id: id
protected_branch:
developers_can_push: checked
"#{name}": can_push
success: ->
row = $(e.target)
......
class @SubscriptionSelect
constructor: ->
$('.js-subscription-event').each (i, el) ->
fieldName = $(el).data("field-name")
$(el).glDropdown(
selectable: true
fieldName: fieldName
toggleLabel: (selected, el, instance) =>
label = 'Subscription'
$item = instance.dropdown.find('.is-active')
label = $item.text() if $item.length
label
clicked: (item, $el, e)->
e.preventDefault()
id: (obj, el) ->
$(el).data("id")
)
......@@ -232,7 +232,9 @@
.nav-block {
.controls {
float: right;
margin-top: 11px;
margin-top: 8px;
padding-bottom: 7px;
border-bottom: 1px solid $border-color;
}
}
......
......@@ -49,6 +49,17 @@
border-color: $border-dark;
color: $color;
}
svg {
path {
fill: $color;
}
use {
stroke: $color;
}
}
}
@mixin btn-green {
......@@ -173,6 +184,13 @@
.caret {
margin-left: 5px;
}
svg {
height: 15px;
width: auto;
position: relative;
top: 2px;
}
}
.btn-lg {
......
......@@ -70,7 +70,7 @@
}
&.wiki {
padding: $gl-padding;
padding: 30px $gl-padding;
.highlight {
margin-bottom: 9px;
......
......@@ -198,6 +198,10 @@ header.header-pinned-nav {
.sidebar-collapsed-icon {
cursor: pointer;
.btn {
background-color: $gray-light;
}
}
}
......
......@@ -37,39 +37,41 @@
}
h1 {
font-size: 1.3em;
font-size: 2em;
font-weight: 600;
margin: 24px 0 12px;
padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed;
margin: 1em 0 10px;
padding: 0 0 0.3em;
border-bottom: 1px solid $btn-default-border;
color: $gl-gray-dark;
}
h2 {
font-size: 1.2em;
font-size: 1.6em;
font-weight: 600;
margin: 24px 0 12px;
margin: 1em 0 10px;
padding-bottom: 0.3em;
border-bottom: 1px solid $btn-default-border;
color: $gl-gray-dark;
}
h3 {
margin: 24px 0 12px;
font-size: 1.1em;
margin: 1em 0 10px;
font-size: 1.4em;
}
h4 {
margin: 24px 0 12px;
font-size: 0.98em;
margin: 1em 0 10px;
font-size: 1.25em;
}
h5 {
margin: 24px 0 12px;
font-size: 0.95em;
margin: 1em 0 10px;
font-size: 1em;
}
h6 {
margin: 24px 0 12px;
font-size: 0.90em;
margin: 1em 0 10px;
font-size: 0.95em;
}
blockquote {
......@@ -115,7 +117,7 @@
ul, ol {
padding: 0;
margin: 6px 0 6px 28px !important;
margin: 3px 0 3px 28px !important;
}
li {
......
......@@ -16,7 +16,7 @@ $border-color: #e5e5e5;
$focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$dark-background-color: #f7f7f7;
$dark-background-color: #f5f5f5;
$table-text-gray: #8f8f8f;
/*
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -122,7 +122,8 @@
button {
float: right;
padding: 3px 5px;
padding: 1px 5px;
background-color: $gray-light;
}
}
......
......@@ -63,7 +63,7 @@ form.edit-issue {
.merge-request,
.issue {
&.today {
background: #f8feef;
background: #f3fff2;
border-color: #e1e8d5;
}
......@@ -78,6 +78,14 @@ form.edit-issue {
}
}
.merge-request-ci-status {
svg {
margin-right: 4px;
position: relative;
top: 1px;
}
}
@media (max-width: $screen-xs-max) {
.issue-btn-group {
width: 100%;
......
......@@ -60,8 +60,10 @@
.ci_widget {
border-bottom: 1px solid #eef0f2;
i {
svg {
margin-right: 4px;
position: relative;
top: 1px;
}
&.ci-success {
......@@ -196,6 +198,16 @@
.merge-request-title {
margin-bottom: 2px;
.ci-status-link {
svg {
height: 16px;
width: 16px;
position: relative;
top: 3px;
}
}
}
}
......@@ -270,7 +282,7 @@
.item-title {
@media (min-width: $screen-sm-min) {
width: 49%;
width: 45%;
}
}
......
.pipelines {
.stage {
max-width: 80px;
width: 80px;
max-width: 90px;
width: 90px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
......@@ -30,13 +30,17 @@
}
.table.builds {
min-width: 1100px;
min-width: 1200px;
tr {
th {
padding: 16px;
padding: 16px 8px;
border: none;
}
td {
padding: 10px 8px;
}
}
tbody {
......@@ -45,6 +49,14 @@
.commit-link {
.ci-status {
svg {
top: 1px;
margin-right: 0;
}
}
a:hover {
text-decoration: none;
}
......@@ -53,9 +65,8 @@
.branch-commit {
.branch-name {
margin-left: 8px;
font-weight: bold;
max-width: 180px;
max-width: 150px;
overflow: hidden;
display: inline-block;
white-space: nowrap;
......@@ -64,10 +75,15 @@
}
svg {
margin: 0 6px;
height: 14px;
width: auto;
vertical-align: middle;
fill: $table-text-gray;
}
.fa {
font-size: 12px;
color: $table-text-gray;
}
.commit-id {
......@@ -100,6 +116,31 @@
}
}
.icon-container {
display: inline-block;
text-align: right;
width: 20px;
.fa {
position: relative;
right: 3px;
}
svg {
position: relative;
right: 1px;
}
}
.stage-cell {
svg {
height: 18px;
width: 18px;
vertical-align: middle;
}
}
.duration,
.finished-at {
color: $table-text-gray;
......@@ -107,21 +148,19 @@
.fa {
font-size: 12px;
margin-right: 4px;
}
svg {
height: 12px;
width: auto;
width: 12px;
height: auto;
vertical-align: middle;
}
.fa,
svg {
margin-right: 5px;
margin-right: 4px;
}
}
.pipeline-actions {
min-width: 140px;
.btn {
margin: 0;
......
......@@ -129,6 +129,17 @@
color: $layout-link-gray;
}
svg {
path {
fill: $layout-link-gray;
}
use {
stroke: $layout-link-gray;
}
}
.fa-caret-down {
margin-left: 3px;
}
......@@ -322,18 +333,53 @@ a.deploy-project-label {
}
.fork-namespaces {
.fork-thumbnail {
text-align: center;
margin-bottom: $gl-padding;
.caption {
padding: $gl-padding 0;
min-height: 30px;
}
.row {
-webkit-flex-wrap: wrap;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.fork-thumbnail {
@include border-radius($border-radius-base);
background-color: $white-light;
border: 1px solid $border-white-light;
height: 202px;
margin: $gl-padding;
text-align: center;
width: 169px;
&:hover, &.forked {
background-color: $row-hover;
border-color: $row-hover-border;
}
.no-avatar {
width: 100px;
height: 100px;
background-color: $gray-light;
border: 1px solid $gray-dark;
margin: 0 auto;
@include border-radius(50%);
i {
font-size: 100px;
color: $gray-dark;
}
}
a {
display: block;
width: 100%;
height: 100%;
padding-top: $gl-padding;
color: $gl-gray;
.caption {
min-height: 30px;
padding: $gl-padding 0;
}
}
img {
@include border-radius(50%);
max-width: 100px;
img {
@include border-radius(50%);
max-width: 100px;
}
}
}
}
......@@ -486,6 +532,11 @@ pre.light-well {
> span {
margin-left: 10px;
}
svg {
position: relative;
top: 2px;
}
}
}
......
......@@ -41,6 +41,14 @@
color: $blue-normal;
border-color: $blue-normal;
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 1px;
margin: 0 3px;
}
}
.ci-status-icon-success {
......
.tag-buttons {
line-height: 40px;
.btn:not(.dropdown-toggle) {
margin-left: 10px;
}
}
......@@ -19,7 +19,7 @@
border-top: 1px solid $table-border-gray;
td, th {
line-height: 23px;
line-height: 21px;
}
&:hover {
......
......@@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level)
params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level, :request_access_enabled)
end
end
class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
before_action :service, only: [:edit, :update]
def index
......@@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
if service.update_attributes(application_services_params[:service])
if service.update_attributes(service_params[:service])
redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully'
else
......@@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController
def service
@service ||= Service.where(id: params[:id], template: true).first
end
def application_services_params
application_services_params = params.permit(:id,
service: Projects::ServicesController::ALLOWED_PARAMS)
if application_services_params[:service].is_a?(Hash)
Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param|
application_services_params[:service].delete(param) if application_services_params[:service][param].blank?
end
end
application_services_params
end
end
......@@ -7,7 +7,8 @@ module CreatesCommit
commit_params = @commit_params.merge(
source_project: @project,
source_branch: @ref,
target_branch: @target_branch
target_branch: @target_branch,
previous_path: @previous_path
)
result = service.new(@tree_edit_project, current_user, commit_params).execute
......
......@@ -10,7 +10,6 @@ module DiffForPath
diff_commit = commit_for_diff(diff_file)
blob = diff_file.blob(diff_commit)
@expand_all_diffs = true
locals = {
diff_file: diff_file,
......
module ServiceParams
extend ActiveSupport::Concern
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :build_events, :wiki_page_events,
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
def service_params
dynamic_params = []
dynamic_params.concat(@service.event_channel_names)
service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
if service_params[:service].is_a?(Hash)
FILTER_BLANK_PARAMS.each do |param|
service_params[:service].delete(param) if service_params[:service][param].blank?
end
end
service_params
end
end
......@@ -121,7 +121,7 @@ class GroupsController < Groups::ApplicationController
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock)
params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock, :request_access_enabled)
end
def load_events
......
......@@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
def index
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
end
def build
badge = Gitlab::Badge::Build.new(project, params[:ref])
......
......@@ -38,6 +38,12 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
if params[:file_path].present?
@previous_path = @path
@path = params[:file_path]
@commit_params[:file_path] = @path
end
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
......
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry, :play]
before_action :authorize_update_build!, except: [:index, :show, :status, :raw]
layout 'project'
......@@ -49,14 +49,19 @@ class Projects::BuildsController < Projects::ApplicationController
end
def retry
unless @build.retryable?
return render_404
end
return render_404 unless @build.retryable?
build = Ci::Build.retry(@build, current_user)
redirect_to build_path(build)
end
def play
return render_404 unless @build.playable?
build = @build.play(current_user)
redirect_to build_path(build)
end
def cancel
@build.cancel
redirect_to build_path(@build)
......
......@@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def show
apply_diff_view_cookie!
end
def diff_for_path
......
......@@ -226,6 +226,7 @@ class Projects::IssuesController < Projects::ApplicationController
:assignee_id,
:milestone_id,
:state_event,
:subscription_event,
label_ids: [],
add_label_ids: [],
remove_label_ids: []
......
class Projects::PipelinesSettingsController < Projects::ApplicationController
before_action :authorize_admin_pipeline!
def show
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
end
def update
if @project.update_attributes(update_params)
flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
else
render 'index'
end
end
private
def create_params
params.require(:pipeline).permit(:ref)
end
def update_params
params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds
)
end
end
......@@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
end
def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push)
params.require(:protected_branch).permit(:name, :developers_can_push, :developers_can_merge)
end
end
......@@ -25,7 +25,7 @@ class Projects::RefsController < Projects::ApplicationController
when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id)
when "badges"
namespace_project_badges_path(@project.namespace, @project, ref: @id)
namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id)
else
namespace_project_commits_path(@project.namespace, @project, @id)
end
......
class Projects::ServicesController < Projects::ApplicationController
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :build_events, :wiki_page_events,
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
include ServiceParams
# Authorize
before_action :authorize_admin_project!
......@@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def update
if @service.update_attributes(service_params)
if @service.update_attributes(service_params[:service])
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project,
@service.to_param, notice:
......@@ -45,8 +30,9 @@ class Projects::ServicesController < Projects::ApplicationController
end
def test
data = Gitlab::PushDataBuilder.build_sample(project, current_user)
data = @service.test_data(project, current_user)
outcome = @service.test(data)
if outcome[:success]
message = { notice: 'We sent a request to the provided URL' }
else
......@@ -63,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController
def service
@service ||= @project.services.find { |service| service.to_param == params[:id] }
end
def service_params
service_params = params.require(:service).permit(ALLOWED_PARAMS)
FILTER_BLANK_PARAMS.each do |param|
service_params.delete(param) if service_params[param].blank?
end
service_params
end
end
......@@ -5,7 +5,7 @@ class ProjectsController < Projects::ApplicationController
before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create]
before_action :assign_ref_vars, only: [:show], if: :repo_exists?
before_action :tree, only: [:show], if: :project_view_files?
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
......@@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :only_allow_merge_if_build_succeeds
:public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled
)
end
......
module AvatarsHelper
def author_avatar(commit_or_event, options = {})
user_avatar(options.merge({
user: commit_or_event.author,
user_name: commit_or_event.author_name,
user_email: commit_or_event.author_email,
}))
end
private
def user_avatar(options = {})
avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name]
avatar = image_tag(
avatar_icon(options[:user] || options[:user_email], avatar_size),
class: "avatar has-tooltip hidden-xs s#{avatar_size}",
alt: "#{user_name}'s avatar",
title: user_name
)
if options[:user]
link_to(avatar, user_path(options[:user]))
elsif options[:user_email]
mail_to(options[:user_email], avatar)
end
end
end
......@@ -12,7 +12,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_exists?(branch_name)
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name)
::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
end
def project_branches
......
......@@ -26,18 +26,20 @@ module CiStatusHelper
icon_name =
case status
when 'success'
'check'
'icon_status_success'
when 'success_with_warnings'
'icon_status_warning'
when 'failed'
'close'
'icon_status_failed'
when 'pending'
'clock-o'
'icon_status_pending'
when 'running'
'spinner'
'icon_status_running'
else
'circle'
'icon_status_cancel'
end
icon(icon_name + ' fw')
custom_icon(icon_name)
end
def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '')
......
......@@ -16,16 +16,6 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer))
end
def commit_author_avatar(commit, options = {})
options = options.merge(source: :author)
user = commit.send(options[:source])
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
person_email = user.try(:email) || source_email
image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "")
end
def image_diff_class(diff)
if diff.deleted_file
"deleted"
......
......@@ -9,7 +9,7 @@ module DiffHelper
end
def expand_all_diffs?
@expand_all_diffs || params[:expand_all_diffs].present?
params[:expand_all_diffs].present?
end
def diff_view
......@@ -23,13 +23,14 @@ module DiffHelper
end
def diff_options
default_options = Commit.max_diff_options
options = { ignore_whitespace_change: hide_whitespace?, no_collapse: expand_all_diffs? }
if action_name == 'diff_for_path'
default_options[:paths] = params.values_at(:old_path, :new_path)
options[:no_collapse] = true
options[:paths] = params.values_at(:old_path, :new_path)
end
default_options.merge(ignore_whitespace_change: hide_whitespace?)
Commit.max_diff_options.merge(options)
end
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
......
module ExternalWikiHelper
def get_project_wiki_path(project)
external_wiki_service = project.services.
find { |service| service.to_param == 'external_wiki' }
if external_wiki_service.present? && external_wiki_service.active?
external_wiki_service = project.external_wiki
if external_wiki_service
external_wiki_service.properties['external_wiki_url']
else
namespace_project_wiki_path(project.namespace, project, :home)
......
module ServicesHelper
def service_event_description(event)
case event
when "push"
"Event will be triggered by a push to the repository"
when "tag_push"
"Event will be triggered when a new tag is pushed to the repository"
when "note"
"Event will be triggered when someone adds a comment"
when "issue"
"Event will be triggered when an issue is created/updated/merged"
when "merge_request"
"Event will be triggered when a merge request is created/updated/merged"
when "build"
"Event will be triggered when a build status changes"
when "wiki_page"
"Event will be triggered when a wiki page is created/updated"
end
end
def service_event_field_name(event)
event = event.pluralize if %w[merge_request issue].include?(event)
"#{event}_events"
end
end
module TimeHelper
def duration_in_words(finished_at, started_at)
if finished_at && started_at
interval_in_seconds = finished_at.to_i - started_at.to_i
elsif started_at
interval_in_seconds = Time.now.to_i - started_at.to_i
end
time_interval_in_words(interval_in_seconds)
end
def time_interval_in_words(interval_in_seconds)
minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60
......@@ -25,9 +15,19 @@ module TimeHelper
end
def duration_in_numbers(finished_at, started_at)
diff_in_seconds = finished_at.to_i - started_at.to_i
time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S"
interval = interval_in_seconds(started_at, finished_at)
time_format = interval < 1.hour ? "%M:%S" : "%H:%M:%S"
Time.at(diff_in_seconds).utc.strftime(time_format)
Time.at(interval).utc.strftime(time_format)
end
private
def interval_in_seconds(started_at, finished_at = nil)
if started_at && finished_at
finished_at.to_i - started_at.to_i
elsif started_at
Time.now.to_i - started_at.to_i
end
end
end
......@@ -6,6 +6,7 @@ module Emails
add_project_headers
add_build_headers('failed')
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
end
......
......@@ -172,7 +172,7 @@ class Ability
rules << :read_build if project.public_builds?
unless owner || project.team.member?(user) || project_group_member?(project, user)
rules << :request_access
rules << :request_access if project.request_access_enabled
end
end
......@@ -373,7 +373,7 @@ class Ability
end
if group.public? || (group.internal? && !user.external?)
rules << :request_access unless group.users.include?(user)
rules << :request_access if group.request_access_enabled && group.users.exclude?(user)
end
rules.flatten
......
......@@ -12,12 +12,10 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :with_artifacts, ->() { where.not(artifacts_file: nil) }
scope :with_artifacts, ->() { where.not(artifacts_file: [nil, '']) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :latest_successful_with_artifacts, ->() do
with_artifacts.success.order(id: :desc)
end
scope :manual_actions, ->() { where(when: :manual) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
......@@ -94,6 +92,29 @@ module Ci
end
end
def manual?
self.when == 'manual'
end
def other_actions
pipeline.manual_actions.where.not(name: name)
end
def playable?
project.builds_enabled? && commands.present? && manual?
end
def play(current_user = nil)
# Try to queue a current build
if self.queue
self.update(user: current_user)
self
else
# Otherwise we need to create a duplicate
Ci::Build.retry(self, current_user)
end
end
def retryable?
project.builds_enabled? && commands.present? && complete?
end
......@@ -124,11 +145,14 @@ module Ci
end
def variables
variables = []
variables += predefined_variables
variables += yaml_variables if yaml_variables
variables += project_variables
variables += trigger_variables
variables = predefined_variables
variables += project.predefined_variables
variables += pipeline.predefined_variables
variables += runner.predefined_variables if runner
variables += project.container_registry_variables
variables += yaml_variables
variables += project.secret_variables
variables += trigger_request.user_variables if trigger_request
variables
end
......@@ -388,6 +412,14 @@ module Ci
self.update(artifacts_expire_at: nil)
end
def when
read_attribute(:when) || build_attributes_from_config[:when] || 'on_success'
end
def yaml_variables
read_attribute(:yaml_variables) || build_attributes_from_config[:yaml_variables] || []
end
private
def update_artifacts_size
......@@ -406,29 +438,30 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
end
def project_variables
project.variables.map do |variable|
{ key: variable.key, value: variable.value, public: false }
end
def predefined_variables
variables = [
{ key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true },
{ key: 'CI_BUILD_ID', value: id.to_s, public: true },
{ key: 'CI_BUILD_TOKEN', value: token, public: false },
{ key: 'CI_BUILD_REF', value: sha, public: true },
{ key: 'CI_BUILD_BEFORE_SHA', value: before_sha, public: true },
{ key: 'CI_BUILD_REF_NAME', value: ref, public: true },
{ key: 'CI_BUILD_NAME', value: name, public: true },
{ key: 'CI_BUILD_STAGE', value: stage, public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }
]
variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request
variables
end
def trigger_variables
if trigger_request && trigger_request.variables
trigger_request.variables.map do |key, value|
{ key: key, value: value, public: false }
end
else
[]
end
end
def build_attributes_from_config
return {} unless pipeline.config_processor
def predefined_variables
variables = []
variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
variables << { key: :CI_BUILD_NAME, value: name, public: true }
variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
variables
pipeline.config_processor.build_attributes(name)
end
end
end
......@@ -20,6 +20,11 @@ module Ci
after_touch :update_state
after_save :keep_around_commits
# ref can't be HEAD or SHA, can only be branch/tag name
scope :latest_successful_for, ->(ref = default_branch) do
where(ref: ref).success.order(id: :desc).limit(1)
end
def self.truncate_sha(sha)
sha[0...8]
end
......@@ -51,6 +56,10 @@ module Ci
commit.try(:message)
end
def git_commit_title
commit.try(:title)
end
def short_sha
Ci::Pipeline.truncate_sha(sha)
end
......@@ -65,6 +74,10 @@ module Ci
!tag?
end
def manual_actions
builds.latest.manual_actions
end
def retryable?
builds.latest.any? do |build|
build.failed? && build.retryable?
......@@ -190,6 +203,12 @@ module Ci
Note.for_commit_id(sha)
end
def predefined_variables
[
{ key: 'CI_PIPELINE_ID', value: id.to_s, public: true }
]
end
private
def build_builds_for_stages(stages, user, status, trigger_request)
......@@ -198,8 +217,9 @@ module Ci
# build builds only for the first stage that has builds available.
#
stages.any? do |stage|
CreateBuildsService.new(self)
.execute(stage, user, status, trigger_request).present?
CreateBuildsService.new(self).
execute(stage, user, status, trigger_request).
any?(&:active?)
end
end
......
......@@ -114,6 +114,14 @@ module Ci
tag_list.any?
end
def predefined_variables
[
{ key: 'CI_RUNNER_ID', value: id.to_s, public: true },
{ key: 'CI_RUNNER_DESCRIPTION', value: description, public: true },
{ key: 'CI_RUNNER_TAGS', value: tag_list.to_s, public: true }
]
end
private
def tag_constraints
......
......@@ -7,5 +7,13 @@ module Ci
has_many :builds, class_name: 'Ci::Build'
serialize :variables
def user_variables
return [] unless variables
variables.map do |key, value|
{ key: key, value: value, public: false }
end
end
end
end
......@@ -17,17 +17,19 @@ class CommitStatus < ActiveRecord::Base
alias_attribute :author, :user
scope :latest, -> do
max_id = unscope(:select).
select("max(#{table_name}.id)").
group(:name, :commit_id)
max_id = unscope(:select).select("max(#{quoted_table_name}.id)")
where(id: max_id)
where(id: max_id.group(:name, :commit_id))
end
scope :retried, -> { where.not(id: latest) }
scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
state_machine :status, initial: :pending do
event :queue do
transition skipped: :pending
end
event :run do
transition pending: :running
end
......
......@@ -16,10 +16,10 @@ module Statuseable
deduce_status = "(CASE
WHEN (#{builds})=0 THEN NULL
WHEN (#{builds})=(#{success})+(#{ignored}) THEN 'success'
WHEN (#{builds})=(#{pending}) THEN 'pending'
WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored}) THEN 'canceled'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success})+(#{ignored})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{pending})+(#{skipped}) THEN 'pending'
WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored})+(#{skipped}) THEN 'canceled'
WHEN (#{running})+(#{pending})>0 THEN 'running'
ELSE 'failed'
END)"
......
......@@ -32,4 +32,8 @@ class Deployment < ActiveRecord::Base
def keep_around_commit
project.repository.keep_around(self.sha)
end
def manual_actions
deployable.try(:other_actions)
end
end
......@@ -552,7 +552,13 @@ class MergeRequest < ActiveRecord::Base
end
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project, 'web').can_push_to_branch?(target_branch)
access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch) || access.can_merge_to_branch?(target_branch)
end
def can_be_merged_via_command_line_by?(user)
access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch)
end
def mergeable_ci_state?
......
class MergeRequestDiff < ActiveRecord::Base
include Sortable
include Importable
include EncodingHelper
# Prevent store of diff if commits amount more then 500
COMMITS_SAFE_SIZE = 100
......@@ -211,6 +212,14 @@ class MergeRequestDiff < ActiveRecord::Base
branch_base_commit.try(:sha)
end
def utf8_st_diffs
st_diffs.map do |diff|
diff.each do |k, v|
diff[k] = encode_utf8(v) if v.respond_to?(:encoding)
end
end
end
#
# #save or #update_attributes providing changes on serialized attributes do a lot of
# serialization and deserialization calls resulting in bad performance.
......
......@@ -430,18 +430,14 @@ class Project < ActiveRecord::Base
end
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_pipeline_for(ref = 'master')
table = Ci::Pipeline.quoted_table_name
# TODO: Use `where(ref: ref).or(sha: ref)` in Rails 5
pipelines.where("#{table}.ref = ? OR #{table}.sha = ?", ref, ref).
success.order(id: :desc)
end
def latest_successful_builds_for(ref = default_branch)
latest_pipeline = pipelines.latest_successful_for(ref).first
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for(ref = 'master')
Ci::Build.joins(:pipeline).
merge(latest_successful_pipeline_for(ref)).
latest_successful_with_artifacts
if latest_pipeline
latest_pipeline.builds.latest.with_artifacts
else
builds.none
end
end
def merge_base_commit(first_commit_id, second_commit_id)
......@@ -665,6 +661,22 @@ class Project < ActiveRecord::Base
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end
def external_wiki
if has_external_wiki.nil?
cache_has_external_wiki # Populate
end
if has_external_wiki
@external_wiki ||= services.external_wikis.first
else
nil
end
end
def cache_has_external_wiki
update_column(:has_external_wiki, services.external_wikis.any?)
end
def build_missing_services
services_templates = Service.where(template: true)
......@@ -853,6 +865,10 @@ class Project < ActiveRecord::Base
protected_branches.matching(branch_name).any?(&:developers_can_push)
end
def developers_can_merge_to_protected_branch?(branch_name)
protected_branches.matching(branch_name).any?(&:developers_can_merge)
end
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
......@@ -1175,4 +1191,34 @@ class Project < ActiveRecord::Base
def ensure_dir_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.path)
end
def predefined_variables
[
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: path, public: true },
{ key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
]
end
def container_registry_variables
return [] unless Gitlab.config.registry.enabled
variables = [
{ key: 'CI_REGISTRY', value: Gitlab.config.registry.host_port, public: true }
]
if container_registry_enabled?
variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_repository_url, public: true }
end
variables
end
def secret_variables
variables.map do |variable|
{ key: variable.key, value: variable.value, public: false }
end
end
end
......@@ -42,6 +42,19 @@ class BuildsEmailService < Service
end
end
def can_test?
project.builds.count > 0
end
def disabled_title
"Please setup a build on your repository."
end
def test_data(project = nil, user = nil)
build = project.builds.last
Gitlab::BuildDataBuilder.build(build)
end
def fields
[
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by comma' },
......@@ -50,6 +63,20 @@ class BuildsEmailService < Service
]
end
def test(data)
begin
# bypass build status verification when testing
data[:build_status] = "failed"
data[:build_allow_failure] = false
result = execute(data)
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result }
end
def should_build_be_notified?(data)
case data[:build_status]
when 'success'
......
......@@ -4,6 +4,9 @@ class SlackService < Service
validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties
# Custom serialized properties initialization
self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) }
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
......@@ -29,13 +32,15 @@ class SlackService < Service
end
def fields
[
{ type: 'text', name: 'webhook',
placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: '#channel' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
]
default_fields =
[
{ type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: "#general" },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
]
default_fields + build_event_channels
end
def supported_events
......@@ -74,7 +79,10 @@ class SlackService < Service
end
opt = {}
opt[:channel] = channel if channel
event_channel = get_channel_field(object_kind) || channel
opt[:channel] = event_channel if event_channel
opt[:username] = username if username
if message
......@@ -83,8 +91,35 @@ class SlackService < Service
end
end
def event_channel_names
supported_events.map { |event| event_channel_name(event) }
end
def event_field(event)
fields.find { |field| field[:name] == event_channel_name(event) }
end
def global_fields
fields.reject { |field| field[:name].end_with?('channel') }
end
private
def get_channel_field(event)
field_name = event_channel_name(event)
self.public_send(field_name)
end
def build_event_channels
supported_events.reduce([]) do |channels, event|
channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" }
end
end
def event_channel_name(event)
"#{event}_channel"
end
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
......
......@@ -392,6 +392,11 @@ class Repository
expire_cache if exists?
# expire cache that don't depend on repository data (when expiring)
expire_tags_cache
expire_tag_count_cache
expire_branches_cache
expire_branch_count_cache
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
......@@ -704,6 +709,7 @@ class Repository
options[:commit] = {
message: message,
branch: ref,
update_ref: false,
}
raw_repository.mkdir(path, options)
......@@ -719,6 +725,7 @@ class Repository
options[:commit] = {
message: message,
branch: ref,
update_ref: false,
}
options[:file] = {
......@@ -731,6 +738,33 @@ class Repository
end
end
def update_file(user, path, content, branch:, previous_path:, message:)
commit_with_hooks(user, branch) do |ref|
committer = user_to_committer(user)
options = {}
options[:committer] = committer
options[:author] = committer
options[:commit] = {
message: message,
branch: ref,
update_ref: false
}
options[:file] = {
content: content,
path: path,
update: true
}
if previous_path
options[:file][:previous_path] = previous_path
Gitlab::Git::Blob.rename(raw_repository, options)
else
Gitlab::Git::Blob.commit(raw_repository, options)
end
end
end
def remove_file(user, path, message, branch)
commit_with_hooks(user, branch) do |ref|
committer = user_to_committer(user)
......@@ -739,7 +773,8 @@ class Repository
options[:author] = committer
options[:commit] = {
message: message,
branch: ref
branch: ref,
update_ref: false,
}
options[:file] = {
......@@ -769,9 +804,9 @@ class Repository
end
end
def merge(user, source_sha, target_branch, options = {})
our_commit = rugged.branches[target_branch].target
their_commit = rugged.lookup(source_sha)
def merge(user, merge_request, options = {})
our_commit = rugged.branches[merge_request.target_branch].target
their_commit = rugged.lookup(merge_request.diff_head_sha)
raise "Invalid merge target" if our_commit.nil?
raise "Invalid merge source" if their_commit.nil?
......@@ -779,14 +814,15 @@ class Repository
merge_index = rugged.merge_commits(our_commit, their_commit)
return false if merge_index.conflicts?
commit_with_hooks(user, target_branch) do |ref|
commit_with_hooks(user, merge_request.target_branch) do
actual_options = options.merge(
parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged),
update_ref: ref
)
Rugged::Commit.create(rugged, actual_options)
commit_id = Rugged::Commit.create(rugged, actual_options)
merge_request.update(in_progress_merge_commit_sha: commit_id)
commit_id
end
end
......@@ -796,15 +832,14 @@ class Repository
return false unless revert_tree_id
commit_with_hooks(user, base_branch) do |ref|
commit_with_hooks(user, base_branch) do
committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
message: commit.revert_message,
author: committer,
committer: committer,
tree: revert_tree_id,
parents: [rugged.lookup(source_sha)],
update_ref: ref)
parents: [rugged.lookup(source_sha)])
end
end
......@@ -814,7 +849,7 @@ class Repository
return false unless cherry_pick_tree_id
commit_with_hooks(user, base_branch) do |ref|
commit_with_hooks(user, base_branch) do
committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
message: commit.message,
......@@ -825,8 +860,7 @@ class Repository
},
committer: committer,
tree: cherry_pick_tree_id,
parents: [rugged.lookup(source_sha)],
update_ref: ref)
parents: [rugged.lookup(source_sha)])
end
end
......@@ -927,20 +961,6 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo)
end
def with_tmp_ref(oldrev = nil)
random_string = SecureRandom.hex
tmp_ref = "refs/tmp/#{random_string}/head"
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
rugged.references.create(tmp_ref, oldrev)
end
# Make commit in tmp ref
yield(tmp_ref)
ensure
rugged.references.delete(tmp_ref) rescue nil
end
def commit_with_hooks(current_user, branch)
update_autocrlf_option
......@@ -953,33 +973,31 @@ class Repository
oldrev = target_branch.target
end
with_tmp_ref(oldrev) do |tmp_ref|
# Make commit in tmp ref
newrev = yield(tmp_ref)
# Make commit
newrev = yield(ref)
unless newrev
raise CommitError.new('Failed to create commit')
end
unless newrev
raise CommitError.new('Failed to create commit')
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty || !target_branch
# Create branch
rugged.references.create(ref, newrev)
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty || !target_branch
# Create branch
rugged.references.create(ref, newrev)
else
# Update head
current_head = find_branch(branch).target
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
else
# Update head
current_head = find_branch(branch).target
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
else
raise CommitError.new('Commit was rejected because branch received new push')
end
raise CommitError.new('Commit was rejected because branch received new push')
end
end
newrev
end
newrev
end
def ls_files(ref)
......
......@@ -17,6 +17,7 @@ class Service < ActiveRecord::Base
after_commit :reset_updated_properties
after_commit :cache_project_has_external_issue_tracker
after_commit :cache_project_has_external_wiki
belongs_to :project, inverse_of: :services
has_one :service_hook
......@@ -25,6 +26,7 @@ class Service < ActiveRecord::Base
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
scope :issue_trackers, -> { where(category: 'issue_tracker') }
scope :external_wikis, -> { where(type: 'ExternalWikiService') }
scope :active, -> { where(active: true) }
scope :without_defaults, -> { where(default: false) }
......@@ -76,6 +78,22 @@ class Service < ActiveRecord::Base
[]
end
def test_data(project, user)
Gitlab::PushDataBuilder.build_sample(project, user)
end
def event_channel_names
[]
end
def event_field(event)
nil
end
def global_fields
fields
end
def supported_events
%w(push tag_push issue merge_request wiki_page)
end
......@@ -94,6 +112,11 @@ class Service < ActiveRecord::Base
!project.empty_repo?
end
# reason why service cannot be tested
def disabled_title
"Please setup a project repository."
end
# Provide convenient accessor methods
# for each serialized property.
# Also keep track of updated properties in a similar way as ActiveModel::Dirty
......@@ -203,4 +226,10 @@ class Service < ActiveRecord::Base
project.cache_has_external_issue_tracker
end
end
def cache_project_has_external_wiki
if project && !project.destroyed?
project.cache_has_external_wiki
end
end
end
......@@ -854,7 +854,7 @@ class User < ActiveRecord::Base
groups.joins(:shared_projects).select(:project_id)]
if min_access_level
scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
scope = { access_level: Gitlab::Access.all_values.select { |access| access >= min_access_level } }
relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) }
end
......
......@@ -15,7 +15,7 @@ module Ci
status == 'success'
when 'on_failure'
status == 'failed'
when 'always'
when 'always', 'manual'
%w(success failed).include?(status)
end
end
......@@ -47,6 +47,10 @@ module Ci
user: user,
project: @pipeline.project)
# TODO: The proper implementation for this is in
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5295
build_attrs[:status] = 'skipped' if build_attrs[:when] == 'manual'
##
# We do not persist new builds here.
# Those will be persisted when @pipeline is saved.
......
......@@ -23,7 +23,7 @@ module Commits
private
def check_push_permissions
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise ValidationError.new('You are not allowed to push into this branch')
......@@ -31,7 +31,7 @@ module Commits
true
end
def create_target_branch(new_branch)
# Temporary branch exists and contains the change commit
return success if repository.find_branch(new_branch)
......
......@@ -15,21 +15,19 @@ class CreateBranchService < BaseService
return error('Branch already exists')
end
new_branch = nil
if source_project != @project
repository.with_tmp_ref do |tmp_ref|
repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{ref}",
tmp_ref
)
new_branch = repository.add_branch(current_user, branch_name, tmp_ref)
end
else
new_branch = repository.add_branch(current_user, branch_name, ref)
end
new_branch = if source_project != @project
repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{ref}",
"refs/heads/#{branch_name}"
)
repository.after_create_branch
repository.find_branch(branch_name)
else
repository.add_branch(current_user, branch_name, ref)
end
if new_branch
success(new_branch)
......
......@@ -9,12 +9,14 @@ module Files
@commit_message = params[:commit_message]
@file_path = params[:file_path]
@previous_path = params[:previous_path]
@file_content = if params[:file_content_encoding] == 'base64'
Base64.decode64(params[:file_content])
else
params[:file_content]
end
# Validate parameters
validate
# Create new branch if it different from source_branch
......@@ -42,7 +44,7 @@ module Files
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise_error("You are not allowed to push into this branch")
......
......@@ -3,7 +3,10 @@ require_relative "base_service"
module Files
class UpdateService < Files::BaseService
def commit
repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, true)
repository.update_file(current_user, @file_path, @file_content,
branch: @target_branch,
previous_path: @previous_path,
message: @commit_message)
end
end
end
......@@ -89,7 +89,8 @@ class GitPushService < BaseService
# Set protection on the default branch if configured
if current_application_settings.default_branch_protection != PROTECTION_NONE
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
developers_can_merge = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge })
end
end
......
......@@ -101,6 +101,7 @@ class IssuableBaseService < BaseService
def update(issuable)
change_state(issuable)
change_subscription(issuable)
filter_params
old_labels = issuable.labels.to_a
......@@ -124,6 +125,15 @@ class IssuableBaseService < BaseService
end
end
def change_subscription(issuable)
case params.delete(:subscription_event)
when 'subscribe'
issuable.subscribe(current_user)
when 'unsubscribe'
issuable.unsubscribe(current_user)
end
end
def has_changes?(issuable, old_labels: [])
valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]
......
......@@ -4,7 +4,7 @@ module Issues
issues_ids = params.delete(:issues_ids).split(",")
issue_params = params
%i(state_event milestone_id assignee_id add_label_ids remove_label_ids).each do |key|
%i(state_event milestone_id assignee_id add_label_ids remove_label_ids subscription_event).each do |key|
issue_params.delete(key) unless issue_params[key].present?
end
......
......@@ -34,7 +34,7 @@ module MergeRequests
committer: committer
}
commit_id = repository.merge(current_user, merge_request.diff_head_sha, merge_request.target_branch, options)
commit_id = repository.merge(current_user, merge_request, options)
merge_request.update(merge_commit_sha: commit_id)
rescue GitHooksService::PreReceiveError => e
merge_request.update(merge_error: e.message)
......@@ -43,6 +43,8 @@ module MergeRequests
merge_request.update(merge_error: "Something went wrong during merge")
Rails.logger.error(e.message)
false
ensure
merge_request.update(in_progress_merge_commit_sha: nil)
end
def after_merge
......
......@@ -48,7 +48,7 @@ module MergeRequests
end
def force_push?
Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
end
# Refresh merge request diff if we push to source or target branch of merge request
......
......@@ -9,7 +9,7 @@ module Projects
private
def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
if [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success
else
......@@ -21,6 +21,10 @@ module Projects
Gitlab::ImportExport::VersionSaver.new(shared: @shared)
end
def avatar_saver
Gitlab::ImportExport::AvatarSaver.new(project: project, shared: @shared)
end
def project_tree_saver
Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
end
......
......@@ -14,4 +14,8 @@ class AvatarUploader < CarrierWave::Uploader::Base
def reset_events_cache(file)
model.reset_events_cache if model.is_a?(User)
end
def exists?
model.avatar.file && model.avatar.file.exists?
end
end
......@@ -9,6 +9,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
- if @group.new_record?
.form-group
.col-sm-offset-2.col-sm-10
......
.emoji-menu
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis"
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Search emoji"
.emoji-menu-content
- Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis|
%h5.emoji-menu-title
......
......@@ -4,11 +4,7 @@
#{time_ago_with_tooltip(event.created_at)}
= cache [event, current_application_settings, "v2.2"] do
- if event.author
= link_to user_path(event.author) do
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
- else
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
= author_avatar(event, size: 40)
- if event.created_project?
= render "events/event/created_project", event: event
......
......@@ -21,6 +21,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
.form-group
%hr
= f.label :share_with_group_lock, class: 'control-label' do
......
- project = @target_project || @project
- noteable_class = @noteable.class if @noteable.present?
- if @noteable
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: @noteable.class, type_id: params[:id])}"
GitLab.GfmAutoComplete.cachedData = undefined;
GitLab.GfmAutoComplete.setup();
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_class, type_id: params[:id])}"
GitLab.GfmAutoComplete.cachedData = undefined;
GitLab.GfmAutoComplete.setup();
%ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do
= icon('bookmark fw')
%span
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
= icon('question-circle fw')
%span
Help
......@@ -39,7 +39,7 @@
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
%span
Triggers
= nav_link(controller: :badges) do
= link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
= nav_link(controller: :pipelines_settings) do
= link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
%span
Badges
CI/CD Pipelines
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/cropper.js')
= page_specific_javascript_tag('profile/application.js')
= page_specific_javascript_tag('profile/profile_bundle.js')
%fieldset.builds-feature
%h5.prepend-top-0
Builds
- unless @repository.gitlab_ci_yml
.form-group
%p Builds need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
.form-group
%p Get recent application code using the following command:
.radio
= f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false'
%strong git clone
%br
%span.descr Slower but makes sure you have a clean dir before every build
.radio
= f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch
%br
%span.descr Faster
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block per build in minutes
.form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
%span.input-group-addon /
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon /
%p.help-block
We will use this regular expression to find test coverage output in build trace.
Leave blank if you want to disable this feature
.bs-callout.bs-callout-info
%p Below are examples of regex for existing tools:
%ul
%li
Simplecov (Ruby) -
%code \(\d+.\d+\%\) covered
%li
pytest-cov (Python) -
%code \d+\%\s*$
%li
phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\%
%li
gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$
%li
tap --coverage-report=text-summary (Node.js) -
%code ^Statements\s*:\s*([^%]+)
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public builds
.help-block Allow everyone to access builds for Public and Internal projects
.form-group.append-bottom-0
= f.label :runners_token, "Runners token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used to checkout project.
- page_title 'Badges'
- badges_path = namespace_project_badges_path(@project.namespace, @project)
.prepend-top-10
.panel.panel-default
.panel-heading
%b Builds badge &middot;
= @build_badge.to_html
.pull-right
= render 'shared/ref_switcher', destination: 'badges', align_right: true
.panel-body
.row
.col-md-2.text-center
Markdown
.col-md-10.code.js-syntax-highlight
= highlight('.md', @build_badge.to_markdown)
.row
%hr
.row
.col-md-2.text-center
HTML
.col-md-10.code.js-syntax-highlight
= highlight('.html', @build_badge.to_html)
......@@ -4,7 +4,9 @@
= icon('code-fork')
= ref
%span.editor-file-name
= @path
- if current_action?(:edit) || current_action?(:update)
= text_field_tag 'file_path', (params[:file_path] || @path),
class: 'form-control new-file-path'
- if current_action?(:new) || current_action?(:create)
%span.editor-file-name
......
......@@ -27,9 +27,9 @@
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do
Compare
- pipeline = @project.latest_successful_pipeline_for(branch.name).first
- pipeline = @project.pipelines.latest_successful_for(branch.name).first
- if pipeline
- artifacts = pipeline.builds.latest_successful_with_artifacts
- artifacts = pipeline.builds.latest.with_artifacts
- if artifacts.any?
.dropdown.inline.artifacts-btn
%a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
......
......@@ -49,7 +49,7 @@
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
#{duration_in_words(@build.finished_at, @build.started_at)}
= time_interval_in_words(@build.duration)
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
......@@ -94,9 +94,9 @@
.block
.title
Commit message
Commit title
%p.build-light-text.append-bottom-0
#{@build.pipeline.git_commit_message}
#{@build.pipeline.git_commit_title}
- if @build.tags.any?
.block
......
......@@ -14,9 +14,9 @@
%li
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do
%span Download tar.gz
- pipeline = @project.latest_successful_pipeline_for(@ref).first
- pipeline = @project.pipelines.latest_successful_for(@ref).first
- if pipeline
- artifacts = pipeline.builds.latest_successful_with_artifacts
- artifacts = pipeline.builds.latest.with_artifacts
- if artifacts.any?
%li.dropdown-header Artifacts
- unless pipeline.latest?
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment