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
134
135
136
137
138
139
140
141
142
|
# frozen_string_literal: true
module Projects
class UpdatePagesService < BaseService
include Gitlab::Utils::StrongMemoize
# old deployment can be cached by pages daemon
# so we need to give pages daemon some time update cache
# 10 minutes is enough, but 30 feels safer
OLD_DEPLOYMENTS_DESTRUCTION_DELAY = 30.minutes
attr_reader :build, :deployment_update
def initialize(project, build)
@project = project
@build = build
@deployment_update = ::Gitlab::Pages::DeploymentUpdate.new(project, build)
end
def execute
register_attempt
::Ci::Pipelines::AddJobService.new(@build.pipeline).execute!(commit_status) do |job|
job.enqueue!
job.run!
end
return error(deployment_update.errors.first.full_message) unless deployment_update.valid?
build.artifacts_file.use_file do |artifacts_path|
deployment = create_pages_deployment(artifacts_path, build)
break error('The uploaded artifact size does not match the expected value') unless deployment
break error(deployment_update.errors.first.full_message) unless deployment_update.valid?
deactive_old_deployments(deployment)
success
end
rescue StandardError => e
error(e.message)
raise e
end
private
def success
commit_status.success
publish_deployed_event
super
end
def error(message)
register_failure
log_error("Projects::UpdatePagesService: #{message}")
commit_status.allow_failure = !deployment_update.latest?
commit_status.description = message
commit_status.drop(:script_failure)
super
end
# Create status notifying the deployment of pages
def commit_status
GenericCommitStatus.new(
user: build.user,
ci_stage: stage,
name: 'pages:deploy',
stage: 'deploy',
stage_idx: stage.position
)
end
strong_memoize_attr :commit_status
# rubocop: disable Performance/ActiveRecordSubtransactionMethods
def stage
build.pipeline.stages.safe_find_or_create_by(name: 'deploy', pipeline_id: build.pipeline.id) do |stage|
stage.position = GenericCommitStatus::EXTERNAL_STAGE_IDX
stage.project = build.project
end
end
strong_memoize_attr :commit_status
# rubocop: enable Performance/ActiveRecordSubtransactionMethods
def create_pages_deployment(artifacts_path, build)
File.open(artifacts_path) do |file|
attributes = pages_deployment_attributes(file, build)
deployment = project.pages_deployments.build(**attributes)
break if deployment.file.size != file.size
deployment.tap(&:save!)
end
end
# overridden on EE
def pages_deployment_attributes(file, build)
{
file: file,
file_count: deployment_update.entries_count,
file_sha256: build.job_artifacts_archive.file_sha256,
ci_build_id: build.id,
root_directory: build.options[:publish],
upload_ready: false
}
end
def deactive_old_deployments(deployment)
PagesDeployment.deactivate_deployments_older_than(
deployment,
time: OLD_DEPLOYMENTS_DESTRUCTION_DELAY.from_now)
end
def register_attempt
pages_deployments_total_counter.increment
end
def register_failure
pages_deployments_failed_total_counter.increment
end
def pages_deployments_total_counter
Gitlab::Metrics.counter(:pages_deployments_total, "Counter of GitLab Pages deployments triggered")
end
strong_memoize_attr :pages_deployments_total_counter
def pages_deployments_failed_total_counter
Gitlab::Metrics.counter(:pages_deployments_failed_total, "Counter of GitLab Pages deployments which failed")
end
strong_memoize_attr :pages_deployments_failed_total_counter
def publish_deployed_event
event = ::Pages::PageDeployedEvent.new(data: {
project_id: project.id,
namespace_id: project.namespace_id,
root_namespace_id: project.root_namespace.id
})
Gitlab::EventStore.publish(event)
end
end
end
::Projects::UpdatePagesService.prepend_mod
|