spreadtheword 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b676601f96251a4526b79a395e3a7b93a7d83e253d2f7897c2648cf1d529e4ad
4
+ data.tar.gz: 983c4020d19cb4ffa9d2e2ce7f1cbc94454184301294306b548ff12b16eb94d9
5
+ SHA512:
6
+ metadata.gz: ce7082cff5b1ec90ff41d0002af108010abdc2616f034c44bf6cc86715d72dfd90bd42c604ab15b5d55c9f81bf9367ac4967130832360664b1122a73f097565c
7
+ data.tar.gz: 6c36e551114b0557c5c57182491835e4eac5ae1eb25669c42cd68b65fb26090b67d9bafd385bb8da692bf5b3e838f8cfef3c864e3fab1b80b9b3e155a71d3ecf
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in spreadtheword.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,112 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ spreadtheword (1.0.0)
5
+ activesupport (~> 5.2)
6
+ gitlab (~> 4.3.0)
7
+ google-cloud-translate (~> 1.2)
8
+ nokogiri (~> 1.8)
9
+ pry (~> 0.11.3)
10
+ wrike3 (~> 0.4.0)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activesupport (5.2.0)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 0.7, < 2)
18
+ minitest (~> 5.1)
19
+ tzinfo (~> 1.1)
20
+ addressable (2.5.2)
21
+ public_suffix (>= 2.0.2, < 4.0)
22
+ coderay (1.1.2)
23
+ concurrent-ruby (1.0.5)
24
+ faraday (0.15.2)
25
+ multipart-post (>= 1.2, < 3)
26
+ gitlab (4.3.0)
27
+ httparty
28
+ terminal-table
29
+ google-cloud-core (1.2.1)
30
+ google-cloud-env (~> 1.0)
31
+ google-cloud-env (1.0.2)
32
+ faraday (~> 0.11)
33
+ google-cloud-translate (1.2.0)
34
+ faraday (~> 0.13)
35
+ google-cloud-core (~> 1.2)
36
+ googleauth (~> 0.6.2)
37
+ googleauth (0.6.2)
38
+ faraday (~> 0.12)
39
+ jwt (>= 1.4, < 3.0)
40
+ logging (~> 2.0)
41
+ memoist (~> 0.12)
42
+ multi_json (~> 1.11)
43
+ os (~> 0.9)
44
+ signet (~> 0.7)
45
+ httparty (0.13.7)
46
+ json (~> 1.8)
47
+ multi_xml (>= 0.5.2)
48
+ i18n (1.0.1)
49
+ concurrent-ruby (~> 1.0)
50
+ json (1.8.6)
51
+ jwt (2.1.0)
52
+ little-plugger (1.1.4)
53
+ logging (2.2.2)
54
+ little-plugger (~> 1.1)
55
+ multi_json (~> 1.10)
56
+ memoist (0.16.0)
57
+ metaclass (0.0.4)
58
+ method_source (0.9.0)
59
+ mime-types (3.1)
60
+ mime-types-data (~> 3.2015)
61
+ mime-types-data (3.2016.0521)
62
+ mini_portile2 (2.3.0)
63
+ minitest (5.11.3)
64
+ mocha (1.5.0)
65
+ metaclass (~> 0.0.1)
66
+ multi_json (1.13.1)
67
+ multi_xml (0.6.0)
68
+ multipart-post (2.0.0)
69
+ nokogiri (1.8.3)
70
+ mini_portile2 (~> 2.3.0)
71
+ nokogiri (1.8.3-x64-mingw32)
72
+ mini_portile2 (~> 2.3.0)
73
+ os (0.9.6)
74
+ pry (0.11.3)
75
+ coderay (~> 1.1.0)
76
+ method_source (~> 0.9.0)
77
+ public_suffix (3.0.2)
78
+ rake (10.5.0)
79
+ shoulda (3.5.0)
80
+ shoulda-context (~> 1.0, >= 1.0.1)
81
+ shoulda-matchers (>= 1.4.1, < 3.0)
82
+ shoulda-context (1.2.2)
83
+ shoulda-matchers (2.8.0)
84
+ activesupport (>= 3.0.0)
85
+ signet (0.8.1)
86
+ addressable (~> 2.3)
87
+ faraday (~> 0.9)
88
+ jwt (>= 1.5, < 3.0)
89
+ multi_json (~> 1.10)
90
+ terminal-table (1.8.0)
91
+ unicode-display_width (~> 1.1, >= 1.1.1)
92
+ thread_safe (0.3.6)
93
+ tzinfo (1.2.5)
94
+ thread_safe (~> 0.1)
95
+ unicode-display_width (1.4.0)
96
+ wrike3 (0.4.0)
97
+ httparty (~> 0.13.7)
98
+ mime-types
99
+ mocha (~> 1.1)
100
+ shoulda (~> 3.5)
101
+
102
+ PLATFORMS
103
+ ruby
104
+ x64-mingw32
105
+
106
+ DEPENDENCIES
107
+ bundler (~> 1.16)
108
+ rake (~> 10.0)
109
+ spreadtheword!
110
+
111
+ BUNDLED WITH
112
+ 1.16.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Minqi Pan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ <h1 align="center" style="border-bottom: none;">Spread the Word</h1>
2
+ <h3 align="center">Automatically generate a release-note document based on git commit messages</h3>
3
+ <p align="center">
4
+ <a href="https://travis-ci.org/pmq20/spreadtheword">
5
+ <img alt="Build Status" src="https://travis-ci.org/pmq20/spreadtheword.svg?branch=master" />
6
+ </a>
7
+ <a href="https://ci.appveyor.com/project/pmq20/spreadtheword/branch/master">
8
+ <img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/xdb4p03gvrjr0m6m?svg=true" />
9
+ </a>
10
+ <a href="https://codecov.io/gh/pmq20/spreadtheword">
11
+ <img alt="codecov" src="https://codecov.io/gh/pmq20/spreadtheword/branch/master/graph/badge.svg" />
12
+ </a>
13
+ <a href="https://snyk.io/test/github/pmq20/spreadtheword">
14
+ <img src="https://snyk.io/test/github/pmq20/spreadtheword/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/pmq20/spreadtheword?targetFile=Frontend%2Fpackage.json" style="max-width:100%;">
15
+ </a>
16
+ <a href="http://isitmaintained.com/project/pmq20/spreadtheword">
17
+ <img alt="Average time to resolve an issue" src="http://isitmaintained.com/badge/resolution/pmq20/spreadtheword.svg" />
18
+ </a>
19
+ <a href="http://isitmaintained.com/project/pmq20/spreadtheword">
20
+ <img alt="Percentage of issues still open" src="http://isitmaintained.com/badge/open/pmq20/spreadtheword.svg" />
21
+ </a>
22
+ </p>
23
+
24
+ ## Features
25
+
26
+ * Multiple projects are supported, which means git messages from multiple repositories can be merged to produce a unified release document
27
+ * Multiple output formats are supported, e.g. LaTeX
28
+ * Integrates with Wrike and GitLab to fetch developement task titles
29
+ * Integrates with Google Translate to automatically translate messages to English
30
+
31
+ ## Commit Message Conventions
32
+
33
+ - `{W1}` menas Wrike task `XXX` as in Wrike's permalink `https://www.wrike.com/open.htm?id=XXX`. Eg. `{W4123780} fix config files`
34
+ - `{#1}` means issue #1 of Gitlab. Eg. `{#63} add backend code for fields`
35
+ - `{prj#1}` means issue #1 of Gitlab project `prj`. Eg. `{UI#1} add code for forms`
36
+
37
+ ## Install
38
+
39
+ gem install spreadtheword
40
+
41
+ ## Usage
42
+
43
+ spreadtheword [PROJECT 1] [PROJECT 2]...[PROJECT N] [OPTION 1] [OPTION 2]...[OPTION N]
44
+ --author=STRING Specifies the author of the output document. Default: user.name of git config
45
+ --console If present, start the console after spreadtheword initializes.
46
+ --google-translate-key=STRING
47
+ Specifies a Google Translate API access key and translate commit messages that contain non-ASCII characters to English.
48
+ -h, --help Prints this help and exit
49
+ --gitlab-endpoint=URL Specifies GitLab API endpoint URL.
50
+ --gitlab-token=STRING Specifies the OAuth access token of your GitLab.
51
+ --quiet If present, spreadtheword would not output anything to stderr.
52
+ --since=TAG/COMMIT-SHA1 Specifies the begining from which the git commits will be fetched. Default: the first commit
53
+ --title=STRING Specifies the title of the output document. Default: "Relase Notes"
54
+ -v, --version Prints the version of spreadtheword and exit
55
+ --wrike-token=STRING Specifies the access token of your Wrike API app.
56
+
57
+ ## Notes
58
+
59
+ * If no projects were provided, the current directory would be used as the sole project directory;
60
+ * If multiple projects were provided, the git commit messages of those projects would be merged;
61
+ * If no options were specified, their default (see below) will be used.
62
+
63
+ ## Example
64
+
65
+ spreadtheword \
66
+ /projects/X \
67
+ /projects/Y \
68
+ --since=v1.5 \
69
+ --title="Your Project v1.6 Release Notes" \
70
+ --author="Your Company, Inc." \
71
+ --google-translate-key="XXX" \
72
+ --gitlab-endpoint="https://example.net/api/v4" \
73
+ --gitlab-token="XXX" \
74
+ --wrike-token="XXX" \
75
+ > v1.6.tex
76
+
77
+ Hint: you might need to set environment variable `LANG=en_US.UTF-8` to support wide characters of git log messages.
78
+
79
+ ## License
80
+
81
+ MIT
82
+
83
+ ## See Also
84
+
85
+ - [gitlab](https://github.com/narkoz/gitlab): Ruby client and CLI for GitLab API.
86
+ - [wrike3](https://github.com/morshedalam/wrike3): Ruby client for the Wrike API V3.
87
+ - [google-cloud-translate](https://github.com/GoogleCloudPlatform/google-cloud-ruby/tree/master/google-cloud-translate): the official library for Google Cloud Translation API.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "spreadtheword"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/spreadtheword ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (c) 2018 Minqi Pan <pmq2001@gmail.com>
4
+ #
5
+ # This file is part of spreadtheword, distributed under the MIT License
6
+ # For full terms see the included LICENSE file
7
+
8
+ require 'bundler/setup'
9
+ require 'optparse'
10
+ require 'ostruct'
11
+ require 'pry'
12
+ require 'spreadtheword'
13
+
14
+ USAGE = %Q{
15
+ Spread the Word (spreadtheword) v#{::Spreadtheword::VERSION}
16
+ Automatically generate a release-note document based on git commit messages.
17
+ Notes
18
+ * If no projects were provided, the current directory would be used as the sole project directory;
19
+ * If multiple projects were provided, the git commit messages of those projects would be merged;
20
+ * If no options were specified, their default (see below) will be used.
21
+ Usage
22
+ spreadtheword [PROJECT 1] [PROJECT 2]...[PROJECT N] [OPTION 1] [OPTION 2]...[OPTION N]
23
+ }.strip
24
+
25
+ projects = []
26
+ ARGV.keep_if do |x|
27
+ if '-' == x[0]
28
+ true
29
+ else
30
+ projects << x.dup
31
+ false
32
+ end
33
+ end
34
+
35
+ options = OpenStruct.new
36
+
37
+ outer_opts = nil
38
+
39
+ usage = lambda do |out|
40
+ out.puts outer_opts
41
+ end
42
+
43
+ OptionParser.new do |opts|
44
+ opts.banner = USAGE
45
+
46
+ opts.on("--author=STRING", "Specifies the author of the output document. Default: user.name of git config") do |x|
47
+ options.author = x
48
+ end
49
+
50
+ opts.on("--console", "If present, start the console after spreadtheword initializes.") do
51
+ options.console = true
52
+ end
53
+
54
+ opts.on("--google-translate-key=STRING", "Specifies a Google Translate API access key and translate commit messages that contain non-ASCII characters to English.") do |x|
55
+ options.googleTranslateKey = x
56
+ end
57
+
58
+ opts.on("-h", "--help", "Prints this help and exit") do
59
+ usage.call(STDOUT)
60
+ STDOUT.puts
61
+ exit 0
62
+ end
63
+
64
+ opts.on("--gitlab-endpoint=URL", "Specifies GitLab API endpoint URL.") do |x|
65
+ options.gitlabEndpoint = x
66
+ end
67
+
68
+ opts.on("--gitlab-token=STRING", "Specifies the OAuth access token of your GitLab.") do |x|
69
+ options.gitlabToken = x
70
+ end
71
+
72
+ opts.on("--quiet", "If present, spreadtheword would not output anything to stderr.") do
73
+ options.quiet = true
74
+ end
75
+
76
+ opts.on("--since=TAG/COMMIT-SHA1", 'Specifies the begining from which the git commits will be fetched. Default: the first commit') do |x|
77
+ options.since = x
78
+ end
79
+
80
+ opts.on("--title=STRING", 'Specifies the title of the output document. Default: "Relase Notes"') do |x|
81
+ options.title = x
82
+ end
83
+
84
+ opts.on("-v", "--version", "Prints the version of spreadtheword and exit") do
85
+ puts ::Spreadtheword::VERSION
86
+ exit 0
87
+ end
88
+
89
+ opts.on("--wrike-token=STRING", "Specifies the access token of your Wrike API app.") do |x|
90
+ options.wrikeToken = x
91
+ end
92
+
93
+ outer_opts = opts
94
+ end.parse!
95
+
96
+ instance = ::Spreadtheword.new projects, options
97
+ if options.console
98
+ binding.pry
99
+ end
100
+ instance.run!
101
+
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ ruby %~dp0\spreadtheword %*
@@ -0,0 +1,142 @@
1
+ require 'nokogiri'
2
+
3
+ class Spreadtheword::LaTeX
4
+ def initialize title, author, topics, getTranslation
5
+ @title = title
6
+ @author = author
7
+ @topics = topics
8
+ @getTranslation = getTranslation
9
+ end
10
+
11
+ def write!
12
+ puts %Q_
13
+ % !TEX TS-program = pdflatex
14
+ % !TEX encoding = UTF-8 Unicode
15
+
16
+ \\documentclass[11pt]{article} % use larger type; default would be 10pt
17
+ \\usepackage{hyperref}
18
+ \\usepackage[utf8]{inputenc} % set input encoding (not needed with XeLaTeX)
19
+ \\usepackage{geometry} % to change the page dimensions
20
+ \\geometry{a4paper} % or letterpaper (US) or a5paper or....
21
+ \\usepackage{graphicx} % support the \\includegraphics command and options
22
+ \\usepackage{booktabs} % for much better looking tables
23
+ \\usepackage{array} % for better arrays (eg matrices) in maths
24
+ \\usepackage{paralist} % very flexible & customisable lists (eg. enumerate/itemize, etc.)
25
+ \\usepackage{verbatim} % adds environment for commenting out blocks of text & for better verbatim
26
+ \\usepackage{subfig} % make it possible to include more than one captioned figure/table in a single float
27
+ \\usepackage{fancyhdr} % This should be set AFTER setting up the page geometry
28
+ \\pagestyle{fancy} % options: empty , plain , fancy
29
+ \\renewcommand{\\headrulewidth}{0pt} % customise the layout...
30
+ \\lhead{}\\chead{}\\rhead{}
31
+ \\lfoot{}\\cfoot{\\thepage}\\rfoot{}
32
+ \\usepackage[nottoc,notlof,notlot]{tocbibind} % Put the bibliography in the ToC
33
+ \\usepackage[titles,subfigure]{tocloft} % Alter the style of the Table of Contents
34
+ \\renewcommand{\\cftsecfont}{\\rmfamily\\mdseries\\upshape}
35
+ \\renewcommand{\\cftsecpagefont}{\\rmfamily\\mdseries\\upshape} % No bold!
36
+ \\title{#{@title}}
37
+ \\author{#{@author}}
38
+ \\begin{document}
39
+ \\maketitle
40
+ \\setcounter{tocdepth}{1}
41
+ \\tableofcontents
42
+ \\newpage
43
+ #{sections}
44
+ \\end{document}
45
+ _
46
+ end
47
+
48
+ def sections
49
+ ret = ''
50
+ @topics.each do |k,v|
51
+ next if k.nil?
52
+ first = v[0]
53
+ title = k
54
+ description = ''
55
+ url = ''
56
+ if :gitlab == first[:origin]
57
+ title = first[:title]
58
+ description = first[:payload].description
59
+ url = first[:payload].web_url
60
+ elsif :wrike == first[:origin]
61
+ title = first[:title]
62
+ description = Nokogiri::HTML(first[:payload]['description'].gsub('<br />', "\n\n")).text
63
+ url = first[:payload][:spreadthewordPermalink]
64
+ end
65
+ ret += %Q_
66
+ \\section{#{escape title}}
67
+
68
+ \\subsection{Background}
69
+
70
+ \\url{#{escape url}}
71
+ _
72
+
73
+ if description.present?
74
+ ret += %Q_
75
+ \\subsection{Description}
76
+
77
+ #{escape description}
78
+
79
+ _
80
+ end
81
+
82
+ ret += printDevelopers(v)
83
+ ret += %Q_
84
+ \\newpage
85
+ _
86
+ end
87
+ if @topics[nil]
88
+ ret += %Q_
89
+ \\section{Others}
90
+ _
91
+ ret += printDevelopers(@topics[nil])
92
+ end
93
+ ret
94
+ end
95
+
96
+ def printDevelopers(values)
97
+ developers = {}
98
+ values.each do |x|
99
+ developers[x[:commit].author] ||= []
100
+ developers[x[:commit].author] << x
101
+ end
102
+ ret = %Q_
103
+ \\subsection{Developers}
104
+
105
+ \\begin{enumerate}
106
+ _
107
+ developers.each do |k,v|
108
+ ret += %Q_
109
+ \\item #{escape k} ($#{v.size*100 / values.size}\\%$)
110
+ _
111
+ end
112
+ ret += %Q_
113
+ \\end{enumerate}
114
+ _
115
+ developers.each do |k,v|
116
+ ret += %Q_
117
+ \\subsection{#{escape k}'s Commit Messages}
118
+ \\begin{enumerate}
119
+ _
120
+ uniqM = v.map do |x|
121
+ x[:commit].msg
122
+ end.uniq
123
+ uniqM.each do |x|
124
+ ret += %Q_
125
+ \\item #{escape x}
126
+ _
127
+ end
128
+ ret += %Q_
129
+ \\end{enumerate}
130
+ _
131
+ end
132
+ ret
133
+ end
134
+
135
+ def escape str
136
+ return 'N/A' unless str.present?
137
+ if @getTranslation && str =~ Spreadtheword::NONASCII
138
+ str = @getTranslation.call(str)
139
+ end
140
+ str.gsub(Spreadtheword::NONASCII, '').gsub('\\', '\\textbackslash ').gsub('&', '\\\&').gsub('%', '\\%').gsub('$', '\\$').gsub('#', '\\#').gsub('_', '\\_').gsub('{', '\\{').gsub('}', '\\}').gsub('~', '\\textasciitilde ').gsub('^', '\\textasciicircum ')
141
+ end
142
+ end
@@ -0,0 +1,12 @@
1
+ class Spreadtheword::Utils
2
+ def initialize(options)
3
+ @options = options
4
+ end
5
+
6
+ def say(something)
7
+ unless @options.quiet
8
+ STDERR.print something
9
+ STDERR.flush
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ class Spreadtheword
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,203 @@
1
+ require 'uri'
2
+ require 'ostruct'
3
+ require 'active_support/all'
4
+ require 'wrike3'
5
+ require 'gitlab'
6
+ require 'spreadtheword/version'
7
+ require 'spreadtheword/utils'
8
+ require 'spreadtheword/latex'
9
+ require 'google/cloud/translate'
10
+
11
+ class Spreadtheword
12
+ CONNECTOR = '__spreadtheword__'
13
+ NONASCII = /[^\u0000-\u007F]+/
14
+
15
+ def initialize(projects, options)
16
+ @projects = projects.any? ? projects : [Dir.pwd]
17
+ @title = options.title ? options.title : 'Relase Notes'
18
+ @author = options.author ? options.author : gitUserName
19
+ @since = options.since
20
+
21
+ configureGitlab(options) if options.gitlabToken
22
+ configureWrike(options) if options.wrikeToken
23
+ configureGoogleTranslate(options) if options.googleTranslateKey
24
+
25
+ @utils = Utils.new(options)
26
+ @logs = []
27
+ @topics = {}
28
+ end
29
+
30
+ def configureGitlab(options)
31
+ Gitlab.configure do |config|
32
+ config.endpoint = options.gitlabEndpoint
33
+ config.private_token = options.gitlabToken
34
+ end
35
+ @gitlab = URI(options.gitlabEndpoint)
36
+ @gitlabCurrentProject = nil
37
+ @gilabProjects = {}
38
+ @gitlabCache = {}
39
+ end
40
+
41
+ def configureWrike(options)
42
+ Wrike3.configure do |config|
43
+ config.access_token = options.wrikeToken
44
+ end
45
+ @wrike = Wrike3()
46
+ @wrikeCache = {}
47
+ end
48
+
49
+ def configureGoogleTranslate(options)
50
+ @translate = Google::Cloud::Translate.new(key: options.googleTranslateKey)
51
+ translateCache = {}
52
+ @getTranslation = lambda { |sentence|
53
+ unless translateCache[sentence]
54
+ @utils.say "Translating\n-> #{sentence}\n"
55
+ translateCache[sentence] = @translate.translate(sentence, to: "en").text
56
+ @utils.say "<- #{translateCache[sentence]}\n"
57
+ end
58
+ translateCache[sentence]
59
+ }
60
+ end
61
+
62
+ def getGitlab(projectId, issueNumber)
63
+ unless @gitlabCache[projectId] && @gitlabCache[projectId][issueNumber]
64
+ @gitlabCache[projectId] ||= {}
65
+ @gitlabCache[projectId][issueNumber] = Gitlab.issue(projectId, issueNumber)
66
+ end
67
+ return @gitlabCache[projectId][issueNumber]
68
+ end
69
+
70
+ def getWrike(wId)
71
+ unless @wrikeCache[wId]
72
+ permalink = "https://www.wrike.com/open.htm?id=#{wId}"
73
+ @utils.say "Fetching Wrike task #{permalink}"
74
+ tasks = @wrike.task.list nil, nil, permalink: permalink
75
+ @utils.say "."
76
+ taskId = tasks['data'][0]['id']
77
+ task = @wrike.task.details taskId
78
+ @utils.say "."
79
+ @wrikeCache[wId] = task['data'][0]
80
+ @wrikeCache[wId][:spreadthewordPermalink] = permalink
81
+ @utils.say "\n"
82
+ end
83
+ return @wrikeCache[wId]
84
+ end
85
+
86
+ def run!
87
+ fetchAllLogs
88
+ parseTopics
89
+ writer = Spreadtheword::LaTeX.new(@title, @author, @topics, @getTranslation)
90
+ writer.write!
91
+ end
92
+
93
+ def gitlabSetCurrentProject
94
+ remotes = `git remote -v`
95
+ remotes.to_s.split("\n").each do |line|
96
+ if line.include?(@gitlab.host)
97
+ lines = line.split(@gitlab.host)
98
+ liness = lines[1].split('/')
99
+ @gitlabCurrentProject = {
100
+ namespace: liness[1],
101
+ project: liness[2].split('.git')[0],
102
+ }
103
+ return
104
+ end
105
+ end
106
+ end
107
+
108
+ def fetchAllLogs
109
+ @projects.each do |project|
110
+ @utils.say "Fetching git commit logs from #{project}\n"
111
+ Dir.chdir(project) do
112
+ gitlabSetCurrentProject if @gitlab
113
+ fetchLogs
114
+ end
115
+ end
116
+ end
117
+
118
+ def fetchLogs
119
+ cmd = %Q{git log --pretty=format:"%an__spreadtheword__%s"}
120
+ if @since
121
+ cmd = %Q{#{cmd} #{@since}..master}
122
+ end
123
+ logs = `#{cmd}`.to_s.split("\n")
124
+ logs.delete_if do |x|
125
+ x.nil? || '' == x.to_s.strip
126
+ end
127
+ logs.map! do |x|
128
+ contents = x.split(CONNECTOR)
129
+ if contents[1].nil? || '' == contents[1].to_s.strip
130
+ contents[1] = ''
131
+ end
132
+ OpenStruct.new.tap do |y|
133
+ y.author = contents[0]
134
+ y.origMsg = contents[1]
135
+ if @translate && y.origMsg =~ NONASCII
136
+ y.msg = @getTranslation.call(contents[1])
137
+ else
138
+ y.msg = y.origMsg
139
+ end
140
+ if @gitlab
141
+ y.gitlabProject = @gitlabCurrentProject
142
+ end
143
+ end
144
+ end
145
+ @logs.concat logs
146
+ end
147
+
148
+ def parseTopics
149
+ @logs.each do |x|
150
+ origin = :plain
151
+ identifier = nil
152
+ payload = nil
153
+ title = 'Others'
154
+ begin
155
+ if x.origMsg =~ /\{W(\d+)\}/
156
+ origin = :wrike
157
+ identifier = "W#{$1}"
158
+ payload = getWrike($1)
159
+ title = payload['title']
160
+ x.msg = x.msg.gsub(/\{W\d+\}/, '')
161
+ elsif x.origMsg =~ /\{#(\d+)\}/
162
+ origin = :gitlab
163
+ targetProjectId = "#{x.gitlabProject[:namespace]}/#{x.gitlabProject[:project]}"
164
+ identifier = "#{targetProjectId}##{$1}"
165
+ payload = getGitlab(targetProjectId, $1)
166
+ title = payload.title
167
+ x.msg = x.msg.gsub(/\{#\d+\}/, '')
168
+ elsif x.origMsg =~ /\{(.+)#(\d+)\}/
169
+ origin = :gitlab
170
+ if $1.include?('/')
171
+ targetProjectId = $1.dup
172
+ else
173
+ targetProjectId = "#{x.gitlabProject[:namespace]}/#{$1}"
174
+ end
175
+ identifier = "#{targetProjectId}##{$2}"
176
+ payload = getGitlab(targetProjectId, $2)
177
+ title = payload.title
178
+ x.msg = x.msg.gsub(/\{.+#\d+\}/, '')
179
+ end
180
+ rescue => e
181
+ STDERR.puts "!!! Exception when parsing topic !!! #{e}"
182
+ origin = :plain
183
+ identifier = nil
184
+ payload = nil
185
+ title = 'Others'
186
+ end
187
+ if @translate && title =~ NONASCII
188
+ title = @getTranslation.call(title)
189
+ end
190
+ @topics[identifier] ||= []
191
+ @topics[identifier] << {
192
+ origin: origin,
193
+ commit: x,
194
+ payload: payload,
195
+ title: title,
196
+ }
197
+ end
198
+ end
199
+
200
+ def gitUserName
201
+ `git config --get user.name`.to_s.strip
202
+ end
203
+ end
@@ -0,0 +1,33 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "spreadtheword/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "spreadtheword"
8
+ spec.version = Spreadtheword::VERSION
9
+ spec.authors = ["Minqi Pan"]
10
+ spec.email = ["pmq2001@gmail.com"]
11
+
12
+ spec.summary = %q{Automatically generate a release-note document based on git commit messages.}
13
+ spec.description = %q{Automatically generate a release-note document based on git commit messages.}
14
+ spec.homepage = "https://github.com/pmq20/spreadtheword"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+ spec.add_dependency 'gitlab', '~> 4.3.0'
26
+ spec.add_dependency 'wrike3', '~> 0.4.0'
27
+ spec.add_dependency 'google-cloud-translate', '~> 1.2'
28
+ spec.add_dependency 'activesupport', '~> 5.2'
29
+ spec.add_dependency 'nokogiri', '~> 1.8'
30
+ spec.add_dependency 'pry', '~> 0.11.3'
31
+ spec.add_development_dependency "bundler", "~> 1.16"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreadtheword
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Minqi Pan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gitlab
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: wrike3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: google-cloud-translate
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.8'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.11.3
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.11.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.16'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.16'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ description: Automatically generate a release-note document based on git commit messages.
126
+ email:
127
+ - pmq2001@gmail.com
128
+ executables:
129
+ - spreadtheword
130
+ - spreadtheword.bat
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - Gemfile
136
+ - Gemfile.lock
137
+ - LICENSE
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - exe/spreadtheword
143
+ - exe/spreadtheword.bat
144
+ - lib/spreadtheword.rb
145
+ - lib/spreadtheword/latex.rb
146
+ - lib/spreadtheword/utils.rb
147
+ - lib/spreadtheword/version.rb
148
+ - spreadtheword.gemspec
149
+ homepage: https://github.com/pmq20/spreadtheword
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.7.7
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Automatically generate a release-note document based on git commit messages.
173
+ test_files: []