@@ -57,7 +57,28 @@ export default class GitLab {
57
57
}
58
58
59
59
const updatedExistingLabels = labelsToRemove . length ? await this . getRepositoryLabels ( ) : existingLabels ; // Refresh labels after deletion, only if needed
60
- const existingLabelsNames = updatedExistingLabels . map ( label => label . name ) ;
60
+ const managedLabelsNames = this . MANAGED_LABELS . map ( label => label . name ) ;
61
+
62
+ // Remove managed labels that are no longer in the MANAGED_LABELS list
63
+ const obsoleteManagedLabels = updatedExistingLabels . filter ( label =>
64
+ label . description
65
+ && label . description . includes ( MANAGED_BY_OTA_MARKER )
66
+ && ! managedLabelsNames . includes ( label . name ) ) ;
67
+
68
+ if ( obsoleteManagedLabels . length ) {
69
+ logger . info ( `Removing obsolete managed labels: ${ obsoleteManagedLabels . map ( label => `"${ label . name } "` ) . join ( ', ' ) } ` ) ;
70
+
71
+ for ( const label of obsoleteManagedLabels ) {
72
+ await this . deleteLabel ( label . name ) ; /* eslint-disable-line no-await-in-loop */
73
+ }
74
+ }
75
+
76
+ // Refresh labels after obsolete removal
77
+ const finalExistingLabels = obsoleteManagedLabels . length ? await this . getRepositoryLabels ( ) : updatedExistingLabels ;
78
+ const existingLabelsNames = finalExistingLabels . map ( label => label . name ) ;
79
+ const existingLabelsMap = new Map ( finalExistingLabels . map ( label => [ label . name , label ] ) ) ;
80
+
81
+ // Find labels that need to be created
61
82
const missingLabels = this . MANAGED_LABELS . filter ( label => ! existingLabelsNames . includes ( label . name ) ) ;
62
83
63
84
if ( missingLabels . length ) {
@@ -71,6 +92,32 @@ export default class GitLab {
71
92
} ) ;
72
93
}
73
94
}
95
+
96
+ // Update existing labels if description or color changed
97
+ const labelsToUpdate = this . MANAGED_LABELS . filter ( label => {
98
+ const existingLabel = existingLabelsMap . get ( label . name ) ;
99
+
100
+ if ( ! existingLabel ) {
101
+ return false ;
102
+ }
103
+
104
+ const expectedDescription = `${ label . description } ${ MANAGED_BY_OTA_MARKER } ` ;
105
+ const expectedColor = `#${ label . color } ` ;
106
+
107
+ return existingLabel . description !== expectedDescription || existingLabel . color !== expectedColor ;
108
+ } ) ;
109
+
110
+ if ( labelsToUpdate . length ) {
111
+ logger . info ( `Updating labels with changed descriptions: ${ labelsToUpdate . map ( label => `"${ label . name } "` ) . join ( ', ' ) } ` ) ;
112
+
113
+ for ( const label of labelsToUpdate ) {
114
+ await this . updateLabel ( { /* eslint-disable-line no-await-in-loop */
115
+ name : label . name ,
116
+ color : `#${ label . color } ` ,
117
+ description : `${ label . description } ${ MANAGED_BY_OTA_MARKER } ` ,
118
+ } ) ;
119
+ }
120
+ }
74
121
} catch ( error ) {
75
122
logger . error ( `Failed to handle repository labels: ${ error . message } ` ) ;
76
123
}
@@ -155,6 +202,40 @@ export default class GitLab {
155
202
}
156
203
}
157
204
205
+ async updateLabel ( { name, color, description } ) {
206
+ try {
207
+ const label = {
208
+ name,
209
+ color,
210
+ description,
211
+ } ;
212
+
213
+ const options = GitLab . baseOptionsHttpReq ( ) ;
214
+
215
+ options . method = 'PUT' ;
216
+ options . body = JSON . stringify ( label ) ;
217
+ options . headers = {
218
+ 'Content-Type' : 'application/json' ,
219
+ ...options . headers ,
220
+ } ;
221
+
222
+ const response = await nodeFetch (
223
+ `${ this . apiBaseURL } /projects/${ this . projectId } /labels/${ encodeURIComponent ( name ) } ` ,
224
+ options ,
225
+ ) ;
226
+
227
+ const res = await response . json ( ) ;
228
+
229
+ if ( response . ok ) {
230
+ logger . info ( `Label updated: ${ res . name } , color: ${ res . color } ` ) ;
231
+ } else {
232
+ logger . error ( `updateLabel response: ${ JSON . stringify ( res ) } ` ) ;
233
+ }
234
+ } catch ( error ) {
235
+ logger . error ( `Failed to update label: ${ error } ` ) ;
236
+ }
237
+ }
238
+
158
239
async createIssue ( { title, description, labels } ) {
159
240
try {
160
241
const issue = {
0 commit comments