From 736b772f6b0aa7256924998cc1b010e3f8a5fa8d Mon Sep 17 00:00:00 2001 From: Lukasz Kreczko Date: Tue, 29 May 2018 15:57:11 +0100 Subject: [PATCH 01/26] added gitlab fetcher --- .../gitlab_fetcher.rb | 479 ++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 lib/github_changelog_generator/gitlab_fetcher.rb diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb new file mode 100644 index 000000000..4b148094c --- /dev/null +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -0,0 +1,479 @@ +# frozen_string_literal: true + +require "tmpdir" +require "retriable" +require +module GitHubChangelogGenerator + # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data + # (such as filtering, validating, e.t.c) + # + # Example: + # fetcher = GitHubChangelogGenerator::OctoFetcher.new(options) + class GitlabFetcher + PER_PAGE_NUMBER = 100 + MAX_THREAD_NUMBER = 25 + MAX_FORBIDDEN_RETRIES = 100 + CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN" + GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ + "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument." + NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \ + "This script can make only 50 requests to GitHub API per hour without token!" + + # @param options [Hash] Options passed in + # @option options [String] :user GitHub username + # @option options [String] :project GitHub project + # @option options [String] :since Only issues updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. eg. Time.parse("2016-01-01 10:00:00").iso8601 + # @option options [Boolean] :http_cache Use ActiveSupport::Cache::FileStore to cache http requests + # @option options [Boolean] :cache_file If using http_cache, this is the cache file path + # @option options [Boolean] :cache_log If using http_cache, this is the cache log file path + def initialize(options = {}) + @options = options || {} + @user = @options[:user] + @project = @options[:project] + @since = @options[:since] + @http_cache = @options[:http_cache] + @cache_file = nil + @cache_log = nil + @commits = [] + @compares = {} + # prepare_cache + # configure_octokit_ssl + @client = Gitlab::Client.new(github_options) + end + + # def prepare_cache + # return unless @http_cache + # @cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") } + # @cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") } + # init_cache + # end + + def github_options + result = {} + github_token = fetch_github_token + result[:access_token] = github_token if github_token + endpoint = @options[:github_endpoint] + result[:api_endpoint] = endpoint if endpoint + result + end + + # def configure_octokit_ssl + # ca_file = @options[:ssl_ca_file] || ENV["SSL_CA_FILE"] || File.expand_path("ssl_certs/cacert.pem", __dir__) + # Octokit.connection_options = { ssl: { ca_file: ca_file } } + # end + + # def init_cache + # Octokit.middleware = Faraday::RackBuilder.new do |builder| + # builder.use(Faraday::HttpCache, serializer: Marshal, + # store: ActiveSupport::Cache::FileStore.new(@cache_file), + # logger: Logger.new(@cache_log), + # shared_cache: false) + # builder.use Octokit::Response::RaiseError + # builder.adapter Faraday.default_adapter + # # builder.response :logger + # end + # end + + DEFAULT_REQUEST_OPTIONS = { per_page: PER_PAGE_NUMBER } + + # Fetch all tags from repo + # + # @return [Array ] array of tags + def get_all_tags + print "Fetching tags...\r" if @options[:verbose] + + check_github_response { github_fetch_tags } + end + + # # Returns the number of pages for a API call + # # + # # @return [Integer] number of pages for this API call in total + # def calculate_pages(client, method, request_options) + # # Makes the first API call so that we can call last_response + # check_github_response do + # client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options)) + # end + # + # last_response = client.last_response + # + # if (last_pg = last_response.rels[:last]) + # querystring_as_hash(last_pg.href)["page"].to_i + # else + # 1 + # end + # end + + # Fill input array with tags + # + # @return [Array ] array of tags in repo + def github_fetch_tags + tags = [] + page_i = 0 + count_pages = calculate_pages(@client, "tags", {}) + + iterate_pages(@client, "tags") do |new_tags| + page_i += PER_PAGE_NUMBER + print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") + tags.concat(new_tags) + end + print_empty_line + + if tags.count == 0 + Helper.log.warn "Warning: Can't find any tags in repo. \ +Make sure, that you push tags to remote repo via 'git push --tags'" + else + Helper.log.info "Found #{tags.count} tags" + end + # tags are a Sawyer::Resource. Convert to hash + tags.map { |resource| stringify_keys_deep(resource.to_hash) } + end + + def closed_pr_options + @closed_pr_options ||= { + filter: "all", labels: nil, state: "closed" + }.tap { |options| options[:since] = @since if @since } + end + + # This method fetch all closed issues and separate them to pull requests and pure issues + # (pull request is kind of issue in term of GitHub) + # + # @return [Tuple] with (issues [Array ], pull-requests [Array ]) + def fetch_closed_issues_and_pr + print "Fetching closed issues...\r" if @options[:verbose] + issues = [] + page_i = 0 + count_pages = calculate_pages(@client, "issues", closed_pr_options) + + iterate_pages(@client, "issues", closed_pr_options) do |new_issues| + page_i += PER_PAGE_NUMBER + print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") + issues.concat(new_issues) + break if @options[:max_issues] && issues.length >= @options[:max_issues] + end + print_empty_line + Helper.log.info "Received issues: #{issues.count}" + + # separate arrays of issues and pull requests: + issues.map { |issue| stringify_keys_deep(issue.to_hash) } + .partition { |issue_or_pr| issue_or_pr["pull_request"].nil? } + end + + # Fetch all pull requests. We need them to detect :merged_at parameter + # + # @return [Array ] all pull requests + def fetch_closed_pull_requests + pull_requests = [] + options = { state: "closed" } + + page_i = 0 + count_pages = calculate_pages(@client, "pull_requests", options) + + iterate_pages(@client, "pull_requests", options) do |new_pr| + page_i += PER_PAGE_NUMBER + log_string = "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}" + print_in_same_line(log_string) + pull_requests.concat(new_pr) + end + print_empty_line + + Helper.log.info "Pull Request count: #{pull_requests.count}" + pull_requests.map { |pull_request| stringify_keys_deep(pull_request.to_hash) } + end + + # Fetch event for all issues and add them to 'events' + # + # @param [Array] issues + # @return [Void] + def fetch_events_async(issues) + i = 0 + threads = [] + + issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice| + issues_slice.each do |issue| + threads << Thread.new do + issue["events"] = [] + iterate_pages(@client, "issue_events", issue["number"]) do |new_event| + issue["events"].concat(new_event) + end + issue["events"] = issue["events"].map { |event| stringify_keys_deep(event.to_hash) } + print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}") + i += 1 + end + end + threads.each(&:join) + threads = [] + end + + # to clear line from prev print + print_empty_line + + Helper.log.info "Fetching events for issues and PR: #{i}" + end + + # Fetch comments for PRs and add them to "comments" + # + # @param [Array] prs The array of PRs. + # @return [Void] No return; PRs are updated in-place. + def fetch_comments_async(prs) + threads = [] + + prs.each_slice(MAX_THREAD_NUMBER) do |prs_slice| + prs_slice.each do |pr| + threads << Thread.new do + pr["comments"] = [] + iterate_pages(@client, "issue_comments", pr["number"]) do |new_comment| + pr["comments"].concat(new_comment) + end + pr["comments"] = pr["comments"].map { |comment| stringify_keys_deep(comment.to_hash) } + end + end + threads.each(&:join) + threads = [] + end + nil + end + + # Fetch tag time from repo + # + # @param [Hash] tag GitHub data item about a Tag + # + # @return [Time] time of specified tag + def fetch_date_of_tag(tag) + commit_data = fetch_commit(tag["commit"]["sha"]) + commit_data = stringify_keys_deep(commit_data.to_hash) + + commit_data["commit"]["committer"]["date"] + end + + # Fetch and cache comparison between two github refs + # + # @param [String] older The older sha/tag/branch. + # @param [String] newer The newer sha/tag/branch. + # @return [Hash] Github api response for comparison. + def fetch_compare(older, newer) + unless @compares["#{older}...#{newer}"] + compare_data = check_github_response { @client.compare(user_project, older, newer || "HEAD") } + raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." if compare_data["status"] == "diverged" + @compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash) + end + @compares["#{older}...#{newer}"] + end + + # Fetch commit for specified event + # + # @param [String] commit_id the SHA of a commit to fetch + # @return [Hash] + def fetch_commit(commit_id) + found = commits.find do |commit| + commit["sha"] == commit_id + end + if found + stringify_keys_deep(found.to_hash) + else + # cache miss; don't add to @commits because unsure of order. + check_github_response do + commit = @client.commit(user_project, commit_id) + commit = stringify_keys_deep(commit.to_hash) + commit + end + end + end + + # Fetch all commits + # + # @return [Array] Commits in a repo. + def commits + if @commits.empty? + iterate_pages(@client, "commits") do |new_commits| + @commits.concat(new_commits) + end + end + @commits + end + + # Return the oldest commit in a repo + # + # @return [Hash] Oldest commit in the github git history. + def oldest_commit + commits.last + end + + # @return [String] Default branch of the repo + def default_branch + @default_branch ||= @client.repository(user_project)[:default_branch] + end + + # Fetch all SHAs occurring in or before a given tag and add them to + # "shas_in_tag" + # + # @param [Array] tags The array of tags. + # @return [Nil] No return; tags are updated in-place. + def fetch_tag_shas_async(tags) + i = 0 + threads = [] + print_in_same_line("Fetching SHAs for tags: #{i}/#{tags.count}\r") if @options[:verbose] + + tags.each_slice(MAX_THREAD_NUMBER) do |tags_slice| + tags_slice.each do |tag| + threads << Thread.new do + # Use oldest commit because comparing two arbitrary tags may be diverged + commits_in_tag = fetch_compare(oldest_commit["sha"], tag["name"]) + tag["shas_in_tag"] = commits_in_tag["commits"].collect { |commit| commit["sha"] } + print_in_same_line("Fetching SHAs for tags: #{i + 1}/#{tags.count}") if @options[:verbose] + i += 1 + end + end + threads.each(&:join) + threads = [] + end + + # to clear line from prev print + print_empty_line + + Helper.log.info "Fetching SHAs for tags: #{i}" + nil + end + + private + + def stringify_keys_deep(indata) + case indata + when Array + indata.map do |value| + stringify_keys_deep(value) + end + when Hash + indata.each_with_object({}) do |(key, value), output| + output[key.to_s] = stringify_keys_deep(value) + end + else + indata + end + end + + # Exception raised to warn about moved repositories. + MovedPermanentlyError = Class.new(RuntimeError) + + # Iterates through all pages until there are no more :next pages to follow + # yields the result per page + # + # @param [Octokit::Client] client + # @param [String] method (eg. 'tags') + # + # @yield [Sawyer::Resource] An OctoKit-provided response (which can be empty) + # + # @return [void] + def iterate_pages(client, method, *args) + args << DEFAULT_REQUEST_OPTIONS.merge(extract_request_args(args)) + + check_github_response { client.send(method, user_project, *args) } + last_response = client.last_response.tap do |response| + raise(MovedPermanentlyError, response.data[:url]) if response.status == 301 + end + + yield(last_response.data) + + until (next_one = last_response.rels[:next]).nil? + last_response = check_github_response { next_one.get } + yield(last_response.data) + end + end + + def extract_request_args(args) + if args.size == 1 && args.first.is_a?(Hash) + args.delete_at(0) + elsif args.size > 1 && args.last.is_a?(Hash) + args.delete_at(args.length - 1) + else + {} + end + end + + # This is wrapper with rescue block + # + # @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block + def check_github_response + Retriable.retriable(retry_options) do + yield + end + rescue MovedPermanentlyError => e + fail_with_message(e, "The repository has moved, update your configuration") + rescue Gitlab::Error::Forbidden => e + fail_with_message(e, "Exceeded retry limit") + rescue Gitlab::Error::Unauthorized => e + fail_with_message(e, "Error: wrong GitHub token") + end + + # Presents the exception, and the aborts with the message. + def fail_with_message(error, message) + Helper.log.error("#{error.class}: #{error.message}") + sys_abort(message) + end + + # Exponential backoff + def retry_options + { + on: [Gitlab::Error::Forbidden], + tries: MAX_FORBIDDEN_RETRIES, + base_interval: sleep_base_interval, + multiplier: 1.0, + rand_factor: 0.0, + on_retry: retry_callback + } + end + + def sleep_base_interval + 1.0 + end + + def retry_callback + proc do |exception, try, elapsed_time, next_interval| + Helper.log.warn("RETRY - #{exception.class}: '#{exception.message}'") + Helper.log.warn("#{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try") + Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG + Helper.log.warn @client.rate_limit + end + end + + def sys_abort(msg) + abort(msg) + end + + # Print specified line on the same string + # + # @param [String] log_string + def print_in_same_line(log_string) + print log_string + "\r" + end + + # Print long line with spaces on same line to clear prev message + def print_empty_line + print_in_same_line(" ") + end + + # Returns GitHub token. First try to use variable, provided by --token option, + # otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable. + # + # @return [String] + def fetch_github_token + env_var = @options[:token].presence || ENV["CHANGELOG_GITHUB_TOKEN"] + + Helper.log.warn NO_TOKEN_PROVIDED unless env_var + + env_var + end + + # @return [String] helper to return Github "user/project" + def user_project + "#{@options[:user]}/#{@options[:project]}" + end + + # Returns Hash of all querystring variables in given URI. + # + # @param [String] uri eg. https://api.github.com/repositories/43914960/tags?page=37&foo=1 + # @return [Hash] of all GET variables. eg. { 'page' => 37, 'foo' => 1 } + def querystring_as_hash(uri) + Hash[URI.decode_www_form(URI(uri).query || "")] + end + end +end From c2e9ed990f7304d5b376c001d51e0736b8f6acfa Mon Sep 17 00:00:00 2001 From: kreczko Date: Tue, 29 May 2018 17:36:33 +0100 Subject: [PATCH 02/26] implemented fetch_tags for GITLAB --- .../gitlab_fetcher.rb | 96 +++++++------------ 1 file changed, 35 insertions(+), 61 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 4b148094c..7292b5150 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -2,7 +2,7 @@ require "tmpdir" require "retriable" -require +require "gitlab" module GitHubChangelogGenerator # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data # (such as filtering, validating, e.t.c) @@ -36,44 +36,21 @@ def initialize(options = {}) @cache_log = nil @commits = [] @compares = {} - # prepare_cache - # configure_octokit_ssl - @client = Gitlab::Client.new(github_options) - end - # def prepare_cache - # return unless @http_cache - # @cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") } - # @cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") } - # init_cache - # end + Gitlab.sudo = nil + @client = Gitlab::Client.new(gitlab_options) + @project_id = @client.project_search(@project).first.id + end - def github_options + def gitlab_options result = {} - github_token = fetch_github_token - result[:access_token] = github_token if github_token + access_token = fetch_github_token + result[:private_token] = access_token if access_token endpoint = @options[:github_endpoint] - result[:api_endpoint] = endpoint if endpoint + result[:endpoint] = endpoint if endpoint result end - # def configure_octokit_ssl - # ca_file = @options[:ssl_ca_file] || ENV["SSL_CA_FILE"] || File.expand_path("ssl_certs/cacert.pem", __dir__) - # Octokit.connection_options = { ssl: { ca_file: ca_file } } - # end - - # def init_cache - # Octokit.middleware = Faraday::RackBuilder.new do |builder| - # builder.use(Faraday::HttpCache, serializer: Marshal, - # store: ActiveSupport::Cache::FileStore.new(@cache_file), - # logger: Logger.new(@cache_log), - # shared_cache: false) - # builder.use Octokit::Response::RaiseError - # builder.adapter Faraday.default_adapter - # # builder.response :logger - # end - # end - DEFAULT_REQUEST_OPTIONS = { per_page: PER_PAGE_NUMBER } # Fetch all tags from repo @@ -82,39 +59,36 @@ def github_options def get_all_tags print "Fetching tags...\r" if @options[:verbose] - check_github_response { github_fetch_tags } + check_response { fetch_tags } end - # # Returns the number of pages for a API call - # # - # # @return [Integer] number of pages for this API call in total - # def calculate_pages(client, method, request_options) - # # Makes the first API call so that we can call last_response - # check_github_response do - # client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options)) - # end + # Returns the number of pages for a API call # - # last_response = client.last_response - # - # if (last_pg = last_response.rels[:last]) - # querystring_as_hash(last_pg.href)["page"].to_i - # else - # 1 - # end - # end + # @return [Integer] number of pages for this API call in total + def calculate_pages(client, method, request_options) + # Makes the first API call so that we can call last_response + check_response do + client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options)) + end + + last_response = client.last_response + + if (last_pg = last_response.rels[:last]) + querystring_as_hash(last_pg.href)["page"].to_i + else + 1 + end + end # Fill input array with tags # # @return [Array ] array of tags in repo - def github_fetch_tags + def fetch_tags tags = [] - page_i = 0 - count_pages = calculate_pages(@client, "tags", {}) + new_tags = @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS) - iterate_pages(@client, "tags") do |new_tags| - page_i += PER_PAGE_NUMBER - print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") - tags.concat(new_tags) + new_tags.auto_paginate do |new_tag| + tags.push(new_tag) end print_empty_line @@ -252,7 +226,7 @@ def fetch_date_of_tag(tag) # @return [Hash] Github api response for comparison. def fetch_compare(older, newer) unless @compares["#{older}...#{newer}"] - compare_data = check_github_response { @client.compare(user_project, older, newer || "HEAD") } + compare_data = check_response { @client.compare(user_project, older, newer || "HEAD") } raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." if compare_data["status"] == "diverged" @compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash) end @@ -271,7 +245,7 @@ def fetch_commit(commit_id) stringify_keys_deep(found.to_hash) else # cache miss; don't add to @commits because unsure of order. - check_github_response do + check_response do commit = @client.commit(user_project, commit_id) commit = stringify_keys_deep(commit.to_hash) commit @@ -366,7 +340,7 @@ def stringify_keys_deep(indata) def iterate_pages(client, method, *args) args << DEFAULT_REQUEST_OPTIONS.merge(extract_request_args(args)) - check_github_response { client.send(method, user_project, *args) } + check_response { client.send(method, user_project, *args) } last_response = client.last_response.tap do |response| raise(MovedPermanentlyError, response.data[:url]) if response.status == 301 end @@ -374,7 +348,7 @@ def iterate_pages(client, method, *args) yield(last_response.data) until (next_one = last_response.rels[:next]).nil? - last_response = check_github_response { next_one.get } + last_response = check_response { next_one.get } yield(last_response.data) end end @@ -392,7 +366,7 @@ def extract_request_args(args) # This is wrapper with rescue block # # @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block - def check_github_response + def check_response Retriable.retriable(retry_options) do yield end From 7e6d1e2c3de948a046e8d041b40a0ace3dfc2c22 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 14:18:14 +0100 Subject: [PATCH 03/26] first successful runthrough with gitlab_fetcher --- .../gitlab_fetcher.rb | 126 ++++++------------ 1 file changed, 43 insertions(+), 83 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 7292b5150..08d7945ad 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true +require 'date' require "tmpdir" require "retriable" require "gitlab" + + module GitHubChangelogGenerator # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data # (such as filtering, validating, e.t.c) @@ -20,8 +23,8 @@ class GitlabFetcher "This script can make only 50 requests to GitHub API per hour without token!" # @param options [Hash] Options passed in - # @option options [String] :user GitHub username - # @option options [String] :project GitHub project + # @option options [String] :user Gitlab username + # @option options [String] :project Gitlab project # @option options [String] :since Only issues updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. eg. Time.parse("2016-01-01 10:00:00").iso8601 # @option options [Boolean] :http_cache Use ActiveSupport::Cache::FileStore to cache http requests # @option options [Boolean] :cache_file If using http_cache, this is the cache file path @@ -62,24 +65,6 @@ def get_all_tags check_response { fetch_tags } end - # Returns the number of pages for a API call - # - # @return [Integer] number of pages for this API call in total - def calculate_pages(client, method, request_options) - # Makes the first API call so that we can call last_response - check_response do - client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options)) - end - - last_response = client.last_response - - if (last_pg = last_response.rels[:last]) - querystring_as_hash(last_pg.href)["page"].to_i - else - 1 - end - end - # Fill input array with tags # # @return [Array ] array of tags in repo @@ -104,7 +89,7 @@ def fetch_tags def closed_pr_options @closed_pr_options ||= { - filter: "all", labels: nil, state: "closed" + filter: "all", labels: nil, state: "merged" }.tap { |options| options[:since] = @since if @since } end @@ -114,22 +99,15 @@ def closed_pr_options # @return [Tuple] with (issues [Array ], pull-requests [Array ]) def fetch_closed_issues_and_pr print "Fetching closed issues...\r" if @options[:verbose] - issues = [] - page_i = 0 - count_pages = calculate_pages(@client, "issues", closed_pr_options) - - iterate_pages(@client, "issues", closed_pr_options) do |new_issues| - page_i += PER_PAGE_NUMBER - print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}") - issues.concat(new_issues) - break if @options[:max_issues] && issues.length >= @options[:max_issues] - end + print_empty_line + issues = @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS) + p issues.first + print_empty_line Helper.log.info "Received issues: #{issues.count}" # separate arrays of issues and pull requests: - issues.map { |issue| stringify_keys_deep(issue.to_hash) } - .partition { |issue_or_pr| issue_or_pr["pull_request"].nil? } + return issues.map { |issue| stringify_keys_deep(issue.to_hash) }, fetch_closed_pull_requests end # Fetch all pull requests. We need them to detect :merged_at parameter @@ -137,17 +115,16 @@ def fetch_closed_issues_and_pr # @return [Array ] all pull requests def fetch_closed_pull_requests pull_requests = [] - options = { state: "closed" } - - page_i = 0 - count_pages = calculate_pages(@client, "pull_requests", options) - - iterate_pages(@client, "pull_requests", options) do |new_pr| - page_i += PER_PAGE_NUMBER - log_string = "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}" - print_in_same_line(log_string) - pull_requests.concat(new_pr) + options = { state: "merged", scope: :all} + + @client.merge_requests(@project_id, options).auto_paginate do |new_pr| + new_pr = stringify_keys_deep(new_pr.to_hash) + # align with Github naming + new_pr["number"] = new_pr["iid"] + new_pr["merged_at"] = new_pr["updated_at"] + pull_requests.push(new_pr) end + print_empty_line Helper.log.info "Pull Request count: #{pull_requests.count}" @@ -166,8 +143,8 @@ def fetch_events_async(issues) issues_slice.each do |issue| threads << Thread.new do issue["events"] = [] - iterate_pages(@client, "issue_events", issue["number"]) do |new_event| - issue["events"].concat(new_event) + @client.project_events(@project_id).auto_paginate do |new_event| + issue["events"].push(new_event) end issue["events"] = issue["events"].map { |event| stringify_keys_deep(event.to_hash) } print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}") @@ -195,8 +172,8 @@ def fetch_comments_async(prs) prs_slice.each do |pr| threads << Thread.new do pr["comments"] = [] - iterate_pages(@client, "issue_comments", pr["number"]) do |new_comment| - pr["comments"].concat(new_comment) + @client.merge_request_notes(@project_id, pr["number"]) do |new_comment| + pr["comments"].push(new_comment) end pr["comments"] = pr["comments"].map { |comment| stringify_keys_deep(comment.to_hash) } end @@ -213,10 +190,7 @@ def fetch_comments_async(prs) # # @return [Time] time of specified tag def fetch_date_of_tag(tag) - commit_data = fetch_commit(tag["commit"]["sha"]) - commit_data = stringify_keys_deep(commit_data.to_hash) - - commit_data["commit"]["committer"]["date"] + DateTime.parse(tag["commit"]["committed_date"]) end # Fetch and cache comparison between two github refs @@ -226,8 +200,15 @@ def fetch_date_of_tag(tag) # @return [Hash] Github api response for comparison. def fetch_compare(older, newer) unless @compares["#{older}...#{newer}"] - compare_data = check_response { @client.compare(user_project, older, newer || "HEAD") } - raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." if compare_data["status"] == "diverged" + compare_data = check_response { @client.compare(@project_id, older, newer || "HEAD") } + compare_data = stringify_keys_deep(compare_data.to_hash) + compare_data["commits"].each do |commit| + commit["sha"] = commit["id"] + end + # TODO: do not know what the equivalent for gitlab is + if compare_data["compare_same_ref"] == true then + raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." + end @compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash) end @compares["#{older}...#{newer}"] @@ -239,15 +220,16 @@ def fetch_compare(older, newer) # @return [Hash] def fetch_commit(commit_id) found = commits.find do |commit| - commit["sha"] == commit_id + commit['sha'] == commit_id end if found stringify_keys_deep(found.to_hash) else # cache miss; don't add to @commits because unsure of order. check_response do - commit = @client.commit(user_project, commit_id) + commit = @client.commit(@project_id, commit_id) commit = stringify_keys_deep(commit.to_hash) + commit['sha'] = commit['id'] commit end end @@ -258,8 +240,10 @@ def fetch_commit(commit_id) # @return [Array] Commits in a repo. def commits if @commits.empty? - iterate_pages(@client, "commits") do |new_commits| - @commits.concat(new_commits) + @client.commits(@project_id).auto_paginate do |new_commit| + new_commit = stringify_keys_deep(new_commit.to_hash) + new_commit['sha'] = new_commit['id'] + @commits.push(new_commit) end end @commits @@ -267,14 +251,14 @@ def commits # Return the oldest commit in a repo # - # @return [Hash] Oldest commit in the github git history. + # @return [Hash] Oldest commit in the gitlab git history. def oldest_commit commits.last end # @return [String] Default branch of the repo def default_branch - @default_branch ||= @client.repository(user_project)[:default_branch] + @default_branch ||= @client.project(@project_id)[:default_branch] end # Fetch all SHAs occurring in or before a given tag and add them to @@ -328,30 +312,6 @@ def stringify_keys_deep(indata) # Exception raised to warn about moved repositories. MovedPermanentlyError = Class.new(RuntimeError) - # Iterates through all pages until there are no more :next pages to follow - # yields the result per page - # - # @param [Octokit::Client] client - # @param [String] method (eg. 'tags') - # - # @yield [Sawyer::Resource] An OctoKit-provided response (which can be empty) - # - # @return [void] - def iterate_pages(client, method, *args) - args << DEFAULT_REQUEST_OPTIONS.merge(extract_request_args(args)) - - check_response { client.send(method, user_project, *args) } - last_response = client.last_response.tap do |response| - raise(MovedPermanentlyError, response.data[:url]) if response.status == 301 - end - - yield(last_response.data) - - until (next_one = last_response.rels[:next]).nil? - last_response = check_response { next_one.get } - yield(last_response.data) - end - end def extract_request_args(args) if args.size == 1 && args.first.is_a?(Hash) From 468a7cbfa48c85fb15462a79b1ab2b3cb22822bf Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 14:19:54 +0100 Subject: [PATCH 04/26] adjusting generator_fetcher to accept pr["merge_commit_sha"] if available --- .../generator/generator_fetcher.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator_fetcher.rb b/lib/github_changelog_generator/generator/generator_fetcher.rb index fabe6ef3b..5d851af97 100644 --- a/lib/github_changelog_generator/generator/generator_fetcher.rb +++ b/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -75,9 +75,16 @@ def associate_tagged_prs(tags, prs, total) # fetch that. See # https://developer.github.com/v3/pulls/#get-a-single-pull-request vs. # https://developer.github.com/v3/pulls/#list-pull-requests - if pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) + # gitlab API has this + merge_commit_sha = nil + if pr.has_key?("merge_commit_sha") then + merge_commit_sha = pr["merge_commit_sha"] + elsif pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) + merge_commit_sha = event["commit_id"] + end + unless merge_commit_sha.nil? # Iterate tags.reverse (oldest to newest) to find first tag of each PR. - if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(event["commit_id"]) }) + if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(merge_commit_sha) }) pr["first_occurring_tag"] = oldest_tag["name"] found = true i += 1 From df91ce22b0ff857fbb8c3662b0b1d655cb0ea337 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 14:28:53 +0100 Subject: [PATCH 05/26] added gitlab option --- lib/github_changelog_generator/options.rb | 1 + lib/github_changelog_generator/parser.rb | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/options.rb b/lib/github_changelog_generator/options.rb index 8370d9be3..62237a864 100644 --- a/lib/github_changelog_generator/options.rb +++ b/lib/github_changelog_generator/options.rb @@ -41,6 +41,7 @@ class Options < SimpleDelegator future_release github_endpoint github_site + gitlab header http_cache include_labels diff --git a/lib/github_changelog_generator/parser.rb b/lib/github_changelog_generator/parser.rb index 90a7f07b2..70087dd4d 100755 --- a/lib/github_changelog_generator/parser.rb +++ b/lib/github_changelog_generator/parser.rb @@ -176,6 +176,9 @@ def self.setup_parser(options) opts.on("--github-api [URL]", "The enterprise endpoint to use for your GitHub API.") do |last| options[:github_endpoint] = last end + opts.on("--[no-]gitlab", "Use Gitlab API instead of Github. Default is false.") do |last| + options[:gitlab] = last + end opts.on("--simple-list", "Create a simple list from issues and pull requests. Default is false.") do |v| options[:simple_list] = v end @@ -253,7 +256,8 @@ def self.default_options removed_prefix: "**Removed:**", security_prefix: "**Security fixes:**", http_cache: true, - require: [] + require: [], + gitlab: false ) end end From 89051212a83bc1626520db64d92752198f17ff7b Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 14:29:33 +0100 Subject: [PATCH 06/26] select correct fetcher depending on gitlab option (default github) --- lib/github_changelog_generator/generator/generator.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index dbc5af137..851d4d8ce 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "github_changelog_generator/octo_fetcher" +require "github_changelog_generator/gitlab_fetcher" require "github_changelog_generator/generator/generator_fetcher" require "github_changelog_generator/generator/generator_processor" require "github_changelog_generator/generator/generator_tags" @@ -34,7 +35,7 @@ class Generator def initialize(options = {}) @options = options @tag_times_hash = {} - @fetcher = GitHubChangelogGenerator::OctoFetcher.new(options) + @fetcher = options[:gitlab] ? GitHubChangelogGenerator::GitlabFetcher.new(options) : GitHubChangelogGenerator::OctoFetcher.new(options) @sections = [] end From 4f24ec07388e4fd44227799697c0c1a032b4ab19 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 15:10:38 +0100 Subject: [PATCH 07/26] restricting gitlab fetcher to closed issues --- lib/github_changelog_generator/gitlab_fetcher.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 08d7945ad..02f0a0e0e 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -99,9 +99,8 @@ def closed_pr_options # @return [Tuple] with (issues [Array ], pull-requests [Array ]) def fetch_closed_issues_and_pr print "Fetching closed issues...\r" if @options[:verbose] - print_empty_line - issues = @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS) - p issues.first + options = { state: "closed", scope: :all} + issues = @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS.merge(options)) print_empty_line Helper.log.info "Received issues: #{issues.count}" From 4ef20331fbfe3d808d12908af88cfd17ead6485e Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 15:23:20 +0100 Subject: [PATCH 08/26] fixed authors in merge_requests for gitlab fetcher --- .../gitlab_fetcher.rb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 02f0a0e0e..c2a494bd9 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -1,11 +1,10 @@ # frozen_string_literal: true -require 'date' +require "date" require "tmpdir" require "retriable" require "gitlab" - module GitHubChangelogGenerator # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data # (such as filtering, validating, e.t.c) @@ -69,7 +68,7 @@ def get_all_tags # # @return [Array ] array of tags in repo def fetch_tags - tags = [] + tags = [] new_tags = @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS) new_tags.auto_paginate do |new_tag| @@ -99,14 +98,14 @@ def closed_pr_options # @return [Tuple] with (issues [Array ], pull-requests [Array ]) def fetch_closed_issues_and_pr print "Fetching closed issues...\r" if @options[:verbose] - options = { state: "closed", scope: :all} + options = { state: "closed", scope: :all } issues = @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS.merge(options)) print_empty_line Helper.log.info "Received issues: #{issues.count}" # separate arrays of issues and pull requests: - return issues.map { |issue| stringify_keys_deep(issue.to_hash) }, fetch_closed_pull_requests + [issues.map { |issue| stringify_keys_deep(issue.to_hash) }, fetch_closed_pull_requests] end # Fetch all pull requests. We need them to detect :merged_at parameter @@ -114,13 +113,15 @@ def fetch_closed_issues_and_pr # @return [Array ] all pull requests def fetch_closed_pull_requests pull_requests = [] - options = { state: "merged", scope: :all} + options = { state: "merged", scope: :all } @client.merge_requests(@project_id, options).auto_paginate do |new_pr| new_pr = stringify_keys_deep(new_pr.to_hash) # align with Github naming new_pr["number"] = new_pr["iid"] new_pr["merged_at"] = new_pr["updated_at"] + new_pr["pull_request"] = true + new_pr["user"] = { login: new_pr["author"]["username"], html_url: new_pr["author"]["web_url"] } pull_requests.push(new_pr) end @@ -205,7 +206,7 @@ def fetch_compare(older, newer) commit["sha"] = commit["id"] end # TODO: do not know what the equivalent for gitlab is - if compare_data["compare_same_ref"] == true then + if compare_data["compare_same_ref"] == true raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." end @compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash) @@ -219,7 +220,7 @@ def fetch_compare(older, newer) # @return [Hash] def fetch_commit(commit_id) found = commits.find do |commit| - commit['sha'] == commit_id + commit["sha"] == commit_id end if found stringify_keys_deep(found.to_hash) @@ -228,7 +229,7 @@ def fetch_commit(commit_id) check_response do commit = @client.commit(@project_id, commit_id) commit = stringify_keys_deep(commit.to_hash) - commit['sha'] = commit['id'] + commit["sha"] = commit["id"] commit end end @@ -241,7 +242,7 @@ def commits if @commits.empty? @client.commits(@project_id).auto_paginate do |new_commit| new_commit = stringify_keys_deep(new_commit.to_hash) - new_commit['sha'] = new_commit['id'] + new_commit["sha"] = new_commit["id"] @commits.push(new_commit) end end @@ -311,7 +312,6 @@ def stringify_keys_deep(indata) # Exception raised to warn about moved repositories. MovedPermanentlyError = Class.new(RuntimeError) - def extract_request_args(args) if args.size == 1 && args.first.is_a?(Hash) args.delete_at(0) From 18ede113e3679da79cebb07eb77927f63072bb87 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 16:29:34 +0100 Subject: [PATCH 09/26] fixed typo in generator_fetcher --- lib/github_changelog_generator/generator/generator_fetcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/generator/generator_fetcher.rb b/lib/github_changelog_generator/generator/generator_fetcher.rb index 5d851af97..98e3b64ed 100644 --- a/lib/github_changelog_generator/generator/generator_fetcher.rb +++ b/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -165,7 +165,7 @@ def associate_rebase_comment_prs(tags, prs_left, total) # @param [Hash] issue def find_closed_date_by_commit(issue) unless issue["events"].nil? - # if it's PR -> then find "merged event", in case of usual issue -> fond closed date + # if it's PR -> then find "merged event", in case of usual issue -> find closed date compare_string = issue["merged_at"].nil? ? "closed" : "merged" # reverse! - to find latest closed event. (event goes in date order) issue["events"].reverse!.each do |event| From 86b41c0a2ff953c6386877e23aa5777ba6317c35 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 16:30:22 +0100 Subject: [PATCH 10/26] fixed issues and associated events in gitlab_fetcher --- .../gitlab_fetcher.rb | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index c2a494bd9..a65d8740e 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "date" require "tmpdir" require "retriable" require "gitlab" @@ -97,9 +96,16 @@ def closed_pr_options # # @return [Tuple] with (issues [Array ], pull-requests [Array ]) def fetch_closed_issues_and_pr + issues = [] print "Fetching closed issues...\r" if @options[:verbose] options = { state: "closed", scope: :all } - issues = @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS.merge(options)) + @client.issues(@project_id, DEFAULT_REQUEST_OPTIONS.merge(options)).auto_paginate do |issue| + issue = stringify_keys_deep(issue.to_hash) + issue["body"] = issue["description"] + issue["html_url"] = issue["web_url"] + issue["number"] = issue["iid"] + issues.push(issue) + end print_empty_line Helper.log.info "Received issues: #{issues.count}" @@ -119,6 +125,7 @@ def fetch_closed_pull_requests new_pr = stringify_keys_deep(new_pr.to_hash) # align with Github naming new_pr["number"] = new_pr["iid"] + new_pr["html_url"] = new_pr["web_url"] new_pr["merged_at"] = new_pr["updated_at"] new_pr["pull_request"] = true new_pr["user"] = { login: new_pr["author"]["username"], html_url: new_pr["author"]["web_url"] } @@ -138,15 +145,28 @@ def fetch_closed_pull_requests def fetch_events_async(issues) i = 0 threads = [] + options = { target_type: "issue" } + issue_events = [] + @client.project_events(@project_id, options).auto_paginate do |event| + event = stringify_keys_deep(event.to_hash) + # gitlab to github + event["event"] = event["action_name"] + issue_events.push(event) + end + # p issue_events issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice| issues_slice.each do |issue| threads << Thread.new do issue["events"] = [] - @client.project_events(@project_id).auto_paginate do |new_event| - issue["events"].push(new_event) + issue_events.each do |new_event| + if issue["id"] == new_event["target_id"] + if new_event["action_name"].eql? "closed" + issue["closed_at"] = issue["closed_at"].nil? ? new_event["created_at"] : issue["closed_at"] + end + issue["events"].push(new_event) + end end - issue["events"] = issue["events"].map { |event| stringify_keys_deep(event.to_hash) } print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}") i += 1 end @@ -190,7 +210,7 @@ def fetch_comments_async(prs) # # @return [Time] time of specified tag def fetch_date_of_tag(tag) - DateTime.parse(tag["commit"]["committed_date"]) + Time.parse(tag["commit"]["committed_date"]) end # Fetch and cache comparison between two github refs From 2c261cb20b6117f412f4c9a0319857aa4c70d5bd Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 17:03:43 +0100 Subject: [PATCH 11/26] fixed project_id for gitlab_fetcher in the case of forks --- lib/github_changelog_generator/gitlab_fetcher.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index a65d8740e..0ac70c779 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -40,7 +40,17 @@ def initialize(options = {}) Gitlab.sudo = nil @client = Gitlab::Client.new(gitlab_options) - @project_id = @client.project_search(@project).first.id + @project_id = find_project_id + end + + def find_project_id + project_id = nil + @client.project_search(@project).auto_paginate do |project| + if project.namespace.name.eql? @user + project_id = project.id + end + end + project_id end def gitlab_options @@ -68,9 +78,8 @@ def get_all_tags # @return [Array ] array of tags in repo def fetch_tags tags = [] - new_tags = @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS) - new_tags.auto_paginate do |new_tag| + @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS).auto_paginate do |new_tag| tags.push(new_tag) end print_empty_line From e696d2c3c6ec45cb3446b6b9700133b90b428fe9 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 17:32:04 +0100 Subject: [PATCH 12/26] fixed events for merge requests in gitlab_fetcher --- lib/github_changelog_generator/gitlab_fetcher.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 0ac70c779..1c3cfe027 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -154,7 +154,8 @@ def fetch_closed_pull_requests def fetch_events_async(issues) i = 0 threads = [] - options = { target_type: "issue" } + options = { } + options[:target_type] = issues.first["merged_at"].nil? ? "issue" : "merge_request" issue_events = [] @client.project_events(@project_id, options).auto_paginate do |event| event = stringify_keys_deep(event.to_hash) @@ -176,7 +177,7 @@ def fetch_events_async(issues) issue["events"].push(new_event) end end - print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}") + print_in_same_line("Fetching events for #{options[:target_type]}s: #{i + 1}/#{issues.count}") i += 1 end end @@ -202,6 +203,8 @@ def fetch_comments_async(prs) threads << Thread.new do pr["comments"] = [] @client.merge_request_notes(@project_id, pr["number"]) do |new_comment| + new_comment = stringify_keys_deep(new_comment.to_hash) + new_comment["body"] = new_comment["description"] pr["comments"].push(new_comment) end pr["comments"] = pr["comments"].map { |comment| stringify_keys_deep(comment.to_hash) } From 16fdb8dff04f648e56c839e74b92658c20bcd7a4 Mon Sep 17 00:00:00 2001 From: kreczko Date: Wed, 30 May 2018 17:32:40 +0100 Subject: [PATCH 13/26] fixed merge_commit_sha for older gitlab versions in gitlab_fetcher --- lib/github_changelog_generator/gitlab_fetcher.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 1c3cfe027..f58a03bba 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -138,6 +138,8 @@ def fetch_closed_pull_requests new_pr["merged_at"] = new_pr["updated_at"] new_pr["pull_request"] = true new_pr["user"] = { login: new_pr["author"]["username"], html_url: new_pr["author"]["web_url"] } + # to make it work with older gitlab version or repos that lived across versions + new_pr["merge_commit_sha"] = new_pr["merge_commit_sha"].nil? ? new_pr["sha"]: new_pr["merge_commit_sha"] pull_requests.push(new_pr) end From d1004f7ea242ebfd4e741a490ac7e2e86762783f Mon Sep 17 00:00:00 2001 From: kreczko Date: Tue, 7 Aug 2018 17:56:32 +0100 Subject: [PATCH 14/26] separated fetching issues and pull requests as it was causing problems at times --- lib/github_changelog_generator/generator/generator_fetcher.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/generator/generator_fetcher.rb b/lib/github_changelog_generator/generator/generator_fetcher.rb index 98e3b64ed..c82d6760f 100644 --- a/lib/github_changelog_generator/generator/generator_fetcher.rb +++ b/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -12,7 +12,8 @@ def fetch_events_for_issues_and_pr end # Async fetching events: - @fetcher.fetch_events_async(@issues + @pull_requests) + @fetcher.fetch_events_async(@issues) + @fetcher.fetch_events_async(@pull_requests) end # Async fetching of all tags dates From 681c4b41ebe8b2092affe2a533d05da7e9e8824a Mon Sep 17 00:00:00 2001 From: kreczko Date: Tue, 7 Aug 2018 18:14:28 +0100 Subject: [PATCH 15/26] fix gitlab fetcher for project that have no issues --- lib/github_changelog_generator/gitlab_fetcher.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index f58a03bba..ab35059ab 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -157,6 +157,9 @@ def fetch_events_async(issues) i = 0 threads = [] options = { } + if issues.empty? + return + end options[:target_type] = issues.first["merged_at"].nil? ? "issue" : "merge_request" issue_events = [] @client.project_events(@project_id, options).auto_paginate do |event| From 3b5a974ce42396b1d54534310668fcabcace60da Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 10:04:53 +0000 Subject: [PATCH 16/26] fixed if-logic based on feedback from olleolleolle --- lib/github_changelog_generator/generator/generator_fetcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator_fetcher.rb b/lib/github_changelog_generator/generator/generator_fetcher.rb index c82d6760f..fc7a70b5e 100644 --- a/lib/github_changelog_generator/generator/generator_fetcher.rb +++ b/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -78,12 +78,12 @@ def associate_tagged_prs(tags, prs, total) # https://developer.github.com/v3/pulls/#list-pull-requests # gitlab API has this merge_commit_sha = nil - if pr.has_key?("merge_commit_sha") then + if pr.has_key?("merge_commit_sha") merge_commit_sha = pr["merge_commit_sha"] elsif pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) merge_commit_sha = event["commit_id"] end - unless merge_commit_sha.nil? + if merge_commit_sha # Iterate tags.reverse (oldest to newest) to find first tag of each PR. if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(merge_commit_sha) }) pr["first_occurring_tag"] = oldest_tag["name"] From 2a1350fefa4cb49062b56edbf9df2227feb463d4 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 10:50:34 +0000 Subject: [PATCH 17/26] Github --> Gitlab for GitlabFetcher --- .../generator/generator.rb | 2 +- .../gitlab_fetcher.rb | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index 851d4d8ce..9c982e4fa 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -35,7 +35,7 @@ class Generator def initialize(options = {}) @options = options @tag_times_hash = {} - @fetcher = options[:gitlab] ? GitHubChangelogGenerator::GitlabFetcher.new(options) : GitHubChangelogGenerator::OctoFetcher.new(options) + @fetcher = options[:gitlab] ? GitLabChangelogGenerator::GitlabFetcher.new(options) : GitHubChangelogGenerator::OctoFetcher.new(options) @sections = [] end diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index ab35059ab..7305c0b48 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -4,18 +4,18 @@ require "retriable" require "gitlab" -module GitHubChangelogGenerator +module GitLabChangelogGenerator # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data # (such as filtering, validating, e.t.c) # # Example: - # fetcher = GitHubChangelogGenerator::OctoFetcher.new(options) + # fetcher = GitLabChangelogGenerator::GitlabFetcher.new(options) class GitlabFetcher PER_PAGE_NUMBER = 100 MAX_THREAD_NUMBER = 25 MAX_FORBIDDEN_RETRIES = 100 CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN" - GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ + RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument." NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \ "This script can make only 50 requests to GitHub API per hour without token!" @@ -55,7 +55,7 @@ def find_project_id def gitlab_options result = {} - access_token = fetch_github_token + access_token = fetch_auth_token result[:private_token] = access_token if access_token endpoint = @options[:github_endpoint] result[:endpoint] = endpoint if endpoint @@ -371,7 +371,7 @@ def check_response rescue Gitlab::Error::Forbidden => e fail_with_message(e, "Exceeded retry limit") rescue Gitlab::Error::Unauthorized => e - fail_with_message(e, "Error: wrong GitHub token") + fail_with_message(e, "Error: wrong GitLab token") end # Presents the exception, and the aborts with the message. @@ -400,7 +400,7 @@ def retry_callback proc do |exception, try, elapsed_time, next_interval| Helper.log.warn("RETRY - #{exception.class}: '#{exception.message}'") Helper.log.warn("#{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try") - Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG + Helper.log.warn RATE_LIMIT_EXCEEDED_MSG Helper.log.warn @client.rate_limit end end @@ -425,15 +425,15 @@ def print_empty_line # otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable. # # @return [String] - def fetch_github_token - env_var = @options[:token].presence || ENV["CHANGELOG_GITHUB_TOKEN"] + def fetch_auth_token + env_var = @options[:token].presence || ENV["CHANGELOG_GITLAB_TOKEN"] Helper.log.warn NO_TOKEN_PROVIDED unless env_var env_var end - # @return [String] helper to return Github "user/project" + # @return [String] "user/project" slug def user_project "#{@options[:user]}/#{@options[:project]}" end From 1de4431154df43da39dce0d034a218ddbed610d4 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 11:03:29 +0000 Subject: [PATCH 18/26] added Gitlab URI example --- lib/github_changelog_generator/gitlab_fetcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 7305c0b48..997ce7dd3 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -440,7 +440,7 @@ def user_project # Returns Hash of all querystring variables in given URI. # - # @param [String] uri eg. https://api.github.com/repositories/43914960/tags?page=37&foo=1 + # @param [String] uri eg. https://gitlab.example.com/api/v4/projects/43914960/repository/tags?page=37&foo=1 # @return [Hash] of all GET variables. eg. { 'page' => 37, 'foo' => 1 } def querystring_as_hash(uri) Hash[URI.decode_www_form(URI(uri).query || "")] From d62e1e10a9cd303918051f96f44c512f90510b24 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 14:12:03 +0000 Subject: [PATCH 19/26] missing change from Github->Gitlab change: Helper --> GitHubChangelogGenerator::Helper --- .../gitlab_fetcher.rb | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 997ce7dd3..7655a6b8f 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -85,10 +85,10 @@ def fetch_tags print_empty_line if tags.count == 0 - Helper.log.warn "Warning: Can't find any tags in repo. \ + GitHubChangelogGenerator::Helper.log.warn "Warning: Can't find any tags in repo. \ Make sure, that you push tags to remote repo via 'git push --tags'" else - Helper.log.info "Found #{tags.count} tags" + GitHubChangelogGenerator::Helper.log.info "Found #{tags.count} tags" end # tags are a Sawyer::Resource. Convert to hash tags.map { |resource| stringify_keys_deep(resource.to_hash) } @@ -117,7 +117,7 @@ def fetch_closed_issues_and_pr end print_empty_line - Helper.log.info "Received issues: #{issues.count}" + GitHubChangelogGenerator::Helper.log.info "Received issues: #{issues.count}" # separate arrays of issues and pull requests: [issues.map { |issue| stringify_keys_deep(issue.to_hash) }, fetch_closed_pull_requests] @@ -145,7 +145,7 @@ def fetch_closed_pull_requests print_empty_line - Helper.log.info "Pull Request count: #{pull_requests.count}" + GitHubChangelogGenerator::Helper.log.info "Pull Request count: #{pull_requests.count}" pull_requests.map { |pull_request| stringify_keys_deep(pull_request.to_hash) } end @@ -193,7 +193,7 @@ def fetch_events_async(issues) # to clear line from prev print print_empty_line - Helper.log.info "Fetching events for issues and PR: #{i}" + GitHubChangelogGenerator::Helper.log.info "Fetching events for issues and PR: #{i}" end # Fetch comments for PRs and add them to "comments" @@ -325,7 +325,7 @@ def fetch_tag_shas_async(tags) # to clear line from prev print print_empty_line - Helper.log.info "Fetching SHAs for tags: #{i}" + GitHubChangelogGenerator::Helper.log.info "Fetching SHAs for tags: #{i}" nil end @@ -376,7 +376,7 @@ def check_response # Presents the exception, and the aborts with the message. def fail_with_message(error, message) - Helper.log.error("#{error.class}: #{error.message}") + GitHubChangelogGenerator::Helper.log.error("#{error.class}: #{error.message}") sys_abort(message) end @@ -398,10 +398,10 @@ def sleep_base_interval def retry_callback proc do |exception, try, elapsed_time, next_interval| - Helper.log.warn("RETRY - #{exception.class}: '#{exception.message}'") - Helper.log.warn("#{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try") - Helper.log.warn RATE_LIMIT_EXCEEDED_MSG - Helper.log.warn @client.rate_limit + GitHubChangelogGenerator::Helper.log.warn("RETRY - #{exception.class}: '#{exception.message}'") + GitHubChangelogGenerator::Helper.log.warn("#{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try") + GitHubChangelogGenerator::Helper.log.warn RATE_LIMIT_EXCEEDED_MSG + GitHubChangelogGenerator::Helper.log.warn @client.rate_limit end end @@ -428,7 +428,7 @@ def print_empty_line def fetch_auth_token env_var = @options[:token].presence || ENV["CHANGELOG_GITLAB_TOKEN"] - Helper.log.warn NO_TOKEN_PROVIDED unless env_var + GitHubChangelogGenerator::Helper.log.warn NO_TOKEN_PROVIDED unless env_var env_var end From d6e257fa9562966b0c12a25bf0c643983d734513 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 14:37:15 +0000 Subject: [PATCH 20/26] CHANGELOG_GITHUB_TOKEN --> CHANGELOG_AUTH_TOKEN for gitlab fetcher --- lib/github_changelog_generator/gitlab_fetcher.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 7655a6b8f..3bb9fce52 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -14,10 +14,10 @@ class GitlabFetcher PER_PAGE_NUMBER = 100 MAX_THREAD_NUMBER = 25 MAX_FORBIDDEN_RETRIES = 100 - CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN" + CHANGELOG_AUTH_TOKEN = "CHANGELOG_AUTH_TOKEN" RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument." - NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \ + NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_AUTH_TOKEN was not found. " \ "This script can make only 50 requests to GitHub API per hour without token!" # @param options [Hash] Options passed in @@ -422,11 +422,11 @@ def print_empty_line end # Returns GitHub token. First try to use variable, provided by --token option, - # otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable. + # otherwise try to fetch it from CHANGELOG_AUTH_TOKEN env variable. # # @return [String] def fetch_auth_token - env_var = @options[:token].presence || ENV["CHANGELOG_GITLAB_TOKEN"] + env_var = @options[:token].presence || ENV["CHANGELOG_AUTH_TOKEN"] GitHubChangelogGenerator::Helper.log.warn NO_TOKEN_PROVIDED unless env_var From 264e6baf7d0f6306fdd93542c7cbbf3c686f3509 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 15:03:16 +0000 Subject: [PATCH 21/26] fixed RuboCop issues for gitlab_fetcher --- .../generator/generator_fetcher.rb | 17 +++++++++++------ .../gitlab_fetcher.rb | 14 +++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator_fetcher.rb b/lib/github_changelog_generator/generator/generator_fetcher.rb index fc7a70b5e..c299bf16a 100644 --- a/lib/github_changelog_generator/generator/generator_fetcher.rb +++ b/lib/github_changelog_generator/generator/generator_fetcher.rb @@ -77,12 +77,7 @@ def associate_tagged_prs(tags, prs, total) # https://developer.github.com/v3/pulls/#get-a-single-pull-request vs. # https://developer.github.com/v3/pulls/#list-pull-requests # gitlab API has this - merge_commit_sha = nil - if pr.has_key?("merge_commit_sha") - merge_commit_sha = pr["merge_commit_sha"] - elsif pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) - merge_commit_sha = event["commit_id"] - end + merge_commit_sha = try_merge_commit_sha_from_gitlab(pr) if merge_commit_sha # Iterate tags.reverse (oldest to newest) to find first tag of each PR. if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(merge_commit_sha) }) @@ -104,6 +99,16 @@ def associate_tagged_prs(tags, prs, total) end end + def try_merge_commit_sha_from_gitlab(merge_request) + merge_commit_sha = nil + if merge_request.key?("merge_commit_sha") + merge_commit_sha = merge_request["merge_commit_sha"] + elsif merge_request["events"] && (event = merge_request["events"].find { |e| e["event"] == "merged" }) + merge_commit_sha = event["commit_id"] + end + merge_commit_sha + end + # Associate merged PRs by the HEAD of the release branch. If no # --release-branch was specified, then the github default branch is used. # diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 3bb9fce52..06a095d01 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -46,9 +46,7 @@ def initialize(options = {}) def find_project_id project_id = nil @client.project_search(@project).auto_paginate do |project| - if project.namespace.name.eql? @user - project_id = project.id - end + project_id = project.id if project.namespace.name.eql? @user end project_id end @@ -79,7 +77,7 @@ def get_all_tags def fetch_tags tags = [] - @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS).auto_paginate do |new_tag| + @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS).auto_paginate do |new_tag| tags.push(new_tag) end print_empty_line @@ -139,7 +137,7 @@ def fetch_closed_pull_requests new_pr["pull_request"] = true new_pr["user"] = { login: new_pr["author"]["username"], html_url: new_pr["author"]["web_url"] } # to make it work with older gitlab version or repos that lived across versions - new_pr["merge_commit_sha"] = new_pr["merge_commit_sha"].nil? ? new_pr["sha"]: new_pr["merge_commit_sha"] + new_pr["merge_commit_sha"] = new_pr["merge_commit_sha"].nil? ? new_pr["sha"] : new_pr["merge_commit_sha"] pull_requests.push(new_pr) end @@ -156,10 +154,8 @@ def fetch_closed_pull_requests def fetch_events_async(issues) i = 0 threads = [] - options = { } - if issues.empty? - return - end + options = {} + return if issues.empty? options[:target_type] = issues.first["merged_at"].nil? ? "issue" : "merge_request" issue_events = [] @client.project_events(@project_id, options).auto_paginate do |event| From b13d759a9600cd35e1dc7aeb68088d8763aac786 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 16:38:24 +0000 Subject: [PATCH 22/26] preliminary tests for gitlab_fetcher --- spec/unit/gitlab_fetcher_spec.rb | 541 +++++++++++++++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 spec/unit/gitlab_fetcher_spec.rb diff --git a/spec/unit/gitlab_fetcher_spec.rb b/spec/unit/gitlab_fetcher_spec.rb new file mode 100644 index 000000000..1935206bb --- /dev/null +++ b/spec/unit/gitlab_fetcher_spec.rb @@ -0,0 +1,541 @@ +# frozen_string_literal: true + +describe GitLabChangelogGenerator::GitlabFetcher do + let(:options) do + { + user: "kreczko", + project: "changelog-testrepo", + github_endpoint: "https://gitlab.com/api/v4" + } + end + + let(:fetcher) { GitLabChangelogGenerator::GitlabFetcher.new(options) } + + describe "#check_response" do + context "when returns successfully" do + it "returns block value" do + expect(fetcher.send(:check_response) { 1 + 1 }).to eq(2) + end + end + + context "when raises GitLab::MissingCredentials" do + it "aborts" do + expect(fetcher).to receive(:sys_abort).with("Error: wrong AUTH token") + fetcher.send(:check_response) { raise(GitLab::MissingCredentials) } + end + end + + context "when raises GitLab::Forbidden" do + it "sleeps and retries and then aborts" do + retry_limit = GitLabChangelogGenerator::GitlabFetcher::MAX_FORBIDDEN_RETRIES - 1 + allow(fetcher).to receive(:sleep_base_interval).exactly(retry_limit).times.and_return(0) + + expect(fetcher).to receive(:sys_abort).with("Exceeded retry limit") + fetcher.send(:check_response) { raise(GitLab::Forbidden) } + end + end + end + + describe "#fetch_auth_token" do + token = GitLabChangelogGenerator::GitlabFetcher::CHANGELOG_AUTH_TOKEN + context "when token in ENV exist" do + before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) } + subject { fetcher.send(:fetch_auth_token) } + it { is_expected.to eq(VALID_TOKEN) } + end + + context "when token in ENV is nil" do + before { stub_const("ENV", ENV.to_hash.merge(token => nil)) } + subject { fetcher.send(:fetch_auth_token) } + it { is_expected.to be_nil } + end + + context "when token in options and ENV is nil" do + let(:options) { { token: VALID_TOKEN } } + + before do + stub_const("ENV", ENV.to_hash.merge(token => nil)) + end + + subject { fetcher.send(:fetch_auth_token) } + it { is_expected.to eq(VALID_TOKEN) } + end + + context "when token in options and ENV specified" do + let(:options) { { token: VALID_TOKEN } } + + before do + stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what")) + end + + subject { fetcher.send(:fetch_auth_token) } + it { is_expected.to eq(VALID_TOKEN) } + end + end + + describe "#get_all_tags" do + context "when fetch_tags returns tags" do + it "returns tags" do + mock_tags = ["tag"] + allow(fetcher).to receive(:fetch_tags).and_return(mock_tags) + expect(fetcher.get_all_tags).to eq(mock_tags) + end + end + end + + describe "#fetch_tags" do + context "when wrong token provided", :vcr do + let(:options) do + { + user: "skywinder", + project: "changelog_test", + token: INVALID_TOKEN, + github_endpoint: "https://gitlab.com/api/v4" + } + end + + it "should raise Unauthorized error" do + expect { fetcher.fetch_tags }.to raise_error SystemExit, "Error: wrong AUTH token" + end + end + + # TODO: swap for Gitlab API + context "when API call is valid", :vcr do + it "should return tags" do + expected_tags = [{ "name" => "v0.0.3", + "zipball_url" => + "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3", + "tarball_url" => + "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3", + "commit" => + { "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } }, + { "name" => "v0.0.2", + "zipball_url" => + "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.2", + "tarball_url" => + "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.2", + "commit" => + { "sha" => "9b35bb13dcd15b68e7bcbf10cde5eb937a54f710", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/9b35bb13dcd15b68e7bcbf10cde5eb937a54f710" } }, + { "name" => "v0.0.1", + "zipball_url" => + "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.1", + "tarball_url" => + "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.1", + "commit" => + { "sha" => "4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d" } }, + { "name" => "0.0.4", + "zipball_url" => + "https://api.github.com/repos/skywinder/changelog_test/zipball/0.0.4", + "tarball_url" => + "https://api.github.com/repos/skywinder/changelog_test/tarball/0.0.4", + "commit" => + { "sha" => "ece0c3ab7142b21064b885061c55ede00ef6ce94", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/ece0c3ab7142b21064b885061c55ede00ef6ce94" } }] + + expect(fetcher.fetch_tags).to eq(expected_tags) + end + + it "should return tags count" do + tags = fetcher.fetch_tags + expect(tags.size).to eq(4) + end + end + end + + describe "#fetch_closed_issues_and_pr" do + context "when API call is valid", :vcr do + it "returns issues" do + issues, pull_requests = fetcher.fetch_closed_issues_and_pr + expect(issues.size).to eq(7) + expect(pull_requests.size).to eq(14) + end + + it "returns issue with proper key/values" do + issues, _pull_requests = fetcher.fetch_closed_issues_and_pr + + expected_issue = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14", + "repository_url" => "https://api.github.com/repos/skywinder/changelog_test", + "labels_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}", + "comments_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/comments", + "events_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/events", + "html_url" => "https://github.com/skywinder/changelog_test/issues/14", + "id" => 95_419_412, + "number" => 14, + "title" => "Issue closed from commit from PR", + "user" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "labels" => [], + "state" => "closed", + "locked" => false, + "assignee" => nil, + "assignees" => [], + "milestone" => nil, + "comments" => 0, + "created_at" => "2015-07-16T12:06:08Z", + "updated_at" => "2015-07-16T12:21:42Z", + "closed_at" => "2015-07-16T12:21:42Z", + "body" => "" } + + # Convert times to Time + expected_issue.each_pair do |k, v| + expected_issue[k] = Time.parse(v) if v =~ /^2015-/ + end + + expect(issues.first).to eq(expected_issue) + end + + it "returns pull request with proper key/values" do + _issues, pull_requests = fetcher.fetch_closed_issues_and_pr + + expected_pr = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/21", + "repository_url" => "https://api.github.com/repos/skywinder/changelog_test", + "labels_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/21/labels{/name}", + "comments_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/21/comments", + "events_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/21/events", + "html_url" => "https://github.com/skywinder/changelog_test/pull/21", + "id" => 124_925_759, + "number" => 21, + "title" => "Merged br (should appear in change log with #20)", + "user" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "labels" => [], + "state" => "closed", + "locked" => false, + "assignee" => nil, + "assignees" => [], + "milestone" => nil, + "comments" => 0, + "created_at" => "2016-01-05T09:24:08Z", + "updated_at" => "2016-01-05T09:26:53Z", + "closed_at" => "2016-01-05T09:24:27Z", + "pull_request" => + { "url" => "https://api.github.com/repos/skywinder/changelog_test/pulls/21", + "html_url" => "https://github.com/skywinder/changelog_test/pull/21", + "diff_url" => "https://github.com/skywinder/changelog_test/pull/21.diff", + "patch_url" => "https://github.com/skywinder/changelog_test/pull/21.patch" }, + "body" => + "to test https://github.com/skywinder/github-changelog-generator/pull/305\r\nshould appear in change log with #20" } + + # Convert times to Time + expected_pr.each_pair do |k, v| + expected_pr[k] = Time.parse(v) if v =~ /^2016-01/ + end + + expect(pull_requests.first).to eq(expected_pr) + end + + it "returns issues with labels" do + issues, _pull_requests = fetcher.fetch_closed_issues_and_pr + expected = [[], [], ["Bug"], [], ["enhancement"], ["some label"], []] + expect(issues.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected) + end + + it "returns pull_requests with labels" do + _issues, pull_requests = fetcher.fetch_closed_issues_and_pr + expected = [[], [], [], [], [], ["enhancement"], [], [], ["invalid"], [], [], [], [], ["invalid"]] + expect(pull_requests.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected) + end + end + end + + describe "#fetch_closed_pull_requests" do + context "when API call is valid", :vcr do + it "returns pull requests" do + pull_requests = fetcher.fetch_closed_pull_requests + expect(pull_requests.size).to eq(14) + end + + it "returns correct pull request keys" do + pull_requests = fetcher.fetch_closed_pull_requests + + pr = pull_requests.first + expect(pr.keys).to eq(%w[url id html_url diff_url patch_url issue_url number state locked title user body created_at updated_at closed_at merged_at merge_commit_sha assignee assignees milestone commits_url review_comments_url review_comment_url comments_url statuses_url head base _links]) + end + end + end + + describe "#fetch_events_async" do + context "when API call is valid", :vcr do + it "populates issues" do + issues = [{ "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14", + "repository_url" => "https://api.github.com/repos/skywinder/changelog_test", + "labels_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}", + "comments_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/comments", + "events_url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/14/events", + "html_url" => "https://github.com/skywinder/changelog_test/issues/14", + "id" => 95_419_412, + "number" => 14, + "title" => "Issue closed from commit from PR", + "user" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => + "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "labels" => [], + "state" => "closed", + "locked" => false, + "assignee" => nil, + "assignees" => [], + "milestone" => nil, + "comments" => 0, + "created_at" => "2015-07-16T12:06:08Z", + "updated_at" => "2015-07-16T12:21:42Z", + "closed_at" => "2015-07-16T12:21:42Z", + "body" => "" }] + + # Check that they are blank to begin with + expect(issues.first["events"]).to be_nil + + fetcher.fetch_events_async(issues) + issue_events = issues.first["events"] + + expected_events = [{ "id" => 357_462_189, + "url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189", + "actor" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => + "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "event" => "referenced", + "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555", + "commit_url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555", + "created_at" => "2015-07-16T12:21:16Z" }, + { "id" => 357_462_542, + "url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462542", + "actor" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => + "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "event" => "closed", + "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555", + "commit_url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555", + "created_at" => "2015-07-16T12:21:42Z" }] + + # Convert times to Time + expected_events.map! do |event| + event.each_pair do |k, v| + event[k] = Time.parse(v) if v =~ /^201[56]-/ + end + end + + expect(issue_events).to eq(expected_events) + end + end + end + + describe "#fetch_date_of_tag" do + context "when API call is valid", :vcr do + it "returns date" do + tag = { "name" => "v0.0.3", + "zipball_url" => + "https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3", + "tarball_url" => + "https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3", + "commit" => + { "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } } + + dt = fetcher.fetch_date_of_tag(tag) + expect(dt).to eq(Time.parse("2015-03-04 19:01:48 UTC")) + end + end + end + + describe "#querystring_as_hash" do + it "works on the blank URL" do + expect { fetcher.send(:querystring_as_hash, "") }.not_to raise_error + end + + it "where there are no querystring params" do + expect { fetcher.send(:querystring_as_hash, "http://example.com") }.not_to raise_error + end + + it "returns a String-keyed Hash of querystring params" do + expect(fetcher.send(:querystring_as_hash, "http://example.com/o.html?str=18&wis=12")).to include("wis" => "12", "str" => "18") + end + end + + describe "#fetch_commit" do + context "when API call is valid", :vcr do + it "returns commit" do + event = { "id" => 357_462_189, + "url" => + "https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189", + "actor" => + { "login" => "skywinder", + "id" => 3_356_474, + "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", + "gravatar_id" => "", + "url" => "https://api.github.com/users/skywinder", + "html_url" => "https://github.com/skywinder", + "followers_url" => "https://api.github.com/users/skywinder/followers", + "following_url" => + "https://api.github.com/users/skywinder/following{/other_user}", + "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", + "starred_url" => + "https://api.github.com/users/skywinder/starred{/owner}{/repo}", + "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", + "organizations_url" => "https://api.github.com/users/skywinder/orgs", + "repos_url" => "https://api.github.com/users/skywinder/repos", + "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", + "received_events_url" => + "https://api.github.com/users/skywinder/received_events", + "type" => "User", + "site_admin" => false }, + "event" => "referenced", + "commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555", + "commit_url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555", + "created_at" => "2015-07-16T12:21:16Z" } + commit = fetcher.fetch_commit(event) + + expectations = [ + %w[sha decfe840d1a1b86e0c28700de5362d3365a29555], + ["url", + "https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555"], + # OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/decfe840d1a1b86e0c28700de5362d3365a29555"], + ["html_url", + "https://github.com/skywinder/changelog_test/commit/decfe840d1a1b86e0c28700de5362d3365a29555"], + ["author", + { "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars2.githubusercontent.com/u/3356474?v=4", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }], + ["committer", + { "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars2.githubusercontent.com/u/3356474?v=4", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }], + ["parents", + [{ "sha" => "7ec095e5e3caceacedabf44d0b9b10da17c92e51", + "url" => + "https://api.github.com/repos/skywinder/changelog_test/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51", + # OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51", + "html_url" => + "https://github.com/skywinder/changelog_test/commit/7ec095e5e3caceacedabf44d0b9b10da17c92e51" }]] + ] + + expectations.each do |property, val| + expect(commit[property]).to eq(val) + end + end + end + end + + describe "#commits" do + context "when API is valid", :vcr do + subject do + fetcher.commits + end + + it "returns commits" do + expect(subject.last["sha"]).to eq("4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d") + end + end + end +end From b1f9d092cf7f9f6be35cfd3e6db64c73e8c00fde Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 16:46:27 +0000 Subject: [PATCH 23/26] added gitlab_changelog_generator --- .gitignore | 3 +- bin/gitlab_changelog_generator | 5 ++++ lib/gitlab_changelog_generator.rb | 48 +++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100755 bin/gitlab_changelog_generator create mode 100755 lib/gitlab_changelog_generator.rb diff --git a/.gitignore b/.gitignore index ad0adbc35..7e3fbbc46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -bin/ +bin/* !bin/git-generate-changelog !bin/github_changelog_generator +!bin/gitlab_changelog_generator pkg/ coverage/ .bundle diff --git a/bin/gitlab_changelog_generator b/bin/gitlab_changelog_generator new file mode 100755 index 000000000..86ff22b9d --- /dev/null +++ b/bin/gitlab_changelog_generator @@ -0,0 +1,5 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true + +require_relative "../lib/gitlab_changelog_generator" +GitLabChangelogGenerator::ChangelogGenerator.new.run diff --git a/lib/gitlab_changelog_generator.rb b/lib/gitlab_changelog_generator.rb new file mode 100755 index 000000000..8197b5fe1 --- /dev/null +++ b/lib/gitlab_changelog_generator.rb @@ -0,0 +1,48 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "octokit" +require "faraday-http-cache" +require "logger" +require "active_support" +require "active_support/core_ext/object/blank" +require "json" +require "multi_json" +require "benchmark" + +require "github_changelog_generator/helper" +require "github_changelog_generator/options" +require "github_changelog_generator/parser" +require "github_changelog_generator/parser_file" +require "github_changelog_generator/generator/generator" +require "github_changelog_generator/version" +require "github_changelog_generator/reader" + +# The main module, where placed all classes (now, at least) +module GitLabChangelogGenerator + # Main class and entry point for this script. + class ChangelogGenerator + # Class, responsible for whole changelog generation cycle + # @return initialised instance of ChangelogGenerator + def initialize + @options = GitHubChangelogGenerator::Parser.parse_options + @options[:gitlab] = true + @generator = GitHubChangelogGenerator::Generator.new @options + end + + # The entry point of this script to generate changelog + # @raise (ChangelogGeneratorError) Is thrown when one of specified tags was not found in list of tags. + def run + log = @generator.compound_changelog + + if @options.write_to_file? + output_filename = @options[:output].to_s + File.open(output_filename, "wb") { |file| file.write(log) } + puts "Done!" + puts "Generated log placed in #{Dir.pwd}/#{output_filename}" + else + puts log + end + end + end +end From 0e66b159559f60fab8d1931ea90f1d0cf2766049 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 16:53:31 +0000 Subject: [PATCH 24/26] Last few Github -> Gitlab conversions --- lib/github_changelog_generator/gitlab_fetcher.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index 06a095d01..e0b39a161 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -5,7 +5,7 @@ require "gitlab" module GitLabChangelogGenerator - # A Fetcher responsible for all requests to GitHub and all basic manipulation with related data + # A Fetcher responsible for all requests to GitLab and all basic manipulation with related data # (such as filtering, validating, e.t.c) # # Example: @@ -15,10 +15,10 @@ class GitlabFetcher MAX_THREAD_NUMBER = 25 MAX_FORBIDDEN_RETRIES = 100 CHANGELOG_AUTH_TOKEN = "CHANGELOG_AUTH_TOKEN" - RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \ + RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitLab API rate limit exceeded, changelog may be " \ "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument." NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_AUTH_TOKEN was not found. " \ - "This script can make only 50 requests to GitHub API per hour without token!" + "This script can make only 50 requests to GitLab API per hour without token!" # @param options [Hash] Options passed in # @option options [String] :user Gitlab username @@ -417,7 +417,7 @@ def print_empty_line print_in_same_line(" ") end - # Returns GitHub token. First try to use variable, provided by --token option, + # Returns AUTH token. First try to use variable, provided by --token option, # otherwise try to fetch it from CHANGELOG_AUTH_TOKEN env variable. # # @return [String] From 182ff69325f1d456bc7a33716e90540c493215e4 Mon Sep 17 00:00:00 2001 From: kreczko Date: Thu, 1 Nov 2018 17:07:58 +0000 Subject: [PATCH 25/26] resolved second batch of feedback from @olleolleolle --- .../gitlab_fetcher.rb | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/github_changelog_generator/gitlab_fetcher.rb b/lib/github_changelog_generator/gitlab_fetcher.rb index e0b39a161..721c94470 100644 --- a/lib/github_changelog_generator/gitlab_fetcher.rb +++ b/lib/github_changelog_generator/gitlab_fetcher.rb @@ -21,8 +21,8 @@ class GitlabFetcher "This script can make only 50 requests to GitLab API per hour without token!" # @param options [Hash] Options passed in - # @option options [String] :user Gitlab username - # @option options [String] :project Gitlab project + # @option options [String] :user GitLab username + # @option options [String] :project GitLab project # @option options [String] :since Only issues updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. eg. Time.parse("2016-01-01 10:00:00").iso8601 # @option options [Boolean] :http_cache Use ActiveSupport::Cache::FileStore to cache http requests # @option options [Boolean] :cache_file If using http_cache, this is the cache file path @@ -78,17 +78,16 @@ def fetch_tags tags = [] @client.tags(@project_id, DEFAULT_REQUEST_OPTIONS).auto_paginate do |new_tag| - tags.push(new_tag) + tags << new_tag end print_empty_line - if tags.count == 0 + if tags.empty? GitHubChangelogGenerator::Helper.log.warn "Warning: Can't find any tags in repo. \ Make sure, that you push tags to remote repo via 'git push --tags'" else GitHubChangelogGenerator::Helper.log.info "Found #{tags.count} tags" end - # tags are a Sawyer::Resource. Convert to hash tags.map { |resource| stringify_keys_deep(resource.to_hash) } end @@ -98,8 +97,7 @@ def closed_pr_options }.tap { |options| options[:since] = @since if @since } end - # This method fetch all closed issues and separate them to pull requests and pure issues - # (pull request is kind of issue in term of GitHub) + # This method fetch all closed issues pull requests (GitLab uses the term "merge requests") # # @return [Tuple] with (issues [Array ], pull-requests [Array ]) def fetch_closed_issues_and_pr @@ -138,7 +136,7 @@ def fetch_closed_pull_requests new_pr["user"] = { login: new_pr["author"]["username"], html_url: new_pr["author"]["web_url"] } # to make it work with older gitlab version or repos that lived across versions new_pr["merge_commit_sha"] = new_pr["merge_commit_sha"].nil? ? new_pr["sha"] : new_pr["merge_commit_sha"] - pull_requests.push(new_pr) + pull_requests << new_pr end print_empty_line @@ -156,15 +154,15 @@ def fetch_events_async(issues) threads = [] options = {} return if issues.empty? + options[:target_type] = issues.first["merged_at"].nil? ? "issue" : "merge_request" issue_events = [] @client.project_events(@project_id, options).auto_paginate do |event| event = stringify_keys_deep(event.to_hash) # gitlab to github event["event"] = event["action_name"] - issue_events.push(event) + issue_events << event end - # p issue_events issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice| issues_slice.each do |issue| @@ -175,7 +173,7 @@ def fetch_events_async(issues) if new_event["action_name"].eql? "closed" issue["closed_at"] = issue["closed_at"].nil? ? new_event["created_at"] : issue["closed_at"] end - issue["events"].push(new_event) + issue["events"] << new_event end end print_in_same_line("Fetching events for #{options[:target_type]}s: #{i + 1}/#{issues.count}") @@ -194,7 +192,7 @@ def fetch_events_async(issues) # Fetch comments for PRs and add them to "comments" # - # @param [Array] prs The array of PRs. + # @param prs [Array] PRs for which to fetch comments # @return [Void] No return; PRs are updated in-place. def fetch_comments_async(prs) threads = [] @@ -219,18 +217,18 @@ def fetch_comments_async(prs) # Fetch tag time from repo # - # @param [Hash] tag GitHub data item about a Tag + # @param [Hash] tag GitLab data item about a Tag # # @return [Time] time of specified tag def fetch_date_of_tag(tag) Time.parse(tag["commit"]["committed_date"]) end - # Fetch and cache comparison between two github refs + # Fetch and cache comparison between two GitLab refs # # @param [String] older The older sha/tag/branch. # @param [String] newer The newer sha/tag/branch. - # @return [Hash] Github api response for comparison. + # @return [Hash] GitLab api response for comparison. def fetch_compare(older, newer) unless @compares["#{older}...#{newer}"] compare_data = check_response { @client.compare(@project_id, older, newer || "HEAD") } @@ -240,7 +238,7 @@ def fetch_compare(older, newer) end # TODO: do not know what the equivalent for gitlab is if compare_data["compare_same_ref"] == true - raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." + raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issue and describe how to replicate this issue." end @compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash) end @@ -276,7 +274,7 @@ def commits @client.commits(@project_id).auto_paginate do |new_commit| new_commit = stringify_keys_deep(new_commit.to_hash) new_commit["sha"] = new_commit["id"] - @commits.push(new_commit) + @commits << new_commit end end @commits @@ -298,7 +296,7 @@ def default_branch # "shas_in_tag" # # @param [Array] tags The array of tags. - # @return [Nil] No return; tags are updated in-place. + # @return [void] No return; tags are updated in-place. def fetch_tag_shas_async(tags) i = 0 threads = [] From 602a923b64b0624d70971959b487ae917aa040ae Mon Sep 17 00:00:00 2001 From: Chris Denneen Date: Wed, 29 Jan 2020 09:54:34 -0500 Subject: [PATCH 26/26] Add gitlab to rake task --- lib/github_changelog_generator/task.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/task.rb b/lib/github_changelog_generator/task.rb index 57a5a19f3..d06dc6451 100644 --- a/lib/github_changelog_generator/task.rb +++ b/lib/github_changelog_generator/task.rb @@ -17,7 +17,7 @@ class RakeTask < ::Rake::TaskLib compare_link include_labels exclude_labels bug_labels enhancement_labels between_tags exclude_tags exclude_tags_regex since_tag max_issues - github_site github_endpoint simple_list + github_site github_endpoint simple_list gitlab future_release release_branch verbose release_url base configure_sections add_sections] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy