summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--app/assets/javascripts/header.js6
-rw-r--r--app/assets/javascripts/main.js92
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue10
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue6
-rw-r--r--app/assets/stylesheets/pages/diff.scss2
-rw-r--r--app/controllers/projects/artifacts_controller.rb9
-rw-r--r--app/controllers/projects/build_artifacts_controller.rb9
-rw-r--r--app/models/project.rb18
-rw-r--r--changelogs/unreleased/monospace-registry-tags.yml5
-rw-r--r--changelogs/unreleased/notes-awards-double-tooltip-fix.yml5
-rw-r--r--changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml5
-rw-r--r--doc/development/documentation/styleguide.md64
-rw-r--r--lib/api/job_artifacts.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb11
-rw-r--r--spec/models/project_spec.rb162
-rw-r--r--spec/simplecov_env.rb6
17 files changed, 250 insertions, 168 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3d12f4142ba..45de5ce61c6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -315,6 +315,7 @@ cloud-native-image:
variables:
GIT_DEPTH: "1"
cache: {}
+ when: always
script:
- gem install gitlab --no-document
- CNG_PROJECT_PATH="gitlab-org/build/CNG" BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN ./scripts/trigger-build cng
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index 2fa7a219ea8..3d846310008 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -23,7 +23,7 @@ export default function initTodoToggle() {
});
}
-document.addEventListener('DOMContentLoaded', () => {
+function initStatusTriggers() {
const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger');
const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper');
@@ -72,4 +72,8 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
}
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ requestIdleCallback(initStatusTriggers);
});
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c866e8d180a..4ba3543f9b2 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -66,15 +66,11 @@ gl.lazyLoader = new LazyLoader({
observerNode: '#content-body',
});
-document.addEventListener('DOMContentLoaded', () => {
+// Put all initialisations here that can also wait after everything is rendered and ready
+function deferredInitialisation() {
const $body = $('body');
- const $document = $(document);
- const $window = $(window);
- const $sidebarGutterToggle = $('.js-sidebar-toggle');
- let bootstrapBreakpoint = bp.getBreakpointSize();
initBreadcrumbs();
- initLayoutNav();
initImporterStatus();
initTodoToggle();
initLogoAnimation();
@@ -84,34 +80,6 @@ document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('.search')) initSearchAutocomplete();
if (document.querySelector('#js-peek')) initPerformanceBar({ container: '#js-peek' });
- // Set the default path for all cookies to GitLab's root directory
- Cookies.defaults.path = gon.relative_url_root || '/';
-
- // `hashchange` is not triggered when link target is already in window.location
- $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() {
- const href = this.getAttribute('href');
- if (href.substr(1) === getLocationHash()) {
- setTimeout(handleLocationHash, 1);
- }
- });
-
- if (bootstrapBreakpoint === 'xs') {
- const $rightSidebar = $('aside.right-sidebar, .layout-page');
-
- $rightSidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
- }
-
- // prevent default action for disabled buttons
- $('.btn').click(function clickDisabledButtonCallback(e) {
- if ($(this).hasClass('disabled')) {
- e.preventDefault();
- e.stopImmediatePropagation();
- return false;
- }
-
- return true;
- });
-
addSelectOnFocusBehaviour('.js-select-on-focus');
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
@@ -164,6 +132,48 @@ document.addEventListener('DOMContentLoaded', () => {
viewport: '.layout-page',
});
+ loadAwardsHandler();
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const $body = $('body');
+ const $document = $(document);
+ const $window = $(window);
+ const $sidebarGutterToggle = $('.js-sidebar-toggle');
+ let bootstrapBreakpoint = bp.getBreakpointSize();
+
+ initLayoutNav();
+
+ // Set the default path for all cookies to GitLab's root directory
+ Cookies.defaults.path = gon.relative_url_root || '/';
+
+ // `hashchange` is not triggered when link target is already in window.location
+ $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() {
+ const href = this.getAttribute('href');
+ if (href.substr(1) === getLocationHash()) {
+ setTimeout(handleLocationHash, 1);
+ }
+ });
+
+ if (bootstrapBreakpoint === 'xs') {
+ const $rightSidebar = $('aside.right-sidebar, .layout-page');
+
+ $rightSidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
+ }
+
+ // prevent default action for disabled buttons
+ $('.btn').click(function clickDisabledButtonCallback(e) {
+ if ($(this).hasClass('disabled')) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return false;
+ }
+
+ return true;
+ });
+
+ localTimeAgo($('abbr.timeago, .js-timeago'), true);
+
// Form submitter
$('.trigger-submit').on('change', function triggerSubmitCallback() {
$(this)
@@ -171,8 +181,6 @@ document.addEventListener('DOMContentLoaded', () => {
.submit();
});
- localTimeAgo($('abbr.timeago, .js-timeago'), true);
-
// Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function ajaxCompleteCallback(e) {
const $buttons = $('[type="submit"], .js-disable-on-submit', this);
@@ -195,6 +203,10 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
+ $('.navbar-toggler').on('click', () => {
+ $('.header-content').toggleClass('menu-expanded');
+ });
+
// Commit show suppressed diff
$document.on('click', '.diff-content .js-show-suppressed-diff', function showDiffCallback() {
const $container = $(this).parent();
@@ -202,10 +214,6 @@ document.addEventListener('DOMContentLoaded', () => {
$container.remove();
});
- $('.navbar-toggler').on('click', () => {
- $('.header-content').toggleClass('menu-expanded');
- });
-
// Show/hide comments on diff
$body.on('click', '.js-toggle-diff-comments', function toggleDiffCommentsCallback(e) {
const $this = $(this);
@@ -250,8 +258,6 @@ document.addEventListener('DOMContentLoaded', () => {
$window.on('resize.app', fitSidebarForSize);
- loadAwardsHandler();
-
$('form.filter-form').on('submit', function filterFormSubmitCallback(event) {
const link = document.createElement('a');
link.href = this.action;
@@ -274,4 +280,6 @@ document.addEventListener('DOMContentLoaded', () => {
// initialize field errors
$('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form));
+
+ requestIdleCallback(deferredInitialisation);
});
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index 3d60eb02db8..bde00ea87ff 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapGetters } from 'vuex';
-import { GlTooltipDirective } from '@gitlab/ui';
+import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
import { glEmojiTag } from '../../emoji';
@@ -10,7 +10,7 @@ export default {
Icon,
},
directives: {
- GlTooltip: GlTooltipDirective,
+ tooltip,
},
props: {
awards: {
@@ -167,9 +167,11 @@ export default {
<button
v-for="(awardList, awardName, index) in groupedAwards"
:key="index"
- v-gl-tooltip.bottom="{ boundary: 'viewport' }"
+ v-tooltip
:class="getAwardClassBindings(awardList)"
:title="awardTitle(awardList)"
+ data-boundary="viewport"
+ data-placement="bottom"
class="btn award-control"
type="button"
@click="handleAward(awardName);"
@@ -179,7 +181,7 @@ export default {
</button>
<div v-if="canAwardEmoji" class="award-menu-holder">
<button
- v-gl-tooltip
+ v-tooltip
:class="{ 'js-user-authored': isAuthoredByMe }"
class="award-control btn js-add-award"
title="Add reaction"
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 78c7671856a..2c19973a114 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -70,7 +70,7 @@ export default {
</thead>
<tbody>
<tr v-for="item in repo.list" :key="item.tag">
- <td>
+ <td class="monospace">
{{ item.tag }}
<clipboard-button
v-if="item.location"
@@ -80,7 +80,9 @@ export default {
/>
</td>
<td>
- <span v-gl-tooltip.bottom :title="item.revision">{{ item.shortRevision }}</span>
+ <span v-gl-tooltip.bottom class="monospace" :title="item.revision">{{
+ item.shortRevision
+ }}</span>
</td>
<td>
{{ formatSize(item.size) }}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 18c62cb4f1e..b78f11aadf1 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -9,7 +9,7 @@
@media (min-width: map-get($grid-breakpoints, md)) {
position: -webkit-sticky;
position: sticky;
- top: 92px;
+ top: $header-height + 51px;
margin-left: -1px;
border-left: 1px solid $border-color;
z-index: 102;
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 1a91e07b97f..9a0997e92ee 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -85,20 +85,15 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def build_from_id
- project.builds.find_by(id: params[:job_id]) if params[:job_id]
+ project.get_build(params[:job_id]) if params[:job_id]
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def build_from_ref
return unless @ref_name
- builds = project.latest_successful_builds_for(@ref_name)
- builds.find_by(name: params[:job])
+ project.latest_successful_build_for(params[:job], @ref_name)
end
- # rubocop: enable CodeReuse/ActiveRecord
def artifacts_file
@artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive)
diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb
index 7d4d566499c..d3d5ba5c75d 100644
--- a/app/controllers/projects/build_artifacts_controller.rb
+++ b/app/controllers/projects/build_artifacts_controller.rb
@@ -44,18 +44,13 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
@job ||= job_from_id || job_from_ref
end
- # rubocop: disable CodeReuse/ActiveRecord
def job_from_id
- project.builds.find_by(id: params[:build_id]) if params[:build_id]
+ project.get_build(params[:build_id]) if params[:build_id]
end
- # rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def job_from_ref
return unless @ref_name
- jobs = project.latest_successful_builds_for(@ref_name)
- jobs.find_by(name: params[:job])
+ project.latest_successful_build_for(params[:job], @ref_name)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/models/project.rb b/app/models/project.rb
index cab173503ce..a66ed6736ca 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -647,19 +647,19 @@ class Project < ActiveRecord::Base
end
# ref can't be HEAD, can only be branch/tag name or SHA
- def latest_successful_builds_for(ref = default_branch)
+ def latest_successful_build_for(job_name, ref = default_branch)
latest_pipeline = ci_pipelines.latest_successful_for(ref)
+ return unless latest_pipeline
- if latest_pipeline
- latest_pipeline.builds.latest.with_artifacts_archive
- else
- builds.none
- end
+ latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name)
end
- def latest_successful_build_for(job_name, ref = default_branch)
- builds = latest_successful_builds_for(ref)
- builds.find_by!(name: job_name)
+ def latest_successful_build_for!(job_name, ref = default_branch)
+ latest_successful_build_for(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
+ end
+
+ def get_build(id)
+ builds.find_by(id: id)
end
def merge_base_commit(first_commit_id, second_commit_id)
diff --git a/changelogs/unreleased/monospace-registry-tags.yml b/changelogs/unreleased/monospace-registry-tags.yml
new file mode 100644
index 00000000000..b5992707d8c
--- /dev/null
+++ b/changelogs/unreleased/monospace-registry-tags.yml
@@ -0,0 +1,5 @@
+---
+title: Use monospace font for registry table tag id and tag name
+merge_request: 24205
+author:
+type: other
diff --git a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
new file mode 100644
index 00000000000..23338a60c2a
--- /dev/null
+++ b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed double tooltips on note awards buttons
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
new file mode 100644
index 00000000000..ce8e1829b48
--- /dev/null
+++ b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
@@ -0,0 +1,5 @@
+---
+title: Remove extra space between MR tab bar and sticky file headers
+merge_request:
+author:
+type: fixed
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index a18c21d921a..721e26f2de9 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -15,7 +15,7 @@ For programmatic help adhering to the guidelines, see [linting](index.md#linting
## Files
- [Directory structure](index.md#location-and-naming-documents): place the docs
-in the correct location.
+ in the correct location.
- [Documentation files](index.md#documentation-files): name the files accordingly.
DANGER: **Attention:**
@@ -29,12 +29,12 @@ a test that will fail if it spots a new `README.md` file.
### Markdown
The [documentation website](https://docs.gitlab.com) had its markdown engine migrated from [Redcarpet to GitLab Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108)
-in October, 2018.
+in October 2018.
The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown)
gem will support all [GFM markup](../../user/markdown.md) in the future. For now,
use regular markdown markup, following the rules on this style guide. For a complete
-Kramdown reference, check the [GiLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+Kramdown reference, check the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
Use Kramdown markup wisely: do not overuse its specific markup (e.g., `{:.class}`) as it will not render properly in
[`/help`](#gitlab-help).
@@ -45,38 +45,38 @@ yield a useful result, and ensuring content is helpful and easy to consume.
- What to include:
- Any and all helpful information, processes, and tips for implementing,
-using, and troubleshooting GitLab features. [The documentation is the single source of truth](https://about.gitlab.com/handbook/documentation/#documentation-as-single-source-of-truth-ssot)
-for this information.
+ using, and troubleshooting GitLab features. [The documentation is the single source of truth](https://about.gitlab.com/handbook/documentation/#documentation-as-single-source-of-truth-ssot)
+ for this information.
- 'Risky' or niche problem-solving steps. There is no reason to withhold these or
-store them elsewhere; simply include them along with the rest of the docs including all necessary
-detail, such as specific warnings and caveats about potential ramifications.
+ store them elsewhere; simply include them along with the rest of the docs including all necessary
+ detail, such as specific warnings and caveats about potential ramifications.
- Any content types/sources, if relevant to users or admins. You can freely
-include presentations, videos, etc.; no matter who it was originally written for,
-if it is helpful to any of our audiences, we can include it. If an outside source
-that's under copyright, rephrase, or summarize and link out; do not copy and paste.
+ include presentations, videos, etc.; no matter who it was originally written for,
+ if it is helpful to any of our audiences, we can include it. If an outside source
+ that's under copyright, rephrase, or summarize and link out; do not copy and paste.
- All applicable subsections as described on the [structure and template](structure.md) page,
-with files organized in the [correct directory](index.md#documentation-directory-structure).
+ with files organized in the [correct directory](index.md#documentation-directory-structure).
- To ensure discoverability, link to each doc from its higher-level index page and other related pages.
- When referencing other GitLab products and features, link to their
respective docs; when referencing third-party products or technologies,
link out to their external sites, documentation, and resources.
- Do not duplicate information.
- Structure content in alphabetical order in tables, lists, etc., unless there is
-a logical reason not to (for example, when mirroring the UI or an ordered sequence).
+ a logical reason not to (for example, when mirroring the UI or an ordered sequence).
## Language
- Use inclusive language and avoid jargon, as well as uncommon
-words. The docs should be clear and easy to understand.
+ words. The docs should be clear and easy to understand.
- Write in the 3rd person (use "we", "you", "us", "one", instead of "I" or "me").
- Be clear, concise, and stick to the goal of the doc.
- Write in US English.
- Capitalize "G" and "L" in GitLab.
- Use title case when referring to [features](https://about.gitlab.com/features/) or
-[products](https://about.gitlab.com/pricing/) (e.g., GitLab Runner, Geo,
-Issue Boards, GitLab Core, Git, Prometheus, Kubernetes, etc), and methods or methodologies
-(e.g., Continuous Integration, Continuous Deployment, Scrum, Agile, etc). Note that
-some features are also objects (e.g. "GitLab's Merge Requests support X." and "Create a new merge request for Z.").
+ [products](https://about.gitlab.com/pricing/) (e.g., GitLab Runner, Geo,
+ Issue Boards, GitLab Core, Git, Prometheus, Kubernetes, etc), and methods or methodologies
+ (e.g., Continuous Integration, Continuous Deployment, Scrum, Agile, etc). Note that
+ some features are also objects (e.g. "GitLab's Merge Requests support X." and "Create a new merge request for Z.").
## Text
@@ -127,9 +127,9 @@ Check specific punctuation rules for [list items](#list-items) below.
**Markup:**
-- Use dashes (`- `) for unordered lists instead of asterisks (`* `).
+- Use dashes (`-`) for unordered lists instead of asterisks (`*`).
- Use the number one (`1`) for each item in an ordered list.
-When rendered, the list items will appear with sequential numbering.
+ When rendered, the list items will appear with sequential numbering.
**Punctuation:**
@@ -226,12 +226,11 @@ For other punctuation rules, please refer to the
To indicate the steps of navigation through the UI:
-
- Use the exact word as shown in the UI, including any capital letters as-is.
- Use bold text for navigation items and the char "greater than" (`>`) as separator
-(e.g., `Navigate to your project's **Settings > CI/CD**` ).
+ (e.g., `Navigate to your project's **Settings > CI/CD**` ).
- If there are any expandable menus, make sure to mention that the user
-needs to expand the tab to find the settings you're referring to (e.g., `Navigate to your project's **Settings > CI/CD** and expand **General pipelines**`).
+ needs to expand the tab to find the settings you're referring to (e.g., `Navigate to your project's **Settings > CI/CD** and expand **General pipelines**`).
## Images
@@ -246,7 +245,7 @@ needs to expand the tab to find the settings you're referring to (e.g., `Navigat
- Compress all images with <https://tinypng.com/> or similar tool.
- Compress gifs with <https://ezgif.com/optimize> or similar tool.
- Images should be used (only when necessary) to _illustrate_ the description
-of a process, not to _replace_ it.
+ of a process, not to _replace_ it.
- Max image size: 100KB (gifs included).
- The GitLab docs do not support videos yet.
@@ -266,12 +265,12 @@ Inside the document:
## Code blocks
- Always wrap code added to a sentence in inline code blocks (``` ` ```).
-E.g., `.gitlab-ci.yml`, `git add .`, `CODEOWNERS`, `only: master`.
-File names, commands, entries, and anything that refers to code should be added to code blocks.
-To make things easier for the user, always add a full code block for things that can be
-useful to copy and paste, as they can easily do it with the button on code blocks.
+ E.g., `.gitlab-ci.yml`, `git add .`, `CODEOWNERS`, `only: master`.
+ File names, commands, entries, and anything that refers to code should be added to code blocks.
+ To make things easier for the user, always add a full code block for things that can be
+ useful to copy and paste, as they can easily do it with the button on code blocks.
- For regular code blocks, always use a highlighting class corresponding to the
-language for better readability. Examples:
+ language for better readability. Examples:
```md
```ruby
@@ -592,12 +591,12 @@ on this document. Further explanation is given below.
The following can be used as a template to get started:
-```md
+````md
## Descriptive title
One or two sentence description of what endpoint does.
-```
+```text
METHOD /endpoint
```
@@ -609,7 +608,7 @@ METHOD /endpoint
Example request:
```sh
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/endpoint?parameters'
+curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/endpoint?parameters'
```
Example response:
@@ -620,8 +619,7 @@ Example response:
}
]
```
-
-```
+````
### Fake tokens
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index a4068a200b3..933bd067e26 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -23,17 +23,14 @@ module API
requires :job, type: String, desc: 'The name for the job'
end
route_setting :authentication, job_token_allowed: true
- # rubocop: disable CodeReuse/ActiveRecord
get ':id/jobs/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_download_artifacts!
- builds = user_project.latest_successful_builds_for(params[:ref_name])
- latest_build = builds.find_by!(name: params[:job])
+ latest_build = user_project.latest_successful_build_for!(params[:job], params[:ref_name])
present_carrierwave_file!(latest_build.artifacts_file)
end
- # rubocop: enable CodeReuse/ActiveRecord
desc 'Download a specific file from artifacts archive from a ref' do
detail 'This feature was introduced in GitLab 11.5'
@@ -48,7 +45,7 @@ module API
requirements: { ref_name: /.+/ } do
authorize_download_artifacts!
- build = user_project.latest_successful_build_for(params[:job], params[:ref_name])
+ build = user_project.latest_successful_build_for!(params[:job], params[:ref_name])
path = Gitlab::Ci::Build::Artifacts::Path
.new(params[:artifact_path])
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 5ee8df03d50..5147b17d7ab 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -98,6 +98,17 @@ module QA
resource.value = 'You can see this application secret'
end
+ # Our current Auto DevOps implementation won't update the production
+ # app if we only update a CI variable with no code change.
+ #
+ # Workaround: push new code and use the resultant pipeline.
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = @project
+ push.commit_message = 'Force a Deployment change by pushing new code'
+ push.file_name = 'new_file.txt'
+ push.file_content = 'new file contents'
+ end
+
@project.visit!
Page::Project::Menu.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d1ab0bdba29..3044150bca8 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1105,13 +1105,13 @@ describe Project do
describe '#pipeline_for' do
let(:project) { create(:project, :repository) }
- let!(:pipeline) { create_pipeline }
+ let!(:pipeline) { create_pipeline(project) }
shared_examples 'giving the correct pipeline' do
it { is_expected.to eq(pipeline) }
context 'return latest' do
- let!(:pipeline2) { create_pipeline }
+ let!(:pipeline2) { create_pipeline(project) }
it { is_expected.to eq(pipeline2) }
end
@@ -1128,13 +1128,6 @@ describe Project do
it_behaves_like 'giving the correct pipeline'
end
-
- def create_pipeline
- create(:ci_pipeline,
- project: project,
- ref: 'master',
- sha: project.commit('master').sha)
- end
end
describe '#builds_enabled' do
@@ -1922,38 +1915,21 @@ describe Project do
end
end
- describe '#latest_successful_builds_for and #latest_successful_build_for' do
- def create_pipeline(status = 'success')
- create(:ci_pipeline, project: project,
- sha: project.commit.sha,
- ref: project.default_branch,
- status: status)
- end
-
- def create_build(new_pipeline = pipeline, name = 'test')
- create(:ci_build, :success, :artifacts,
- pipeline: new_pipeline,
- status: new_pipeline.status,
- name: name)
- end
-
+ describe '#latest_successful_build_for' do
let(:project) { create(:project, :repository) }
- let(:pipeline) { create_pipeline }
+ let(:pipeline) { create_pipeline(project) }
context 'with many builds' do
it 'gives the latest builds from latest pipeline' do
- pipeline1 = create_pipeline
- pipeline2 = create_pipeline
+ pipeline1 = create_pipeline(project)
+ pipeline2 = create_pipeline(project)
create_build(pipeline1, 'test')
create_build(pipeline1, 'test2')
build1_p2 = create_build(pipeline2, 'test')
- build2_p2 = create_build(pipeline2, 'test2')
+ create_build(pipeline2, 'test2')
- latest_builds = project.latest_successful_builds_for
- single_build = project.latest_successful_build_for(build1_p2.name)
-
- expect(latest_builds).to contain_exactly(build2_p2, build1_p2)
- expect(single_build).to eq(build1_p2)
+ expect(project.latest_successful_build_for(build1_p2.name))
+ .to eq(build1_p2)
end
end
@@ -1962,53 +1938,115 @@ describe Project do
context 'standalone pipeline' do
it 'returns builds for ref for default_branch' do
- builds = project.latest_successful_builds_for
- single_build = project.latest_successful_build_for(build.name)
+ expect(project.latest_successful_build_for(build.name))
+ .to eq(build)
+ end
- expect(builds).to contain_exactly(build)
- expect(single_build).to eq(build)
+ it 'returns empty relation if the build cannot be found' do
+ expect(project.latest_successful_build_for('TAIL'))
+ .to be_nil
end
+ end
- it 'returns empty relation if the build cannot be found for #latest_successful_builds_for' do
- builds = project.latest_successful_builds_for('TAIL')
+ context 'with some pending pipeline' do
+ before do
+ create_build(create_pipeline(project, 'pending'))
+ end
- expect(builds).to be_kind_of(ActiveRecord::Relation)
- expect(builds).to be_empty
+ it 'gives the latest build from latest pipeline' do
+ expect(project.latest_successful_build_for(build.name))
+ .to eq(build)
end
+ end
+ end
- it 'returns exception if the build cannot be found for #latest_successful_build_for' do
- expect { project.latest_successful_build_for(build.name, 'TAIL') }.to raise_error(ActiveRecord::RecordNotFound)
+ context 'with pending pipeline' do
+ it 'returns empty relation' do
+ pipeline.update(status: 'pending')
+ pending_build = create_build(pipeline)
+
+ expect(project.latest_successful_build_for(pending_build.name)).to be_nil
+ end
+ end
+ end
+
+ describe '#latest_successful_build_for!' do
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create_pipeline(project) }
+
+ context 'with many builds' do
+ it 'gives the latest builds from latest pipeline' do
+ pipeline1 = create_pipeline(project)
+ pipeline2 = create_pipeline(project)
+ create_build(pipeline1, 'test')
+ create_build(pipeline1, 'test2')
+ build1_p2 = create_build(pipeline2, 'test')
+ create_build(pipeline2, 'test2')
+
+ expect(project.latest_successful_build_for(build1_p2.name))
+ .to eq(build1_p2)
+ end
+ end
+
+ context 'with succeeded pipeline' do
+ let!(:build) { create_build }
+
+ context 'standalone pipeline' do
+ it 'returns builds for ref for default_branch' do
+ expect(project.latest_successful_build_for!(build.name))
+ .to eq(build)
+ end
+
+ it 'returns exception if the build cannot be found' do
+ expect { project.latest_successful_build_for!(build.name, 'TAIL') }
+ .to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with some pending pipeline' do
before do
- create_build(create_pipeline('pending'))
+ create_build(create_pipeline(project, 'pending'))
end
it 'gives the latest build from latest pipeline' do
- latest_builds = project.latest_successful_builds_for
- last_single_build = project.latest_successful_build_for(build.name)
-
- expect(latest_builds).to contain_exactly(build)
- expect(last_single_build).to eq(build)
+ expect(project.latest_successful_build_for!(build.name))
+ .to eq(build)
end
end
end
context 'with pending pipeline' do
- before do
+ it 'returns empty relation' do
pipeline.update(status: 'pending')
- create_build(pipeline)
+ pending_build = create_build(pipeline)
+
+ expect { project.latest_successful_build_for!(pending_build.name) }
+ .to raise_error(ActiveRecord::RecordNotFound)
end
+ end
+ end
- it 'returns empty relation' do
- builds = project.latest_successful_builds_for
+ describe '#get_build' do
+ let(:project) { create(:project, :repository) }
+ let(:ci_pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when build exists' do
+ context 'build is associated with project' do
+ let(:build) { create(:ci_build, :success, pipeline: ci_pipeline) }
+
+ it { expect(project.get_build(build.id)).to eq(build) }
+ end
- expect(builds).to be_kind_of(ActiveRecord::Relation)
- expect(builds).to be_empty
+ context 'build is not associated with project' do
+ let(:build) { create(:ci_build, :success) }
+
+ it { expect(project.get_build(build.id)).to be_nil }
end
end
+
+ context 'build does not exists' do
+ it { expect(project.get_build(rand 100)).to be_nil }
+ end
end
describe '#import_status' do
@@ -4390,4 +4428,18 @@ describe Project do
def rugged_config
rugged_repo(project.repository).config
end
+
+ def create_pipeline(project, status = 'success')
+ create(:ci_pipeline, project: project,
+ sha: project.commit.sha,
+ ref: project.default_branch,
+ status: status)
+ end
+
+ def create_build(new_pipeline = pipeline, name = 'test')
+ create(:ci_build, :success, :artifacts,
+ pipeline: new_pipeline,
+ status: new_pipeline.status,
+ name: name)
+ end
end
diff --git a/spec/simplecov_env.rb b/spec/simplecov_env.rb
index 25ddf932d42..82236bb4201 100644
--- a/spec/simplecov_env.rb
+++ b/spec/simplecov_env.rb
@@ -1,5 +1,6 @@
require 'simplecov'
require 'active_support/core_ext/numeric/time'
+require_relative '../lib/gitlab/utils'
module SimpleCovEnv
extend self
@@ -16,8 +17,9 @@ module SimpleCovEnv
def configure_job
SimpleCov.configure do
if ENV['CI_JOB_NAME']
- coverage_dir "coverage/#{ENV['CI_JOB_NAME']}"
- command_name ENV['CI_JOB_NAME']
+ job_name = Gitlab::Utils.slugify(ENV['CI_JOB_NAME'])
+ coverage_dir "coverage/#{job_name}"
+ command_name job_name
end
if ENV['CI']