Files
news.gitea.happylizard.me/bin/gems/bundler-4.0.21/lib/bundler/compact_index_client/cache.rb
T
pizza2d1 4cee170d66
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s
Add bin and edit workflow
2026-09-16 13:11:16 -06:00

108 lines
3.2 KiB
Ruby

# frozen_string_literal: true
require "rubygems/resolver/api_set/gem_parser"
module Bundler
class CompactIndexClient
class Cache
attr_reader :directory
def initialize(directory, fetcher = nil)
@directory = Pathname.new(directory).expand_path
@updater = Updater.new(fetcher) if fetcher
@mutex = Thread::Mutex.new
@endpoints = Set.new
@info_root = mkdir("info")
@special_characters_info_root = mkdir("info-special-characters")
@info_etag_root = mkdir("info-etags")
end
def names
fetch("names", names_path, names_etag_path)
end
def versions
fetch("versions", versions_path, versions_etag_path)
end
def info(name, remote_checksum = nil)
path = info_path(name)
if remote_checksum && remote_checksum != SharedHelpers.checksum_for_file(path, :MD5)
fetch("info/#{name}", path, info_etag_path(name))
else
Bundler::CompactIndexClient.debug { "update skipped info/#{name} (#{remote_checksum ? "versions index checksum is nil" : "versions index checksum matches local"})" }
read(path)
end
end
def reset!
@mutex.synchronize { @endpoints.clear }
end
private
def names_path = directory.join("names")
def names_etag_path = directory.join("names.etag")
def versions_path = directory.join("versions")
def versions_etag_path = directory.join("versions.etag")
def info_path(name)
name = name.to_s
validate_name!(name)
# TODO: converge this into the info_root by hashing all filenames like info_etag_path
if /[^a-z0-9_-]/.match?(name)
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
@special_characters_info_root.join(name)
else
@info_root.join(name)
end
end
def info_etag_path(name)
name = name.to_s
validate_name!(name)
@info_etag_root.join("#{name}-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}")
end
# Gem names come from the remote index and are not otherwise
# validated, so refuse anything that would escape the cache
# directory when used as a path component.
def validate_name!(name)
return if File.basename(name) == name
raise SecurityError, "malformed gem name: #{name.inspect}"
end
def mkdir(name)
directory.join(name).tap do |dir|
SharedHelpers.filesystem_access(dir) do
FileUtils.mkdir_p(dir)
end
end
end
def fetch(remote_path, path, etag_path)
if already_fetched?(remote_path)
Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
else
Bundler::CompactIndexClient.debug { "fetching #{remote_path}" }
@updater&.update(remote_path, path, etag_path)
end
read(path)
end
def already_fetched?(remote_path)
@mutex.synchronize { !@endpoints.add?(remote_path) }
end
def read(path)
return unless path.file?
SharedHelpers.filesystem_access(path, :read, &:read)
end
end
end
end