mirror of
https://github.com/jekyll/jekyll.git
synced 2026-04-28 03:01:03 -04:00
This is a 🙋 feature or enhancement. This is a 🔨 code refactoring. ## Summary Improve and streamline our release processes with some extra automation and a bit more rigor around PRs/commits. ## Context With Jekyll 4.4 released, and under the assumption that the next release will indeed be 5.0, I think it makes a lot of sense to take some time and evaluate our development practices and streamline the process of shipping. We generally go a long time (four months between 4.3.4 and 4.4.0, two years between 4.3.0 and 4.4.0) between releases and this is my attempt at trying to improve that. While this PR is currently incomplete, if there's interest in going this direction, I'll make time over the next few days to clean it up and get it ready to actually ship. In order to do this, I'm relying on the [`release-please`](https://github.com/googleapis/release-please-action) action from Google to do the majority of the heavy lifting. Please read the release-please README in order to learn how release-please works and what it does. In order to make it easier to adopt release-please, I've made two additional changes. ~~The first is to rename `History.markdown` to `CHANGELOG.md` since that's what `release-please` works with out of the box.~~ The other is to add two new github actions workflows to run release-please and to enforce conventional commit conventions on PR titles. Because we squash merge, the PR title becomes the commit message and `release-please` uses the commit messages to know when to bump the version number. One potential caveat with this is that it may become harder to maintain stable branches. My point of view on this is that we've done a relatively poor job of maintaining them regardless and I think it's more important to release often, even if we end up bumping major or minor version numbers more frequently than before. My stance on this is that version bumps have no inherent goodness or badness. They are a communication mechanism. We should let go of having to wait a certain amount of time to do major version bumps or avoid work because it would cause a major version bump, for example. ### Process changes The use of release-please means that we can stop using jekyllbot to do the merges and update History.markdown for us, as release-please will take care of that when we cut the release. We will also no longer need labels on PRs as the use of conventional commits will explain exactly what is changing. The process for releasing becomes: - Merge the docs PR - Merge the automatically generated release-please PR, which will trigger the workflows to do the tagging, releasing, gem publishing, etc. ### Remaining work to do: - [x] Change the pull request settings to only allow squash merges, as jekyllbot enforces this for us today. - [x] ~Update the site publishing process to pull from `CHANGELOG.md` instead of `History.markdown`~ No longer needed. - [x] Integrate jekyllbot into release-please (the release-please PRs will be made by jekyllbot). This allows actions to be triggered on the release-please PRs. - [x] Test the workflows to make sure they generate a PR correctly. - [x] ~Integrate the release publishing workflow into release-please when it creates a release.~ Happens automatically with the existing workflows.
172 lines
4.3 KiB
Ruby
172 lines
4.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "rubygems"
|
|
require "rake"
|
|
require "rdoc"
|
|
require "date"
|
|
require "yaml"
|
|
|
|
$LOAD_PATH.unshift File.expand_path("lib", __dir__)
|
|
require "jekyll/version"
|
|
require "bundler/gem_tasks"
|
|
|
|
Dir.glob("rake/**.rake").each { |f| import f }
|
|
|
|
#############################################################################
|
|
#
|
|
# Helper functions
|
|
#
|
|
#############################################################################
|
|
|
|
def name
|
|
"jekyll"
|
|
end
|
|
|
|
def version
|
|
Jekyll::VERSION
|
|
end
|
|
|
|
def docs_name
|
|
"#{name}-docs"
|
|
end
|
|
|
|
def docs_folder
|
|
"docs"
|
|
end
|
|
|
|
def gemspec_file
|
|
"#{name}.gemspec"
|
|
end
|
|
|
|
def gem_file
|
|
"#{name}-#{Gem::Version.new(version)}.gem"
|
|
end
|
|
|
|
def normalize_bullets(markdown)
|
|
# Normalize both old-style indented bullets (" *") and
|
|
# release-please-style non-indented bullets ("*") to "-"
|
|
markdown.gsub(%r!\n\s{0,2}\*{1}!, "\n-")
|
|
end
|
|
|
|
def linkify_prs(markdown)
|
|
# Match bare PR references like #1234 but skip those already linkified
|
|
# by release-please as [#1234](url)
|
|
markdown.gsub(%r{(?<!&)(?<!\[)#(\d+)}) do |word|
|
|
"[#{word}]({{ site.repository }}/issues/#{word.delete("#")})"
|
|
end
|
|
end
|
|
|
|
def linkify(markdown)
|
|
linkify_prs(markdown)
|
|
end
|
|
|
|
def liquid_escape(markdown)
|
|
markdown.gsub(%r!(`{[{%].+[}%]}`)!, "{% raw %}\\1{% endraw %}")
|
|
end
|
|
|
|
def custom_release_header_anchors(markdown)
|
|
# Match old format: "X.Y.Z / YYYY-MM-DD"
|
|
# Match new release-please format: "[X.Y.Z](compare-url) (YYYY-MM-DD)"
|
|
header_regexp = %r!^(?:\[?)(\d{1,2})\.(\d{1,2})\.(\d{1,2})(?:\]\([^)]*\))? [\(/]\s?\d{4}-\d{2}-\d{2}\)?!
|
|
section_regexp = %r!^### \w[\w ]*$!
|
|
markdown.split(%r!^##\s!).map do |release_notes|
|
|
_, major, minor, patch = *release_notes.match(header_regexp)
|
|
release_notes
|
|
.gsub(header_regexp, "\\0\n{: #v\\1-\\2-\\3}")
|
|
.gsub(section_regexp) { |section| "#{section}\n{: ##{slugify(section)}-v#{major}-#{minor}-#{patch}}" }
|
|
end.join("\n## ")
|
|
end
|
|
|
|
def slugify(header)
|
|
header.delete("#").strip.downcase.gsub(%r!\s+!, "-")
|
|
end
|
|
|
|
def remove_head_from_history(markdown)
|
|
# Match both old format "## X.Y.Z" and release-please format "## [X.Y.Z]"
|
|
index = markdown =~ %r!^##\s+\[?\d+\.\d+\.\d+!
|
|
markdown[index..-1]
|
|
end
|
|
|
|
def converted_history(markdown)
|
|
remove_head_from_history(
|
|
custom_release_header_anchors(
|
|
liquid_escape(
|
|
linkify(
|
|
normalize_bullets(markdown)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
end
|
|
|
|
def siteify_file(file, overrides_front_matter = {})
|
|
abort "You seem to have misplaced your #{file} file. I can haz?" unless File.exist?(file)
|
|
title = begin
|
|
File.read(file).match(%r!\A# (.*)$!)[1]
|
|
rescue NoMethodError
|
|
File.basename(file, ".*").downcase.capitalize
|
|
end
|
|
slug = File.basename(file, ".markdown").downcase
|
|
front_matter = {
|
|
"title" => title,
|
|
"permalink" => "/docs/#{slug}/",
|
|
"note" => "This file is autogenerated. Edit /#{file} instead.",
|
|
}.merge(overrides_front_matter)
|
|
contents = "#{front_matter.to_yaml}---\n\n#{content_for(file)}"
|
|
File.write("#{docs_folder}/_docs/#{slug}.md", contents)
|
|
end
|
|
|
|
def content_for(file)
|
|
contents = File.read(file)
|
|
case file
|
|
when "History.markdown"
|
|
converted_history(contents)
|
|
else
|
|
contents.gsub(%r!\A# .*\n\n?!, "")
|
|
end
|
|
end
|
|
|
|
#############################################################################
|
|
#
|
|
# Standard tasks
|
|
#
|
|
#############################################################################
|
|
|
|
multitask :default => [:test, :features]
|
|
|
|
task :spec => :test
|
|
require "rake/testtask"
|
|
Rake::TestTask.new(:test) do |test|
|
|
test.libs << "lib" << "test"
|
|
test.pattern = "test/**/test_*.rb"
|
|
test.verbose = true
|
|
end
|
|
|
|
require "rdoc/task"
|
|
Rake::RDocTask.new do |rdoc|
|
|
rdoc.rdoc_dir = "rdoc"
|
|
rdoc.title = "#{name} #{version}"
|
|
rdoc.rdoc_files.include("README*")
|
|
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
end
|
|
|
|
begin
|
|
require "cucumber/rake/task"
|
|
Cucumber::Rake::Task.new(:features) do |t|
|
|
t.profile = "travis"
|
|
end
|
|
Cucumber::Rake::Task.new(:"features:html", "Run Cucumber features and produce HTML output") do |t|
|
|
t.profile = "html_report"
|
|
end
|
|
rescue LoadError
|
|
desc "Cucumber rake task not available"
|
|
task :features do
|
|
abort "Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin"
|
|
end
|
|
end
|
|
|
|
desc "Open an irb session preloaded with this library"
|
|
task :console do
|
|
sh "irb -r ./lib/#{name}.rb"
|
|
end
|