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
|
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'optparse'
require 'gitlab'
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
|