Skip to content

Instantly share code, notes, and snippets.

@Sija
Created October 12, 2023 23:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sija/0f0b7989355a9597bc0e5ebcd4bcde26 to your computer and use it in GitHub Desktop.
Save Sija/0f0b7989355a9597bc0e5ebcd4bcde26 to your computer and use it in GitHub Desktop.
Poor man's @dependabot-style updater for ameba
require "colorize"
require "log"
require "yaml"
# ---
target_version = "~> 1.5.0"
root_dir = "~/Code"
paths = %w[
any_hash.cr
backtracer
base62.cr
blurhash.cr
climate.cr
crystal-environment
debug.cr
git-rewrite-author
gphoto2.cr
gphoto2-web.cr
ipaddress.cr
jsonl.cr
ksuid.cr
money
raven.cr
retriable.cr
]
# ---
class Updater
GIT_COMMIT_MESSAGE = "Bump ameba version to %s"
GIT_BRANCH_NAME = "bump-ameba-version-to-%s"
getter path : Path
getter target_github_repo = "crystal-ameba/ameba"
getter target_version : String
getter? dry_run : Bool
def initialize(@path, @target_version, @dry_run = false)
@logger = Log.for(self.class)
end
def run : Nil
@logger.info { "Updating project: #{path.colorize.magenta}" }
shard_yml_path = path / "shard.yml"
unless File.exists?(shard_yml_path)
@logger.info { "Missing shard.yml, skipping" }
return
end
shard_yml = YAML.parse(File.read(shard_yml_path))
version = shard_yml
.dig("development_dependencies", "ameba", "version")
.as_s
if version == @target_version
@logger.info { "Already up to date" }
return
end
@logger.info { "Found version: #{version.colorize.magenta}" }
git_branch_name = GIT_BRANCH_NAME % @target_version.gsub(/[^0-9.]+/, "")
git_commit_message = GIT_COMMIT_MESSAGE % @target_version
git_commit(path, git_branch_name, git_commit_message) do
update_shard_yml(shard_yml_path)
run_cmd "shards install"
end
@logger.info { "Version updated" }
rescue ex
@logger.error(exception: ex) { "Error while updating" }
end
private def run_cmd(cmd)
cmd.each_line do |line|
@logger.info { "Running command: #{line.colorize.magenta}" }
# XXX: Using `Process.run` might be better here
`#{line}` unless dry_run?
end
end
end
private def git_commit(path, branch_name, message, &)
Dir.cd(path) do
run_cmd <<-SH
git stash
git checkout -b #{branch_name}
SH
begin
yield
run_cmd <<-SH
git commit --all --message "#{message}"
git push --set-upstream origin #{branch_name}
gh pr create --assignee "@me" --title "#{message}" --body ""
SH
ensure
run_cmd <<-SH
git checkout -
git branch --delete #{branch_name}
git stash pop
SH
end
end
end
private def update_shard_yml(shard_yml_path)
updated_file = process shard_yml_path,
"development_dependencies", "ameba"
if dry_run?
@logger.info { "Updated shard.yml:\n\n#{updated_file.colorize.magenta}" }
else
@logger.debug { "Updated shard.yml:\n\n#{updated_file.colorize.magenta}" }
File.write(shard_yml_path, updated_file)
end
end
private def process(filepath, l1_key, l2_key)
l1_key = "#{l1_key}:"
l2_key = " #{l2_key}:"
l1 = false
l2 = false
lines = File.read(filepath).lines.map! do |line|
l1 = false if l1 && !line.starts_with? " "
if line == l1_key
l1 = true
next line
end
l2 = false if l2 && !line.starts_with? " "
if l1 && line == l2_key
l2 = true
next line
end
next line unless l2
key, value =
line.split(':', 2).map!(&.strip)
sub = shard_yml_value(key)
next line unless sub
# XXX: bit nasty on the edges
line.sub("#{key}: #{value}", "#{key}: #{sub}")
end
lines.join('\n') + "\n"
end
private def shard_yml_value(key)
case key
when "git" then "https://github.com/#{@target_github_repo}.git"
when "github" then @target_github_repo
when "version" then @target_version
end
end
end
# ---
struct Formatter < Log::StaticFormatter
def run
@io << @entry.timestamp.to_rfc3339(fraction_digits: 6).colorize.dark_gray
string " "
@io << @entry.severity.label.rjust(6).colorize.green
string " "
if @entry.severity.error?
@io << @entry.message.colorize.red
else
@io << @entry.message
end
if ex = @entry.exception
@io << "\n\n" << ex.inspect_with_backtrace.colorize.red
end
end
end
Log.setup_from_env(
backend: Log::IOBackend.new(formatter: Formatter)
)
root_dir = Path[root_dir].expand(home: true)
paths.each do |path|
updater = Updater.new(root_dir / path, target_version, dry_run: true)
updater.run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment