diff options
-rw-r--r-- | .gitlab-ci.yml | 1 | ||||
-rw-r--r-- | app/assets/javascripts/header.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/main.js | 92 | ||||
-rw-r--r-- | app/assets/javascripts/notes/components/note_awards_list.vue | 10 | ||||
-rw-r--r-- | app/assets/javascripts/registry/components/table_registry.vue | 6 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/diff.scss | 2 | ||||
-rw-r--r-- | app/controllers/projects/artifacts_controller.rb | 9 | ||||
-rw-r--r-- | app/controllers/projects/build_artifacts_controller.rb | 9 | ||||
-rw-r--r-- | app/models/project.rb | 18 | ||||
-rw-r--r-- | changelogs/unreleased/monospace-registry-tags.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/notes-awards-double-tooltip-fix.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml | 5 | ||||
-rw-r--r-- | doc/development/documentation/styleguide.md | 64 | ||||
-rw-r--r-- | lib/api/job_artifacts.rb | 7 | ||||
-rw-r--r-- | qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb | 11 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 162 | ||||
-rw-r--r-- | spec/simplecov_env.rb | 6 |
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'] |