diff --git a/CHANGELOG b/CHANGELOG index b67f010c6a0..0c9fd2c9087 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,9 +13,13 @@ v 8.2.0 (unreleased) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page + - Extend yml syntax for only and except to support specifying repository path - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - [API] Add ability to fetch the commit ID of the last commit that actually touched a file + - Add "New file" link to dropdown on project page + - Include commit logs in project search v 8.1.3 - Spread out runner contacted_at updates diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..e485487bcfd 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,6 +33,8 @@ } li.commit { + list-style: none; + .commit-row-title { font-size: $list-font-size; line-height: 20px; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 05c7d3de8bc..00d13a83ce8 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,7 +1,7 @@ class ProjectsController < ApplicationController include ExtractsPath - prepend_before_filter :render_go_import, only: [:show] + prepend_before_action :render_go_import, only: [:show] skip_before_action :authenticate_user!, only: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index eb0408a95e5..9bb42ec86b3 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -23,8 +23,8 @@ class SearchController < ApplicationController @search_results = if @project - unless %w(blobs notes issues merge_requests milestones wiki_blobs). - include?(@scope) + unless %w(blobs notes issues merge_requests milestones wiki_blobs + commits).include?(@scope) @scope = 'blobs' end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 13437b2483f..e58420d82d4 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -187,7 +187,7 @@ module Ci end def config_processor - @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil diff --git a/app/models/repository.rb b/app/models/repository.rb index c9b36bd8170..9266ba27f0a 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,6 +87,15 @@ class Repository commits end + def find_commits_by_message(query) + # Limited to 1000 commits for now, could be parameterized? + args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) + + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) + commits = git_log_results.map { |c| commit(c) } + commits + end + def find_branch(name) branches.find { |branch| branch.name == name } end diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index ceb64ce3157..d1aa8f62463 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -11,6 +11,8 @@ = hidden_field_tag :scope, 'merge_requests' - elsif current_controller?(:wikis) = hidden_field_tag :scope, 'wiki_blobs' + - elsif current_controller?(:commits) + = hidden_field_tag :scope, 'commits' - else = hidden_field_tag :search_code, true diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 4580c912692..bed2b16249e 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -20,6 +20,10 @@ New snippet - if can?(current_user, :push_code, @project) %li.divider + %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do + = icon('file fw') + New file %li = link_to new_namespace_project_branch_path(@project.namespace, @project) do = icon('code-fork fw') diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index d637abfa76b..481451edb23 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -42,6 +42,13 @@ Wiki %span.badge = @search_results.wiki_blobs_count + %li{class: ("active" if @scope == 'commits')} + = link_to search_filter_path(scope: 'commits') do + = icon('history fw') + %span + Commits + %span.badge + = @search_results.commits_count - elsif @show_snippets %li{class: ("active" if @scope == 'snippet_blobs')} diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml new file mode 100644 index 00000000000..4e6c3965dc6 --- /dev/null +++ b/app/views/search/results/_commit.html.haml @@ -0,0 +1,2 @@ +.search-result-row + = render 'projects/commits/commit', project: @project, commit: commit diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 5d46ece1e1b..9e8b0131f8f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -8,24 +8,3 @@ # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end - -# Mark "commits" as uncountable. -# -# Without this change, the routes -# -# resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} -# resources :commits, only: [:show], constraints: {id: /.+/} -# -# would generate identical route helper methods (`project_commit_path`), resulting -# in one of them not getting a helper method at all. -# -# After this change, the helper methods are: -# -# project_commit_path(@project, @project.commit) -# # => "/gitlabhq/commit/bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a -# -# project_commits_path(@project, 'stable/README.md') -# # => "/gitlabhq/commits/stable/README.md" -ActiveSupport::Inflector.inflections do |inflect| - inflect.uncountable %w(commits) -end diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index 25311b07107..623063f357b 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -23,7 +23,8 @@ Example response: "content": "IyA9PSBTY2hlbWEgSW5mb3...", "ref": "master", "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", - "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50" + "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", + "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d" } ``` diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index c8122fc63b3..1cf41aea391 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -1,5 +1,5 @@ # Build script examples -+ [Test and deploy Ruby applications to Heroku](test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy Python applications to Heroku](test-and-deploy-python-application-to-heroku.md) -+ [Test Clojure applications](test-clojure-application.md) ++ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) ++ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) ++ [Test a Clojure application](test-clojure-application.md) diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md index 036b03dd6b9..a236da53fe9 100644 --- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -1,7 +1,7 @@ ## Test and Deploy a python application This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application. -You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://ci.gitlab.com/projects/4080). +You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://gitlab.com/ayufan/python-getting-started/builds?scope=all). ### Configure project This is what the `.gitlab-ci.yml` file looks like for this project: diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md index d2a872f1934..e52e1547461 100644 --- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md @@ -1,7 +1,7 @@ ## Test and Deploy a ruby application This example will guide you how to run tests in your Ruby application and deploy it automatiacally as Heroku application. -You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://ci.gitlab.com/projects/4050). +You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all). ### Configure project This is what the `.gitlab-ci.yml` file looks like for this project: @@ -64,4 +64,4 @@ gitlab-ci-multi-runner register \ With the command above, you create a runner that uses [ruby:2.1](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. -To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. \ No newline at end of file +To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md index eaee94a10f1..56b746ce025 100644 --- a/doc/ci/examples/test-clojure-application.md +++ b/doc/ci/examples/test-clojure-application.md @@ -1,8 +1,8 @@ -## Test Clojure applications +## Test a Clojure application This example will guide you how to run tests in your Clojure application. -You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://ci.gitlab.com/projects/6306). +You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=all). ### Configure project diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index ea8f72bc135..d117a2969be 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -169,7 +169,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. +1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -182,6 +182,18 @@ job: - branches # use special keyword ``` +1. `only` and `except` allow for specify repository path to filter jobs for forks. +The repository path can be used to have jobs executed only for parent repository. + +```yaml +job: + only: + - branches@gitlab-org/gitlab-ce + except: + - master@gitlab-org/gitlab-ce +``` +The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . + ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. diff --git a/lib/api/files.rb b/lib/api/files.rb index 308c84dd135..a7a768f8895 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -43,7 +43,8 @@ module API # "content": "IyA9PSBTY2hlbWEgSW5mb3...", # "ref": "master", # "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", - # "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50" + # "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", + # "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d", # } # get ":id/repository/files" do @@ -71,6 +72,7 @@ module API ref: ref, blob_id: blob.id, commit_id: commit.id, + last_commit_id: user_project.repository.last_commit_for_path(commit.sha, file_path).id } else not_found! 'File' diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index 800f30c2144..635967f4bd4 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -1,3 +1,5 @@ +require 'backup/files' + module Backup class Builds < Files def initialize diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index 0a0ec564ba4..9261f77f3c9 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -1,3 +1,5 @@ +require 'backup/files' + module Backup class Uploads < Files diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index efcd2faffc7..0f57a4f53ab 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -7,10 +7,11 @@ module Ci ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when] - attr_reader :before_script, :image, :services, :variables + attr_reader :before_script, :image, :services, :variables, :path - def initialize(config) + def initialize(config, path = nil) @config = YAML.load(config) + @path = path unless @config.is_a? Hash raise ValidationError, "YAML should be a hash" @@ -63,26 +64,6 @@ module Ci end end - def process?(only_params, except_params, ref, tag) - return true if only_params.nil? && except_params.nil? - - if only_params - return true if tag && only_params.include?("tags") - return true if !tag && only_params.include?("branches") - - only_params.find do |pattern| - match_ref?(pattern, ref) - end - else - return false if tag && except_params.include?("tags") - return false if !tag && except_params.include?("branches") - - except_params.each do |pattern| - return false if match_ref?(pattern, ref) - end - end - end - def build_job(name, job) { stage_idx: stages.index(job[:stage]), @@ -101,14 +82,6 @@ module Ci } end - def match_ref?(pattern, ref) - if pattern.first == "/" && pattern.last == "/" - Regexp.new(pattern[1...-1]) =~ ref - else - pattern == ref - end - end - def normalize_script(script) if script.is_a? Array script.join("\n") @@ -208,5 +181,36 @@ module Ci def validate_string(value) value.is_a?(String) || value.is_a?(Symbol) end + + def process?(only_params, except_params, ref, tag) + if only_params.present? + return false unless matching?(only_params, ref, tag) + end + + if except_params.present? + return false if matching?(except_params, ref, tag) + end + + true + end + + def matching?(patterns, ref, tag) + patterns.any? do |pattern| + match_ref?(pattern, ref, tag) + end + end + + def match_ref?(pattern, ref, tag) + pattern, path = pattern.split('@', 2) + return false if path && path != self.path + return true if tag && pattern == 'tags' + return true if !tag && pattern == 'branches' + + if pattern.first == "/" && pattern.last == "/" + Regexp.new(pattern[1...-1]) =~ ref + else + pattern == ref + end + end end end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 0a2be605af9..70de6a74e76 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -20,6 +20,8 @@ module Gitlab Kaminari.paginate_array(blobs).page(page).per(per_page) when 'wiki_blobs' Kaminari.paginate_array(wiki_blobs).page(page).per(per_page) + when 'commits' + Kaminari.paginate_array(commits).page(page).per(per_page) else super end @@ -27,7 +29,7 @@ module Gitlab def total_count @total_count ||= issues_count + merge_requests_count + blobs_count + - notes_count + wiki_blobs_count + notes_count + wiki_blobs_count + commits_count end def blobs_count @@ -42,6 +44,10 @@ module Gitlab @wiki_blobs_count ||= wiki_blobs.count end + def commits_count + @commits_count ||= commits.count + end + private def blobs @@ -70,6 +76,14 @@ module Gitlab Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC') end + def commits + if project.empty_repo? || query.blank? + [] + else + project.repository.find_commits_by_message(query).compact + end + end + def limit_project_ids [project.id] end diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index c8881be0954..d5a96fd38f4 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -5,7 +5,7 @@ namespace :spinach do task :project do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets), + %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits), ] run_commands(cmds) end @@ -14,7 +14,7 @@ namespace :spinach do task :other do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets), + %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits), ] run_commands(cmds) end @@ -33,4 +33,4 @@ def run_commands(cmds) cmds.each do |cmd| system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") end -end +end \ No newline at end of file diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index abdb6b89ac5..9963f76f993 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' module Ci describe GitlabCiYamlProcessor do - + let(:path) { 'path' } + describe "#builds_for_ref" do let(:type) { 'test' } @@ -12,7 +13,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({ @@ -28,78 +29,218 @@ module Ci when: "on_success" }) end + + describe :only do + it "does not return builds if only has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["deploy"] } + }) - it "does not return builds if only has another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["deploy"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + it "does not return builds if only has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["/^deploy$/"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end + + it "returns builds if only has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["master"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end + + it "returns builds if only has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["master", "deploy"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "returns builds if only has a branches keyword specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if only has a tags keyword" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["tags"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if only has current repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches@path"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if only has different repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches@fork"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns build only for specified type" do + + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: "test", only: ["master", "deploy"] }, + staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }, + }) + + config_processor = GitlabCiYamlProcessor.new(config, 'fork') + + expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1) + end end - it "does not return builds if only has regexp with another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["/^deploy$/"] } - }) + describe :except do + it "returns builds if except has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["deploy"] } + }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end + + it "returns builds if except has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["/^deploy$/"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end + + it "does not return builds if except has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["master"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end + + it "does not return builds if except has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["master", "deploy"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "does not return builds if except has a branches keyword specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if except has a tags keyword" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["tags"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if except has current repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches@path"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if except has different repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches@fork"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "returns build except specified type" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] }, + staging: { script: "deploy", type: "deploy", except: ["master"] }, + production: { script: "deploy", type: "deploy", except: ["master@fork"] }, + }) + + config_processor = GitlabCiYamlProcessor.new(config, 'fork') + + expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0) + end end - it "returns builds if only has specified this branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["master"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) - end - - it "does not build tags" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", except: ["tags"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - expect(config_processor.builds_for_stage_and_ref(type, "0-1", true).size).to eq(0) - end - - it "returns builds if only has a list of branches including specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["master", "deploy"] } - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) - end - - it "returns build only for specified type" do - - config = YAML.dump({ - before_script: ["pwd"], - build: { script: "build", type: "build", only: ["master", "deploy"] }, - rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, - staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - }) - - config_processor = GitlabCiYamlProcessor.new(config) - - expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) - end end describe "Image and service handling" do @@ -111,7 +252,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ @@ -139,7 +280,7 @@ module Ci rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ @@ -172,7 +313,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.variables).to eq(variables) end end @@ -184,7 +325,7 @@ module Ci rspec: { script: "rspec", when: when_state } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) builds = config_processor.builds_for_stage_and_ref("test", "master") expect(builds.size).to eq(1) expect(builds.first[:when]).to eq(when_state) @@ -200,154 +341,154 @@ module Ci it "returns errors if tags parameter is invalid" do config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") end it "returns errors if before_script parameter is invalid" do config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") end it "returns errors if job name is blank" do config = YAML.dump({ '' => { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") end it "returns errors if job name is non-string" do config = YAML.dump({ 10 => { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") end it "returns errors if job image parameter is invalid" do config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") end it "returns errors if services parameter is not an array" do config = YAML.dump({ services: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if services parameter is not an array of strings" do config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if job services parameter is not an array" do config = YAML.dump({ rspec: { script: "test", services: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if job services parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if there are unknown parameters" do config = YAML.dump({ extra: "bundle update" }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do config = YAML.dump({ extra: { services: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there is no any jobs defined" do config = YAML.dump({ before_script: ["bundle update"] }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") end it "returns errors if job allow_failure parameter is not an boolean" do config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") end it "returns errors if job stage is not a string" do config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a pre-defined stage" do config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") end it "returns errors if stages is not an array" do config = YAML.dump({ types: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if stages is not an array of strings" do config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if variables is not a map" do config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end it "returns errors if variables is not a map of key-valued strings" do config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end it "returns errors if job when is not on_success, on_failure or always" do config = YAML.dump({ rspec: { script: "test", when: 1 } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always") end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index cb67ec95d57..47863d54579 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -468,7 +468,7 @@ describe Notify do subject { Notify.note_commit_email(recipient.id, note.id) } it_behaves_like 'a note email' - it_behaves_like 'an answer to an existing thread', 'commits' + it_behaves_like 'an answer to an existing thread', 'commit' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 05e51532eb8..319fa0a7c8d 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,6 +26,15 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end + describe :find_commits_by_message do + subject { repository.find_commits_by_message('submodule').map{ |k| k.id } } + + it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') } + it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + end + describe :blob_at do context 'blank sha' do subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') } diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 042e6352567..8efa09f75fd 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -19,6 +19,7 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end