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
|
#!/usr/bin/env ruby
# frozen_string_literal: true
# We need to take some precautions when using the `gitlab` gem in this project.
#
# See https://docs.gitlab.com/ee/development/pipelines/internals.html#using-the-gitlab-ruby-gem-in-the-canonical-project.
require 'gitlab'
require 'optparse'
module Packages
class AutomatedCleanup
PACKAGES_PER_PAGE = 100
# $GITLAB_PROJECT_PACKAGES_CLEANUP_API_TOKEN => `Packages Cleanup` project token
def initialize(
project_path: ENV['CI_PROJECT_PATH'],
gitlab_token: ENV['GITLAB_PROJECT_PACKAGES_CLEANUP_API_TOKEN'],
api_endpoint: ENV['CI_API_V4_URL'],
options: {}
)
@project_path = project_path
@gitlab_token = gitlab_token
@api_endpoint = api_endpoint
@dry_run = options[:dry_run]
puts "Dry-run mode." if dry_run
end
def gitlab
@gitlab ||= begin
Gitlab.configure do |config|
config.endpoint = api_endpoint
config.private_token = gitlab_token
end
Gitlab
end
end
def perform_gitlab_package_cleanup!(package_name:, days_for_delete:)
puts "Checking for '#{package_name}' packages created at least #{days_for_delete} days ago..."
gitlab.project_packages(project_path,
package_type: 'generic',
package_name: package_name,
per_page: PACKAGES_PER_PAGE).auto_paginate do |package|
next unless package.name == package_name # the search is fuzzy, so we better check the actual package name
if old_enough(package, days_for_delete) && not_recently_downloaded(package, days_for_delete)
delete_package(package)
end
end
end
private
attr_reader :project_path, :gitlab_token, :api_endpoint, :dry_run
def delete_package(package)
print_package_state(package)
gitlab.delete_project_package(project_path, package.id) unless dry_run
rescue Gitlab::Error::Forbidden
puts "Package #{package_full_name(package)} is forbidden: skipping it"
end
def time_ago(days:)
Time.now - (days * 24 * 3600)
end
def old_enough(package, days_for_delete)
Time.parse(package.created_at) < time_ago(days: days_for_delete)
end
def not_recently_downloaded(package, days_for_delete)
package.last_downloaded_at.nil? ||
Time.parse(package.last_downloaded_at) < time_ago(days: days_for_delete)
end
def print_package_state(package)
download_text =
if package.last_downloaded_at
"last downloaded on #{package.last_downloaded_at}"
else
"never downloaded"
end
puts "\nPackage #{package_full_name(package)} (created on #{package.created_at}) was " \
"#{download_text}: deleting it.\n"
end
def package_full_name(package)
"'#{package.name}/#{package.version}'"
end
end
end
def timed(task)
start = Time.now
yield(self)
puts "#{task} finished in #{Time.now - start} seconds.\n"
end
if $PROGRAM_NAME == __FILE__
options = {
dry_run: false
}
OptionParser.new do |opts|
opts.on("-d", "--dry-run", "Whether to perform a dry-run or not.") do |value|
options[:dry_run] = true
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end.parse!
automated_cleanup = Packages::AutomatedCleanup.new(options: options)
timed('"gitlab-workhorse" packages cleanup') do
automated_cleanup.perform_gitlab_package_cleanup!(package_name: 'gitlab-workhorse', days_for_delete: 30)
end
timed('"assets" packages cleanup') do
automated_cleanup.perform_gitlab_package_cleanup!(package_name: 'assets', days_for_delete: 7)
end
timed('"fixtures" packages cleanup') do
automated_cleanup.perform_gitlab_package_cleanup!(package_name: 'fixtures', days_for_delete: 14)
end
end
|