forked from pact-foundation/docs.pact.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
support.rb
210 lines (180 loc) · 6.81 KB
/
support.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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
require 'addressable'
require 'octokit'
require 'base64'
require 'fileutils'
require 'pathname'
module UrlAbsolutizer
extend self
def absolutize_links(contents, source_repository_slug, link_transformer, contents_source_path, synced_source_paths)
contents.gsub(/\]\(([^)]+)\)/) { | match |
url = match[2..-2]
if url.start_with?('http', '#')
match
else
url_without_anchor, anchor = url.split('#', 2)
optional_anchor = anchor ? "##{anchor}" : ""
# the path of the file in the docs.pact.io site
path_from_root = if url.start_with?('.')
Addressable::URI.parse(File.join(File.dirname(contents_source_path), url_without_anchor)).normalize.to_s.chomp('/')
else
url_without_anchor.delete_prefix('/')
end
transformed_link = link_transformer.call(path_from_root) + optional_anchor
if synced_source_paths.include?(path_from_root)
"](#{transformed_link})"
elsif synced_source_paths.include?(File.join(path_from_root, 'README.md'))
"](#{transformed_link})"
else
absolute_url = (Addressable::URI.parse("https://github.com/#{source_repository_slug}/blob/master/") + path_from_root).to_s + optional_anchor
"](#{absolute_url})"
end
end
}
end
end
class MarkdownFileContents
attr_accessor :lines, :fields, :comments
def initialize(lines, fields = {}, comments = [])
@lines = lines
@fields = fields
@comments = comments
end
def extract_title
heading_underline_index = lines.find_index{ |line| line.start_with?("======") }
if heading_underline_index && heading_underline_index > 0
lines.delete_at(heading_underline_index)
title = lines.delete_at(heading_underline_index - 1).strip
self.fields[:title] = title
else
heading_index = lines.find_index { | line | line.start_with?('#') }
if heading_index
title = lines.delete_at(heading_index).gsub(/^#+/, '').strip
self.fields[:title] = title
else
raise "Could not find a heading for page with head lines #{lines[0..5].join}"
end
end
end
def to_s
comments_maybe_newlines = comments.dup
if lines.first && lines.first.strip != ''
comments_maybe_newlines << ""
end
["---", header_lines, "---", comments_maybe_newlines, lines].flatten.join("\n") + "\n"
end
def find_and_replace(find, replace)
@lines = lines.collect{ |line| line.gsub(find, replace) }
end
def remove_lines_including(substring)
@lines = lines.select{ |line| !line.include?(substring) }
end
def add_lines_at_start(*new_lines)
@lines = new_lines + lines
end
def clean_up_changelog
@lines = lines.collect do | line |
if line.start_with?('#')
line
elsif line.strip.size.zero?
line
elsif line.include?('feat:') || line.include?('fix:') || line.include?('Merge pull request')
line
end
end.compact
end
def absolutize_links(repository_slug, link_transformer, contents_source_path, source_paths)
@lines = lines.collect { | line | UrlAbsolutizer.absolutize_links(line, repository_slug, link_transformer, contents_source_path, source_paths) }
end
def escape_things_that_look_like_jsx_tags
in_code_block = false
@lines = lines.collect do | line |
in_code_block = !in_code_block if line.start_with?('```')
if !in_code_block
if line =~ /<[A-Za-z0-9\-_\s]+>/
in_backticks = false
line.chars.collect do | char |
# Doesn't handle escaped backticks within backticks
in_backticks = !in_backticks if char == '`'
if !in_backticks
case char
when '<' then '<'
when '>' then '&;gt;'
else char
end
else
char
end
end.join
else
line
end
else
line
end
end
end
private
def header_lines
[:title, :custom_edit_url, :slug, :description].collect do | key |
if fields[key]
"#{key}: #{fields[key]}"
end
end.compact
end
end
PROJECT_ROOT = File.expand_path(File.join(__FILE__, '..', '..', '..'))
def relative_path_to path
Pathname.new(File.join(PROJECT_ROOT, 'website', path)).relative_path_from(Pathname.pwd)
end
def edit_comment_for slug
"<!-- This file has been synced from the #{slug} repository. Please do not edit it directly. The URL of the source file can be found in the custom_edit_url value above -->"
end
def get_file_list(repository_slug, include_conditions = [], exclude_conditions = [])
client = Octokit::Client.new(:access_token => ENV['GITHUB_ACCESS_TOKEN'])
client.auto_paginate = true
tree = client.tree(repository_slug, 'master', recursive: true).tree
if include_conditions.any? || exclude_conditions.any?
filter_file_list(tree, include_conditions, exclude_conditions)
else
tree
end
end
def filter_file_list(file_list, include_conditions, exclude_conditions = [])
file_list.select do | file |
include_conditions.any?{ |lambda| lambda.call(file.path) } && !exclude_conditions.any? { |lambda| lambda.call(file.path) }
end
end
def each_file(files)
files.each do | file |
file_resource = file.rels[:self].get
yield file.path, Base64.decode64(file_resource.data.content)
end
end
def select_actions(custom_actions, path)
custom_actions
.select{ | selector , _ | selector == :all || selector == path || (selector.respond_to?(:call) && selector.call(path)) }
.collect(&:last)
end
def process_file(path, content, path_transformer, custom_actions, comment, source_file_paths, source_repository_slug)
destination = path_transformer.call(path)
fields = { custom_edit_url: "https://github.com/#{source_repository_slug}/edit/master/#{path}" }
md_file_contents = MarkdownFileContents.new(content.split("\n"), fields, [comment])
select_actions(custom_actions, path).each { |action| action.call(md_file_contents) }
if source_file_paths && source_repository_slug
link_transformer = -> (path){
path_transformer.call(path).delete_prefix('website/docs').chomp('.md')
}
md_file_contents.absolutize_links(source_repository_slug, link_transformer, path, source_file_paths)
end
puts "Writing file #{destination}"
FileUtils.mkdir_p(File.dirname(destination))
File.open(destination, "w") { |file| file << md_file_contents.to_s }
md_file_contents
end
def sync source_repository_slug, include_filter, ignore_filter, path_transformer, actions
file_list = get_file_list(source_repository_slug)
sync_file_list = filter_file_list(file_list, include_filter, ignore_filter)
processed_files = each_file(sync_file_list) do | path, content |
process_file(path, content, path_transformer, actions, edit_comment_for(source_repository_slug), sync_file_list.collect(&:path), source_repository_slug)
end
end