-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathdocs_generator.rb
196 lines (166 loc) · 5.2 KB
/
docs_generator.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
require 'set'
require 'logging'
require 'docs_compressor'
require 'git_manager'
require 'generators/release'
require 'generators/master'
require 'version_number'
# This class is responsible for coordinating docs generation.
#
# The documentation is generated below `basedir`. There, each release and master
# have their own directory:
#
# tag1
# tag2
# tag3
# ...
# tagN
# master
#
# The generator checks the current release tags of the project to detect new
# releases in any of the branches from 3.2 up.
#
# Top-level symlinks point to the actual root directories. If we assume v4.1.0
# is the current stable release, this is the idea:
#
# api/v3.2.0 -> basedir/v3.2.0/doc/rdoc
# api/v3.2 -> api/v3.2.0
# api/v4.1.0 -> basedir/v4.1.0/doc/rdoc
# api/v4.1 -> api/v4.1.0
# api/stable -> api/v4.1.0
# api/edge -> basedir/master/doc/rdoc
#
# and same for guides:
#
# guides/v3.2.0 -> basedir/v3.2.0/railties/guides/output
# guides/v3.2 -> guides/v3.2.0
# guides/v4.1.0 -> basedir/v4.1.0/guides/output
# guides/v4.1 -> guides/v4.1.0
# guides/stable -> guides/v4.1.0
# guides/edge -> basedir/master/guides/output
#
# If new releases are detected, symlinks are adjusted as needed.
#
# Once everything related to release docs is done, edge docs are generated.
#
# Documentation files are further compressed to leverage NGINX gzip_static.
#
# The docs generator assumes a master directory with an up to date working
# copy, it is the responsability of the caller to get that in place via the
# git manager. It is also the responsibility of the caller to ensure there is
# only one generator being executed at the same time.
class DocsGenerator
include Logging
API = 'api'
GUIDES = 'guides'
STABLE = 'stable'
EDGE = 'edge'
attr_reader :basedir, :git_manager
# To instantiate a docs generator you need to pass the base directory under
# which the structure documented above has to be generated. This is the home
# directory in the docs server.
#
# @param basedir [String]
# @param git_manager [GitManager]
def initialize(basedir, git_manager = GitManager.new(basedir), verbose: false, tags: nil)
@basedir = File.expand_path(basedir)
@git_manager = git_manager
@verbose = verbose
@tags = tags.to_s.split(',')
end
def generate
Dir.chdir(basedir) do
generate_release_docs
generate_edge_docs
end
end
def generate_release_docs
new_release_docs = false
git_manager.release_tags.each do |tag|
targeted = @tags.any? && @tags.include?(tag)
if !targeted && @tags.any?
log "Skipping #{tag}"
next
end
if generate_docs_for_release?(tag) || targeted
generate_docs_for_release(tag)
new_release_docs = true
end
end
adjust_symlinks_for_series if new_release_docs
end
def generate_docs_for_release?(tag)
VersionNumber.new(tag) >= '3.2' && !Dir.exist?(tag)
end
def generate_docs_for_release(tag)
git_manager.checkout(tag)
generator = Generators::Release.new(tag, tag, verbose: @verbose)
generator.generate
DocsCompressor.new(generator.api_output).compress
DocsCompressor.new(generator.guides_output).compress
create_api_symlink(generator.api_output, tag)
create_guides_symlink(generator.guides_output, tag)
end
def generate_edge_docs
if @tags.any? && [email protected]?("edge")
log "Skipping edge"
return
end
generator = Generators::Master.new(
git_manager.short_sha1,
'master',
verbose: @verbose
)
generator.generate
DocsCompressor.new(generator.api_output).compress
DocsCompressor.new(generator.guides_output).compress
# Force the recreation of the symlink to be forward compatible, if the docs
# structure changes in master we need the symlink to point to the new dirs.
create_api_symlink(generator.api_output, EDGE, force: true)
create_guides_symlink(generator.guides_output, EDGE, force: true)
end
def create_api_symlink(origin, symlink, options={})
create_symlink(API, origin, symlink, options)
end
def create_guides_symlink(origin, symlink, options={})
create_symlink(GUIDES, origin, symlink, options)
end
def create_symlink(dir, origin, symlink, options)
FileUtils.mkdir_p(dir)
Dir.chdir(dir) do
FileUtils.rm_f(symlink) if options[:force]
File.symlink(origin, symlink)
end
end
def adjust_symlinks_for_series
series.each do |series, target|
[API, GUIDES].each do |_|
Dir.chdir(_) do
unless File.exist?(series) && File.readlink(series) == target
FileUtils.rm_f(series)
File.symlink(target, series)
end
end
end
end
end
def series
rds = release_directories
{STABLE => max_tag(rds)}.tap do |series|
directories_per_serie = rds.to_set.classify {|rd| rd[/v\d+.\d+/]}
directories_per_serie.each do |s, dirs|
series[s] = max_tag(dirs)
end
end
end
def release_directories
[].tap do |dirs|
Dir.foreach(basedir) do |fname|
dirs << fname if File.basename(fname) =~ /\Av[\d.]+\z/
end
end
end
def max_tag(tags)
tags.max {|t, g| VersionNumber.new(t) <=> VersionNumber.new(g)}
end
end