Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • project/automatic_updates
  • issue/automatic_updates-3159719
  • issue/automatic_updates-3227923
  • issue/automatic_updates-3227927
  • issue/automatic_updates-3227122
  • issue/automatic_updates-3227940
  • issue/automatic_updates-3228095
  • issue/automatic_updates-3228101
  • issue/automatic_updates-3228115
  • issue/automatic_updates-3228125
  • issue/automatic_updates-3228359
  • issue/automatic_updates-3228392
  • issue/automatic_updates-3225753
  • issue/automatic_updates-3227588
  • issue/automatic_updates-3228521
  • issue/automatic_updates-3228539
  • issue/automatic_updates-3226570
  • issue/automatic_updates-3228806
  • issue/automatic_updates-3227575
  • issue/automatic_updates-3229485
  • issue/automatic_updates-3228955
  • issue/automatic_updates-3230024
  • issue/automatic_updates-3230250
  • issue/automatic_updates-3230249
  • issue/automatic_updates-3230264
  • issue/automatic_updates-3230510
  • issue/automatic_updates-3230235
  • issue/automatic_updates-3230050
  • issue/automatic_updates-3230934
  • issue/automatic_updates-3230049
  • issue/automatic_updates-3230507
  • issue/automatic_updates-3231153
  • issue/automatic_updates-3231992
  • issue/automatic_updates-3232003
  • issue/automatic_updates-3232004
  • issue/automatic_updates-3231996
  • issue/automatic_updates-3231999
  • issue/automatic_updates-3230668
  • issue/automatic_updates-3232000
  • issue/automatic_updates-3232420
  • issue/automatic_updates-3232729
  • issue/automatic_updates-3232761
  • issue/automatic_updates-3231997
  • issue/automatic_updates-3232927
  • issue/automatic_updates-3232959
  • issue/automatic_updates-3233124
  • issue/automatic_updates-3230856
  • issue/automatic_updates-3233493
  • issue/automatic_updates-3233587
  • issue/automatic_updates-3233521
  • issue/automatic_updates-3236405
  • issue/automatic_updates-3236299
  • issue/automatic_updates-3238586
  • issue/automatic_updates-3238647
  • issue/automatic_updates-3238717
  • issue/automatic_updates-3238846
  • issue/automatic_updates-3238852
  • issue/automatic_updates-3238866
  • issue/automatic_updates-3238714
  • issue/automatic_updates-3239103
  • issue/automatic_updates-3233138
  • issue/automatic_updates-3239466
  • issue/automatic_updates-3239645
  • issue/automatic_updates-3239673
  • issue/automatic_updates-3277775
  • issue/automatic_updates-3280923
  • issue/automatic_updates-3275315
  • issue/automatic_updates-3279527
  • issue/automatic_updates-3279733
  • issue/automatic_updates-3281405
  • issue/automatic_updates-3281397
  • issue/automatic_updates-3280204
  • issue/automatic_updates-3277817
  • issue/automatic_updates-3277230
  • issue/automatic_updates-3276534
  • issue/automatic_updates-3279538
  • issue/automatic_updates-3280168
  • issue/automatic_updates-3271502
  • issue/automatic_updates-3283644
  • issue/automatic_updates-3302524
  • issue/automatic_updates-3275883
  • issue/automatic_updates-3280180
  • issue/automatic_updates-3274837
  • issue/automatic_updates-3281473
  • issue/automatic_updates-3281634
  • issue/automatic_updates-3239889
  • issue/automatic_updates-3240753
  • issue/automatic_updates-3233103
  • issue/automatic_updates-3240971
  • issue/automatic_updates-3233564
  • issue/automatic_updates-3241380
  • issue/automatic_updates-3241105
  • issue/automatic_updates-3242626
  • issue/automatic_updates-3242724
  • issue/automatic_updates-3243057
  • issue/automatic_updates-3243348
  • issue/automatic_updates-3243339
  • issue/automatic_updates-3243422
  • issue/automatic_updates-3243436
  • issue/automatic_updates-3243600
  • issue/automatic_updates-3243851
  • issue/automatic_updates-3243405
  • issue/automatic_updates-3244338
  • issue/automatic_updates-3244337
  • issue/automatic_updates-3244360
  • issue/automatic_updates-3244358
  • issue/automatic_updates-3244679
  • issue/automatic_updates-3244939
  • issue/automatic_updates-3244976
  • issue/automatic_updates-3245376
  • issue/automatic_updates-3245388
  • issue/automatic_updates-3245428
  • issue/automatic_updates-3245655
  • issue/automatic_updates-3245766
  • issue/automatic_updates-3245810
  • issue/automatic_updates-3245846
  • issue/automatic_updates-3246036
  • issue/automatic_updates-3246194
  • issue/automatic_updates-3246203
  • issue/automatic_updates-3246283
  • issue/automatic_updates-3279064
  • issue/automatic_updates-3246420
  • issue/automatic_updates-3246638
  • issue/automatic_updates-3246660
  • issue/automatic_updates-3246673
  • issue/automatic_updates-3246695
  • issue/automatic_updates-3245996
  • issue/automatic_updates-3247308
  • issue/automatic_updates-3247478
  • issue/automatic_updates-3247479
  • issue/automatic_updates-3279086
  • issue/automatic_updates-3248312
  • issue/automatic_updates-3248523
  • issue/automatic_updates-3248527
  • issue/automatic_updates-3244412
  • issue/automatic_updates-3248976
  • issue/automatic_updates-3248909
  • issue/automatic_updates-3246507
  • issue/automatic_updates-3249531
  • issue/automatic_updates-3249517
  • issue/automatic_updates-3249983
  • issue/automatic_updates-3249135
  • issue/automatic_updates-3250136
  • issue/automatic_updates-3250386
  • issue/automatic_updates-3251701
  • issue/automatic_updates-3250696
  • issue/automatic_updates-3248911
  • issue/automatic_updates-3227420
  • issue/automatic_updates-3252071
  • issue/automatic_updates-3252097
  • issue/automatic_updates-3252109
  • issue/automatic_updates-3252126
  • issue/automatic_updates-3249130
  • issue/automatic_updates-3251972
  • issue/automatic_updates-3252187
  • issue/automatic_updates-3252328
  • issue/automatic_updates-3252324
  • issue/automatic_updates-3252533
  • issue/automatic_updates-3253055
  • issue/automatic_updates-3253186
  • issue/automatic_updates-3253395
  • issue/automatic_updates-3253400
  • issue/automatic_updates-3253624
  • issue/automatic_updates-3253647
  • issue/automatic_updates-3253649
  • issue/automatic_updates-3253858
  • issue/automatic_updates-3254166
  • issue/automatic_updates-3254207
  • issue/automatic_updates-3279108
  • issue/automatic_updates-3254606
  • issue/automatic_updates-3254616
  • issue/automatic_updates-3254911
  • issue/automatic_updates-3255011
  • issue/automatic_updates-3255014
  • issue/automatic_updates-3255320
  • issue/automatic_updates-3277562
  • issue/automatic_updates-3255678
  • issue/automatic_updates-3256958
  • issue/automatic_updates-3257115
  • issue/automatic_updates-3257432
  • issue/automatic_updates-3257446
  • issue/automatic_updates-3257473
  • issue/automatic_updates-3257134
  • issue/automatic_updates-3257849
  • issue/automatic_updates-3258065
  • issue/automatic_updates-3258056
  • issue/automatic_updates-3258048
  • issue/automatic_updates-3258045
  • issue/automatic_updates-3258464
  • issue/automatic_updates-3258611
  • issue/automatic_updates-3258646
  • issue/automatic_updates-3258661
  • issue/automatic_updates-3258667
  • issue/automatic_updates-3258590
  • issue/automatic_updates-3259228
  • issue/automatic_updates-3259656
  • issue/automatic_updates-3252299
  • issue/automatic_updates-3259664
  • issue/automatic_updates-3259822
  • issue/automatic_updates-3259810
  • issue/automatic_updates-3259814
  • issue/automatic_updates-3260368
  • issue/automatic_updates-3257845
  • issue/automatic_updates-3260664
  • issue/automatic_updates-3260672
  • issue/automatic_updates-3260698
  • issue/automatic_updates-3260662
  • issue/automatic_updates-3260669
  • issue/automatic_updates-3260668
  • issue/automatic_updates-3261637
  • issue/automatic_updates-3261772
  • issue/automatic_updates-3261847
  • issue/automatic_updates-3262016
  • issue/automatic_updates-3262244
  • issue/automatic_updates-3262303
  • issue/automatic_updates-3261642
  • issue/automatic_updates-3262359
  • issue/automatic_updates-3262542
  • issue/automatic_updates-3261098
  • issue/automatic_updates-3260666
  • issue/automatic_updates-3262587
  • issue/automatic_updates-3262965
  • issue/automatic_updates-3263155
  • issue/automatic_updates-3263223
  • issue/automatic_updates-3254755
  • issue/automatic_updates-3261758
  • issue/automatic_updates-3258059
  • issue/automatic_updates-3259196
  • issue/automatic_updates-3263865
  • issue/automatic_updates-3262284
  • issue/automatic_updates-3264554
  • issue/automatic_updates-3248928
  • issue/automatic_updates-3248929
  • issue/automatic_updates-3265072
  • issue/automatic_updates-3265057
  • issue/automatic_updates-3265873
  • issue/automatic_updates-3266092
  • issue/automatic_updates-3266633
  • issue/automatic_updates-3266640
  • issue/automatic_updates-3266687
  • issue/automatic_updates-3266981
  • issue/automatic_updates-3267387
  • issue/automatic_updates-3267389
  • issue/automatic_updates-3267411
  • issue/automatic_updates-3267603
  • issue/automatic_updates-3265874
  • issue/automatic_updates-3267577
  • issue/automatic_updates-3267386
  • issue/automatic_updates-3268363
  • issue/automatic_updates-3268868
  • issue/automatic_updates-3268612
  • issue/automatic_updates-3269097
  • issue/automatic_updates-3267632
  • issue/automatic_updates-3270736
  • issue/automatic_updates-3271144
  • issue/automatic_updates-3271226
  • issue/automatic_updates-3271078
  • issue/automatic_updates-3271371
  • issue/automatic_updates-3267646
  • issue/automatic_updates-3272060
  • issue/automatic_updates-3265031
  • issue/automatic_updates-3253591
  • issue/automatic_updates-3272520
  • issue/automatic_updates-3272061
  • issue/automatic_updates-3272785
  • issue/automatic_updates-3273011
  • issue/automatic_updates-3273008
  • issue/automatic_updates-3273364
  • issue/automatic_updates-3273407
  • issue/automatic_updates-3271235
  • issue/automatic_updates-3273006
  • issue/automatic_updates-3271240
  • issue/automatic_updates-3271468
  • issue/automatic_updates-3272326
  • issue/automatic_updates-3273693
  • issue/automatic_updates-3264849
  • issue/automatic_updates-3273807
  • issue/automatic_updates-3273017
  • issue/automatic_updates-3274269
  • issue/automatic_updates-3274273
  • issue/automatic_updates-3274292
  • issue/automatic_updates-3273812
  • issue/automatic_updates-3274323
  • issue/automatic_updates-3274633
  • issue/automatic_updates-3274858
  • issue/automatic_updates-3274830
  • issue/automatic_updates-3274892
  • issue/automatic_updates-3275075
  • issue/automatic_updates-3275256
  • issue/automatic_updates-3275282
  • issue/automatic_updates-3275320
  • issue/automatic_updates-3275323
  • issue/automatic_updates-3275324
  • issue/automatic_updates-3275317
  • issue/automatic_updates-3275313
  • issue/automatic_updates-3275357
  • issue/automatic_updates-3275474
  • issue/automatic_updates-3275311
  • issue/automatic_updates-3275546
  • issue/automatic_updates-3275508
  • issue/automatic_updates-3275810
  • issue/automatic_updates-3275860
  • issue/automatic_updates-3275865
  • issue/automatic_updates-3275886
  • issue/automatic_updates-3275887
  • issue/automatic_updates-3276031
  • issue/automatic_updates-3276041
  • issue/automatic_updates-3276072
  • issue/automatic_updates-3276159
  • issue/automatic_updates-3276255
  • issue/automatic_updates-3275825
  • issue/automatic_updates-3276661
  • issue/automatic_updates-3275369
  • issue/automatic_updates-3277014
  • issue/automatic_updates-3277035
  • issue/automatic_updates-3272813
  • issue/automatic_updates-3277211
  • issue/automatic_updates-3277229
  • issue/automatic_updates-3275316
  • issue/automatic_updates-3274047
  • issue/automatic_updates-3277815
  • issue/automatic_updates-3276662
  • issue/automatic_updates-3277235
  • issue/automatic_updates-3278411
  • issue/automatic_updates-3278435
  • issue/automatic_updates-3279229
  • issue/automatic_updates-3277007
  • issue/automatic_updates-3284346
  • issue/automatic_updates-3239759
  • issue/automatic_updates-3284495
  • issue/automatic_updates-3284530
  • issue/automatic_updates-3282677
  • issue/automatic_updates-3281379
  • issue/automatic_updates-3285408
  • issue/automatic_updates-3285631
  • issue/automatic_updates-3285669
  • issue/automatic_updates-3273014
  • issue/automatic_updates-3273369
  • issue/automatic_updates-3285491
  • issue/automatic_updates-3285898
  • issue/automatic_updates-3287251
  • issue/automatic_updates-3291147
  • issue/automatic_updates-3286650
  • issue/automatic_updates-3291770
  • issue/automatic_updates-3280403
  • issue/automatic_updates-3291959
  • issue/automatic_updates-3287398
  • issue/automatic_updates-3285145
  • issue/automatic_updates-3292027
  • issue/automatic_updates-3278445
  • issue/automatic_updates-3292933
  • issue/automatic_updates-3292958
  • issue/automatic_updates-3293157
  • issue/automatic_updates-3292956
  • issue/automatic_updates-3293146
  • issue/automatic_updates-3293381
  • issue/automatic_updates-3293449
  • issue/automatic_updates-3293427
  • issue/automatic_updates-3293422
  • issue/automatic_updates-3293685
  • issue/automatic_updates-3291730
  • issue/automatic_updates-3293866
  • issue/automatic_updates-3294338
  • issue/automatic_updates-3293656
  • issue/automatic_updates-3294600
  • issue/automatic_updates-3294335
  • issue/automatic_updates-3295013
  • issue/automatic_updates-3284936
  • issue/automatic_updates-3295596
  • issue/automatic_updates-3295758
  • issue/automatic_updates-3295791
  • issue/automatic_updates-3295830
  • issue/automatic_updates-3295874
  • issue/automatic_updates-3295897
  • issue/automatic_updates-3296074
  • issue/automatic_updates-3295965
  • issue/automatic_updates-3296181
  • issue/automatic_updates-3296065
  • issue/automatic_updates-3296178
  • issue/automatic_updates-3296261
  • issue/automatic_updates-3293148
  • issue/automatic_updates-3303953
  • issue/automatic_updates-3304367
  • issue/automatic_updates-3304640
  • issue/automatic_updates-3304836
  • issue/automatic_updates-3304142
  • issue/automatic_updates-3298349
  • issue/automatic_updates-3298444
  • issue/automatic_updates-3298510
  • issue/automatic_updates-3298431
  • issue/automatic_updates-3298863
  • issue/automatic_updates-3298877
  • issue/automatic_updates-3298904
  • issue/automatic_updates-3298951
  • issue/automatic_updates-3299101
  • issue/automatic_updates-3299417
  • issue/automatic_updates-3293417
  • issue/automatic_updates-3299612
  • issue/automatic_updates-3299093
  • issue/automatic_updates-3299087
  • issue/automatic_updates-3300006
  • issue/automatic_updates-3300036
  • issue/automatic_updates-3293150
  • issue/automatic_updates-3303807
  • issue/automatic_updates-3303929
  • issue/automatic_updates-3304417
  • issue/automatic_updates-3303727
  • issue/automatic_updates-3305527
  • issue/automatic_updates-3301844
  • issue/automatic_updates-3302527
  • issue/automatic_updates-3302673
  • issue/automatic_updates-3302897
  • issue/automatic_updates-3303143
  • issue/automatic_updates-3303168
  • issue/automatic_updates-3303174
  • issue/automatic_updates-3303185
  • issue/automatic_updates-3303200
  • issue/automatic_updates-3303113
  • issue/automatic_updates-3304036
  • issue/automatic_updates-3304165
  • issue/automatic_updates-3304583
  • issue/automatic_updates-3303902
  • issue/automatic_updates-3304651
  • issue/automatic_updates-3305312
  • issue/automatic_updates-3305612
  • issue/automatic_updates-3305568
  • issue/automatic_updates-3305773
  • issue/automatic_updates-3305874
  • issue/automatic_updates-3310901
  • issue/automatic_updates-3310929
  • issue/automatic_updates-3310936
  • issue/automatic_updates-3310972
  • issue/automatic_updates-3311001
  • issue/automatic_updates-3311020
  • issue/automatic_updates-3305240
  • issue/automatic_updates-3305994
  • issue/automatic_updates-3305564
  • issue/automatic_updates-3305998
  • issue/automatic_updates-3306600
  • issue/automatic_updates-3306631
  • issue/automatic_updates-3307086
  • issue/automatic_updates-3307103
  • issue/automatic_updates-3307168
  • issue/automatic_updates-3306163
  • issue/automatic_updates-3304617
  • issue/automatic_updates-3307163
  • issue/automatic_updates-3307398
  • issue/automatic_updates-3307478
  • issue/automatic_updates-3307369
  • issue/automatic_updates-3308404
  • issue/automatic_updates-3307611
  • issue/automatic_updates-3308372
  • issue/automatic_updates-3308686
  • issue/automatic_updates-3308886
  • issue/automatic_updates-3309220
  • issue/automatic_updates-3309270
  • issue/automatic_updates-3308711
  • issue/automatic_updates-3309486
  • issue/automatic_updates-3305167
  • issue/automatic_updates-3309676
  • issue/automatic_updates-3309025
  • issue/automatic_updates-3309891
  • issue/automatic_updates-3308365
  • issue/automatic_updates-3310000
  • issue/automatic_updates-3309205
  • issue/automatic_updates-3248975
  • issue/automatic_updates-3310702
  • issue/automatic_updates-3272313
  • issue/automatic_updates-3310990
  • issue/automatic_updates-3310997
  • issue/automatic_updates-3311200
  • issue/automatic_updates-3310696
  • issue/automatic_updates-3311265
  • issue/automatic_updates-3303167
  • issue/automatic_updates-3310946
  • issue/automatic_updates-3308828
  • issue/automatic_updates-3310666
  • issue/automatic_updates-3109082
  • issue/automatic_updates-3311534
  • issue/automatic_updates-3311436
  • issue/automatic_updates-3308843
  • issue/automatic_updates-3303900
  • issue/automatic_updates-3312085
  • issue/automatic_updates-3312420
  • issue/automatic_updates-3312373
  • issue/automatic_updates-3312421
  • issue/automatic_updates-3312619
  • issue/automatic_updates-3312779
  • issue/automatic_updates-3275991
  • issue/automatic_updates-3312938
  • issue/automatic_updates-3312937
  • issue/automatic_updates-3312960
  • issue/automatic_updates-3312669
  • issue/automatic_updates-3312981
  • issue/automatic_updates-3313319
  • issue/automatic_updates-3313346
  • issue/automatic_updates-3310914
  • issue/automatic_updates-3317220
  • issue/automatic_updates-3317232
  • issue/automatic_updates-3317278
  • issue/automatic_updates-3317409
  • issue/automatic_updates-3317267
  • issue/automatic_updates-3317599
  • issue/automatic_updates-3317988
  • issue/automatic_updates-3318313
  • issue/automatic_updates-3313630
  • issue/automatic_updates-3313717
  • issue/automatic_updates-3313947
  • issue/automatic_updates-3313507
  • issue/automatic_updates-3276645
  • issue/automatic_updates-3313349
  • issue/automatic_updates-3314137
  • issue/automatic_updates-3314143
  • issue/automatic_updates-3304365
  • issue/automatic_updates-3317796
  • issue/automatic_updates-3317996
  • issue/automatic_updates-3316484
  • issue/automatic_updates-3313414
  • issue/automatic_updates-3314764
  • issue/automatic_updates-3314771
  • issue/automatic_updates-3314787
  • issue/automatic_updates-3314805
  • issue/automatic_updates-3314734
  • issue/automatic_updates-3298889
  • issue/automatic_updates-3314803
  • issue/automatic_updates-3315139
  • issue/automatic_updates-3314946
  • issue/automatic_updates-3315449
  • issue/automatic_updates-3315798
  • issue/automatic_updates-3315834
  • issue/automatic_updates-3309602
  • issue/automatic_updates-3316115
  • issue/automatic_updates-3316131
  • issue/automatic_updates-3316293
  • issue/automatic_updates-3316318
  • issue/automatic_updates-3310729
  • issue/automatic_updates-3315700
  • issue/automatic_updates-3316668
  • issue/automatic_updates-3316721
  • issue/automatic_updates-3306283
  • issue/automatic_updates-3316611
  • issue/automatic_updates-3316895
  • issue/automatic_updates-3317385
  • issue/automatic_updates-3318625
  • issue/automatic_updates-3318770
  • issue/automatic_updates-3318846
  • issue/automatic_updates-3318927
  • issue/automatic_updates-3318933
  • issue/automatic_updates-3319044
  • issue/automatic_updates-3319045
  • issue/automatic_updates-3325716
  • issue/automatic_updates-3324421
  • issue/automatic_updates-3320486
  • issue/automatic_updates-3320558
  • issue/automatic_updates-3321206
  • issue/automatic_updates-3326934
  • issue/automatic_updates-3320755
  • issue/automatic_updates-3328765
  • issue/automatic_updates-3325654
  • issue/automatic_updates-3325869
  • issue/automatic_updates-3277034
  • issue/automatic_updates-3328516
  • issue/automatic_updates-3322546
  • issue/automatic_updates-3320487
  • issue/automatic_updates-3320792
  • issue/automatic_updates-3320815
  • issue/automatic_updates-3320638
  • issue/automatic_updates-3321256
  • issue/automatic_updates-3321684
  • issue/automatic_updates-3325522
  • issue/automatic_updates-3326334
  • issue/automatic_updates-3328234
  • issue/automatic_updates-3328740
  • issue/automatic_updates-3316932
  • issue/automatic_updates-3321386
  • issue/automatic_updates-3321933
  • issue/automatic_updates-3321282
  • issue/automatic_updates-3326801
  • issue/automatic_updates-3327753
  • issue/automatic_updates-3328742
  • issue/automatic_updates-3322313
  • issue/automatic_updates-3322203
  • issue/automatic_updates-3322150
  • issue/automatic_updates-3322404
  • issue/automatic_updates-3321236
  • issue/automatic_updates-3321994
  • issue/automatic_updates-3322589
  • issue/automatic_updates-3321904
  • issue/automatic_updates-3316855
  • issue/automatic_updates-3322931
  • issue/automatic_updates-3322913
  • issue/automatic_updates-3323037
  • issue/automatic_updates-3322918
  • issue/automatic_updates-3323211
  • issue/automatic_updates-3323565
  • issue/automatic_updates-3320824
  • issue/automatic_updates-3320836
  • issue/automatic_updates-3329002
  • issue/automatic_updates-3321971
  • issue/automatic_updates-3299094
  • issue/automatic_updates-3248544
  • issue/automatic_updates-3330139
  • issue/automatic_updates-3330365
  • issue/automatic_updates-3330712
  • issue/automatic_updates-3318065
  • issue/automatic_updates-3331310
  • issue/automatic_updates-3322917
  • issue/automatic_updates-3330140
  • issue/automatic_updates-3323003
  • issue/automatic_updates-3331355
  • issue/automatic_updates-3334054
  • issue/automatic_updates-3323706
  • issue/automatic_updates-3331168
  • issue/automatic_updates-3343430
  • issue/automatic_updates-3344562
  • issue/automatic_updates-3345039
  • issue/automatic_updates-3344039
  • issue/automatic_updates-3345313
  • issue/automatic_updates-3331471
  • issue/automatic_updates-3334994
  • issue/automatic_updates-3327229
  • issue/automatic_updates-3343889
  • issue/automatic_updates-3344124
  • issue/automatic_updates-3344689
  • issue/automatic_updates-3316368
  • issue/automatic_updates-3345767
  • issue/automatic_updates-3317815
  • issue/automatic_updates-3328600
  • issue/automatic_updates-3332256
  • issue/automatic_updates-3335766
  • issue/automatic_updates-3335802
  • issue/automatic_updates-3335908
  • issue/automatic_updates-3336243
  • issue/automatic_updates-3336247
  • issue/automatic_updates-3336255
  • issue/automatic_updates-3336259
  • issue/automatic_updates-3337062
  • issue/automatic_updates-3337068
  • issue/automatic_updates-3311229
  • issue/automatic_updates-3337697
  • issue/automatic_updates-3343768
  • issue/automatic_updates-3344127
  • issue/automatic_updates-3344595
  • issue/automatic_updates-3345763
  • issue/automatic_updates-3345765
  • issue/automatic_updates-3345771
  • issue/automatic_updates-3337760
  • issue/automatic_updates-3337049
  • issue/automatic_updates-3342430
  • issue/automatic_updates-3319507
  • issue/automatic_updates-3344583
  • issue/automatic_updates-3345764
  • issue/automatic_updates-3345768
  • issue/automatic_updates-3338667
  • issue/automatic_updates-3339016
  • issue/automatic_updates-3343827
  • issue/automatic_updates-3344556
  • issue/automatic_updates-3338666
  • issue/automatic_updates-3345649
  • issue/automatic_updates-3339657
  • issue/automatic_updates-3339719
  • issue/automatic_updates-3338789
  • issue/automatic_updates-3340022
  • issue/automatic_updates-3340284
  • issue/automatic_updates-3340638
  • issue/automatic_updates-3316617
  • issue/automatic_updates-3341224
  • issue/automatic_updates-3337953
  • issue/automatic_updates-3339714
  • issue/automatic_updates-3341708
  • issue/automatic_updates-3341841
  • issue/automatic_updates-3326486
  • issue/automatic_updates-3321474
  • issue/automatic_updates-3341974
  • issue/automatic_updates-3342120
  • issue/automatic_updates-3342227
  • issue/automatic_updates-3323461
  • issue/automatic_updates-3342137
  • issue/automatic_updates-3342460
  • issue/automatic_updates-3342364
  • issue/automatic_updates-3345028
  • issue/automatic_updates-3345549
  • issue/automatic_updates-3345881
  • issue/automatic_updates-3345762
  • issue/automatic_updates-3342726
  • issue/automatic_updates-3337667
  • issue/automatic_updates-3343463
  • issue/automatic_updates-3345766
  • issue/automatic_updates-3345754
  • issue/automatic_updates-3345633
  • issue/automatic_updates-3345772
  • issue/automatic_updates-3345769
  • issue/automatic_updates-3345761
  • issue/automatic_updates-3345755
  • issue/automatic_updates-3345760
  • issue/automatic_updates-3345757
  • issue/automatic_updates-3346520
  • issue/automatic_updates-3346545
  • issue/automatic_updates-3337054
  • issue/automatic_updates-3346628
  • issue/automatic_updates-3346717
  • issue/automatic_updates-3346594
  • issue/automatic_updates-3346659
  • issue/automatic_updates-3347031
  • issue/automatic_updates-3346547
  • issue/automatic_updates-3347164
  • issue/automatic_updates-3318306
  • issue/automatic_updates-3345646
  • issue/automatic_updates-3347165
  • issue/automatic_updates-3347959
  • issue/automatic_updates-3347267
  • issue/automatic_updates-3338346
  • issue/automatic_updates-3348122
  • issue/automatic_updates-3348129
  • issue/automatic_updates-3348162
  • issue/automatic_updates-3348276
  • issue/automatic_updates-3348441
  • issue/automatic_updates-3351604
  • issue/automatic_updates-3316843
  • issue/automatic_updates-3351908
  • issue/automatic_updates-3352731
  • issue/automatic_updates-3352898
  • issue/automatic_updates-3348866
  • issue/automatic_updates-3342817
  • issue/automatic_updates-3348159
  • issue/automatic_updates-3349142
  • issue/automatic_updates-3349966
  • issue/automatic_updates-3350909
  • issue/automatic_updates-3351594
  • issue/automatic_updates-3351883
  • issue/automatic_updates-3351925
  • issue/automatic_updates-3352198
  • issue/automatic_updates-3334552
  • issue/automatic_updates-3342790
  • issue/automatic_updates-3345222
  • issue/automatic_updates-3351212
  • issue/automatic_updates-3351093
  • issue/automatic_updates-3351247
  • issue/automatic_updates-3351962
  • issue/automatic_updates-3345641
  • issue/automatic_updates-3338940
  • issue/automatic_updates-3343721
  • issue/automatic_updates-3351069
  • issue/automatic_updates-3353219
  • issue/automatic_updates-3159920
  • issue/automatic_updates-3352355
  • issue/automatic_updates-3341469
  • issue/automatic_updates-3354099
  • issue/automatic_updates-3339659
  • issue/automatic_updates-3354312
  • issue/automatic_updates-3351895
  • issue/automatic_updates-3354249
  • issue/automatic_updates-3353270
  • issue/automatic_updates-3354003
  • issue/automatic_updates-3355074
  • issue/automatic_updates-3355094
  • issue/automatic_updates-3355105
  • issue/automatic_updates-3355162
  • issue/automatic_updates-3355446
  • issue/automatic_updates-3355045
  • issue/automatic_updates-3355558
  • issue/automatic_updates-3355553
  • issue/automatic_updates-3354594
  • issue/automatic_updates-3355609
  • issue/automatic_updates-3354325
  • issue/automatic_updates-3356395
  • issue/automatic_updates-3356638
  • issue/automatic_updates-3356640
  • issue/automatic_updates-3354701
  • issue/automatic_updates-3355463
  • issue/automatic_updates-3356804
  • issue/automatic_updates-3357721
  • issue/automatic_updates-3357578
  • issue/automatic_updates-3357657
  • issue/automatic_updates-3304108
  • issue/automatic_updates-3357941
  • issue/automatic_updates-3358012
  • issue/automatic_updates-3345535
  • issue/automatic_updates-3354827
  • issue/automatic_updates-3358570
  • issue/automatic_updates-3358878
  • issue/automatic_updates-3355628
  • issue/automatic_updates-3358504
  • issue/automatic_updates-3357969
  • issue/automatic_updates-3359609
  • issue/automatic_updates-3359727
  • issue/automatic_updates-3359825
  • issue/automatic_updates-3359820
  • issue/automatic_updates-3360548
  • issue/automatic_updates-3357480
  • issue/automatic_updates-3360656
  • issue/automatic_updates-3360763
  • issue/automatic_updates-3360655
  • issue/automatic_updates-3355618
  • issue/automatic_updates-3349351
  • issue/automatic_updates-3362110
  • issue/automatic_updates-3362143
  • issue/automatic_updates-3362589
  • issue/automatic_updates-3349004
  • issue/automatic_updates-3362507
  • issue/automatic_updates-3362746
  • issue/automatic_updates-3362978
  • issue/automatic_updates-3363259
  • issue/automatic_updates-3363725
  • issue/automatic_updates-3363926
  • issue/automatic_updates-3363937
  • issue/automatic_updates-3364514
  • issue/automatic_updates-3284443
  • issue/automatic_updates-3363938
  • issue/automatic_updates-3364731
  • issue/automatic_updates-3364748
  • issue/automatic_updates-3364735
  • issue/automatic_updates-3364725
  • issue/automatic_updates-3359670
  • issue/automatic_updates-3365133
  • issue/automatic_updates-3365151
  • issue/automatic_updates-3365177
  • issue/automatic_updates-3364958
  • issue/automatic_updates-3365390
  • issue/automatic_updates-3365414
  • issue/automatic_updates-3346644
  • issue/automatic_updates-3366267
  • issue/automatic_updates-3366289
  • issue/automatic_updates-3366499
  • issue/automatic_updates-3365437
  • issue/automatic_updates-3354914
  • issue/automatic_updates-3253828
  • issue/automatic_updates-3345484
  • issue/automatic_updates-3352846
  • issue/automatic_updates-3368741
  • issue/automatic_updates-3406008
  • issue/automatic_updates-3406122
  • issue/automatic_updates-3370197
  • issue/automatic_updates-3366271
  • issue/automatic_updates-3364135
  • issue/automatic_updates-3370603
  • issue/automatic_updates-3400146
  • issue/automatic_updates-3406010
  • issue/automatic_updates-3404429
  • issue/automatic_updates-3371212
  • issue/automatic_updates-3371076
  • issue/automatic_updates-3372673
  • issue/automatic_updates-3368808
  • issue/automatic_updates-3374175
  • issue/automatic_updates-3374739
  • issue/automatic_updates-3374753
  • issue/automatic_updates-3375679
  • issue/automatic_updates-3377237
  • issue/automatic_updates-3377245
  • issue/automatic_updates-3378774
  • issue/automatic_updates-3378793
  • issue/automatic_updates-3360485
  • issue/automatic_updates-3380698
  • issue/automatic_updates-3375940
  • issue/automatic_updates-3381294
  • issue/automatic_updates-3381484
  • issue/automatic_updates-3382942
  • issue/automatic_updates-3383462
  • issue/automatic_updates-3383451
  • issue/automatic_updates-3384359
  • issue/automatic_updates-3384637
  • issue/automatic_updates-3375640
  • issue/automatic_updates-3386135
  • issue/automatic_updates-3386533
  • issue/automatic_updates-3387610
  • issue/automatic_updates-3387637
  • issue/automatic_updates-3387656
  • issue/automatic_updates-3389157
  • issue/automatic_updates-3388965
  • issue/automatic_updates-3387379
  • issue/automatic_updates-3390493
  • issue/automatic_updates-3308235
  • issue/automatic_updates-3391715
  • issue/automatic_updates-3392246
  • issue/automatic_updates-3393633
  • issue/automatic_updates-3379344
  • issue/automatic_updates-3394413
  • issue/automatic_updates-3394705
  • issue/automatic_updates-3394936
  • issue/automatic_updates-3363497
  • issue/automatic_updates-3395782
  • issue/automatic_updates-3341406
  • issue/automatic_updates-3311472
  • issue/automatic_updates-3397228
  • issue/automatic_updates-3352654
  • issue/automatic_updates-3398782
  • issue/automatic_updates-3399147
  • issue/automatic_updates-3399155
  • issue/automatic_updates-3406812
  • issue/automatic_updates-3408483
  • issue/automatic_updates-3408488
  • issue/automatic_updates-3408560
  • issue/automatic_updates-3437633
  • issue/automatic_updates-3408835
  • issue/automatic_updates-3408937
  • issue/automatic_updates-3409491
  • issue/automatic_updates-3409519
  • issue/automatic_updates-3377458
  • issue/automatic_updates-3437951
  • issue/automatic_updates-3409774
  • issue/automatic_updates-3410047
  • issue/automatic_updates-3411110
  • issue/automatic_updates-3411241
  • issue/automatic_updates-3411276
  • issue/automatic_updates-3411392
  • issue/automatic_updates-3411240
  • issue/automatic_updates-3412630
  • issue/automatic_updates-3413866
  • issue/automatic_updates-3414168
  • issue/automatic_updates-3415291
  • issue/automatic_updates-3415389
  • issue/automatic_updates-3415403
  • issue/automatic_updates-3416768
  • issue/automatic_updates-3417905
  • issue/automatic_updates-3420188
  • issue/automatic_updates-3421641
  • issue/automatic_updates-3424290
  • issue/automatic_updates-3426229
  • issue/automatic_updates-3426666
  • issue/automatic_updates-3426716
  • issue/automatic_updates-3428199
  • issue/automatic_updates-3436741
  • issue/automatic_updates-3441923
  • issue/automatic_updates-3428651
  • issue/automatic_updates-3441799
  • issue/automatic_updates-3443249
  • issue/automatic_updates-3428922
  • issue/automatic_updates-3441817
  • issue/automatic_updates-3429248
  • issue/automatic_updates-3440646
  • issue/automatic_updates-3437877
  • issue/automatic_updates-3445149
  • issue/automatic_updates-3429268
  • issue/automatic_updates-3442153
  • issue/automatic_updates-3438156
  • issue/automatic_updates-3436993
  • issue/automatic_updates-3437704
  • issue/automatic_updates-3432496
  • issue/automatic_updates-3440006
  • issue/automatic_updates-3435893
  • issue/automatic_updates-3441577
  • issue/automatic_updates-3437023
  • issue/automatic_updates-3432391
  • issue/automatic_updates-3432447
  • issue/automatic_updates-3432472
  • issue/automatic_updates-3432489
  • issue/automatic_updates-3432476
  • issue/automatic_updates-3435918
  • issue/automatic_updates-3439067
  • issue/automatic_updates-3437409
  • issue/automatic_updates-3449093
  • issue/automatic_updates-3449480
  • issue/automatic_updates-3449519
  • issue/automatic_updates-3449631
  • issue/automatic_updates-3449636
  • issue/automatic_updates-3449689
  • issue/automatic_updates-3449693
  • issue/automatic_updates-3450884
  • issue/automatic_updates-3452870
  • issue/automatic_updates-3453030
  • issue/automatic_updates-3455161
  • issue/automatic_updates-3459557
  • issue/automatic_updates-3462138
  • issue/automatic_updates-3462168
  • issue/automatic_updates-3462883
  • issue/automatic_updates-3463662
  • issue/automatic_updates-3463813
  • issue/automatic_updates-3446371
  • issue/automatic_updates-3465155
  • issue/automatic_updates-3340355
  • issue/automatic_updates-3467749
  • issue/automatic_updates-3473410
  • issue/automatic_updates-3441926
  • issue/automatic_updates-3474603
  • issue/automatic_updates-3484802
  • issue/automatic_updates-3498586
  • issue/automatic_updates-3499481
  • issue/automatic_updates-3498018
  • issue/automatic_updates-3506632
  • issue/automatic_updates-3511959
  • issue/automatic_updates-3511943
981 results
Show changes
Showing
with 1870 additions and 0 deletions
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* A batch processor for updates.
*
* @internal
* This class is an internal part of the module's update handling and
* should not be used by external code.
*/
final class BatchProcessor {
/**
* The session key under which the stage ID is stored.
*
* @var string
*/
public const STAGE_ID_SESSION_KEY = '_automatic_updates_extensions_stage_id';
/**
* Gets the update stage service.
*
* @return \Drupal\automatic_updates_extensions\ExtensionUpdateStage
* The update stage service.
*/
private static function getStage(): ExtensionUpdateStage {
return \Drupal::service(ExtensionUpdateStage::class);
}
/**
* Records messages from a throwable, then re-throws it.
*
* @param \Throwable $error
* The caught exception.
* @param array $context
* The current context of the batch job.
*
* @throws \Throwable
* The caught exception, which will always be re-thrown once its messages
* have been recorded.
*/
private static function handleException(\Throwable $error, array &$context): void {
$context['results']['errors'][] = $error->getMessage();
throw $error;
}
/**
* Calls the update stage's begin() method.
*
* @param string[] $project_versions
* The project versions to be staged in the update, keyed by package name.
* @param array $context
* The current context of the batch job.
*
* @see \Drupal\automatic_updates_extensions\ExtensionUpdateStage::begin()
*/
public static function begin(array $project_versions, array &$context): void {
try {
$stage_id = static::getStage()->begin($project_versions);
\Drupal::service('session')->set(static::STAGE_ID_SESSION_KEY, $stage_id);
}
catch (\Throwable $e) {
static::handleException($e, $context);
}
}
/**
* Calls the update stage's stage() method.
*
* @param array $context
* The current context of the batch job.
*
* @see \Drupal\automatic_updates\UpdateStage::stage()
*/
public static function stage(array &$context): void {
$stage_id = \Drupal::service('session')->get(static::STAGE_ID_SESSION_KEY);
try {
static::getStage()->claim($stage_id)->stage();
}
catch (\Throwable $e) {
// If the stage was not already destroyed because of this exception
// destroy it.
if (!static::getStage()->isAvailable()) {
static::clean($stage_id, $context);
}
static::handleException($e, $context);
}
}
/**
* Calls the update stage's apply() method.
*
* @param string $stage_id
* The stage ID.
* @param array $context
* The current context of the batch job.
*
* @see \Drupal\automatic_updates_extensions\ExtensionUpdateStage::apply()
*/
public static function commit(string $stage_id, array &$context): void {
try {
static::getStage()->claim($stage_id)->apply();
// The batch system does not allow any single request to run for longer
// than a second, so this will force the next operation to be done in a
// new request. This helps keep the running code in as consistent a state
// as possible.
// @see \Drupal\package_manager\Stage::apply()
// @see \Drupal\package_manager\Stage::postApply()
// @todo See if there's a better way to ensure the post-apply tasks run
// in a new request in https://www.drupal.org/i/3293150.
sleep(1);
}
catch (\Throwable $e) {
static::handleException($e, $context);
}
}
/**
* Calls the update stage's postApply() method.
*
* @param string $stage_id
* The stage ID.
* @param array $context
* The current context of the batch job.
*
* @see \Drupal\automatic_updates\UpdateStage::postApply()
*/
public static function postApply(string $stage_id, array &$context): void {
try {
static::getStage()->claim($stage_id)->postApply();
}
catch (\Throwable $e) {
static::handleException($e, $context);
}
}
/**
* Calls the update stage's destroy() method.
*
* @param string $stage_id
* The stage ID.
* @param array $context
* The current context of the batch job.
*
* @see \Drupal\automatic_updates_extensions\ExtensionUpdateStage::destroy()
*/
public static function clean(string $stage_id, array &$context): void {
try {
static::getStage()->claim($stage_id)->destroy();
}
catch (\Throwable $e) {
static::handleException($e, $context);
}
}
/**
* Finishes the stage batch job.
*
* @param bool $success
* Indicate that the batch API tasks were all completed successfully.
* @param array $results
* An array of all the results.
* @param array $operations
* A list of the operations that had not been completed by the batch API.
*/
public static function finishStage(bool $success, array $results, array $operations): ?RedirectResponse {
if ($success) {
$stage_id = \Drupal::service('session')->get(static::STAGE_ID_SESSION_KEY);
$url = Url::fromRoute('automatic_updates_extension.confirmation_page', [
'stage_id' => $stage_id,
]);
return new RedirectResponse($url->setAbsolute()->toString());
}
static::handleBatchError($results);
return NULL;
}
/**
* Finishes the commit batch job.
*
* @param bool $success
* Indicate that the batch API tasks were all completed successfully.
* @param array $results
* An array of all the results.
* @param array $operations
* A list of the operations that had not been completed by the batch API.
*/
public static function finishCommit(bool $success, array $results, array $operations): ?RedirectResponse {
\Drupal::service('session')->remove(static::STAGE_ID_SESSION_KEY);
if ($success) {
$url = Url::fromRoute('automatic_updates.finish')
->setAbsolute()
->toString();
return new RedirectResponse($url);
}
static::handleBatchError($results);
return NULL;
}
/**
* Handles a batch job that finished with errors.
*
* @param array $results
* The batch results.
*/
private static function handleBatchError(array $results): void {
if (isset($results['errors'])) {
foreach ($results['errors'] as $error) {
\Drupal::messenger()->addError($error);
}
}
else {
\Drupal::messenger()->addError("Update error");
}
}
}
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\FailureMarker;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\StageBase;
use PhpTuf\ComposerStager\API\Core\BeginnerInterface;
use PhpTuf\ComposerStager\API\Core\CommitterInterface;
use PhpTuf\ComposerStager\API\Core\StagerInterface;
use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* Defines a service to perform updates for modules and themes.
*
* @internal
* This class is an internal part of the module's update handling and
* should not be used by external code.
*/
final class ExtensionUpdateStage extends StageBase {
/**
* {@inheritdoc}
*/
protected string $type = 'automatic_updates_extensions:attended';
/**
* Constructs a new ExtensionUpdateStage object.
*
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \PhpTuf\ComposerStager\API\Core\BeginnerInterface $beginner
* The beginner service.
* @param \PhpTuf\ComposerStager\API\Core\StagerInterface $stager
* The stager service.
* @param \PhpTuf\ComposerStager\API\Core\CommitterInterface $committer
* The committer service.
* @param \Drupal\Core\Queue\QueueFactory $queueFactory
* The queue factory.
* @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The event dispatcher service.
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempStoreFactory
* The shared tempstore factory.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface $pathFactory
* The path factory service.
* @param \Drupal\package_manager\FailureMarker $failureMarker
* The failure marker service.
*/
public function __construct(
protected readonly ComposerInspector $composerInspector,
PathLocator $pathLocator,
BeginnerInterface $beginner,
StagerInterface $stager,
CommitterInterface $committer,
QueueFactory $queueFactory,
EventDispatcherInterface $eventDispatcher,
SharedTempStoreFactory $tempStoreFactory,
TimeInterface $time,
PathFactoryInterface $pathFactory,
FailureMarker $failureMarker,
) {
parent::__construct($pathLocator, $beginner, $stager, $committer, $queueFactory, $eventDispatcher, $tempStoreFactory, $time, $pathFactory, $failureMarker);
}
/**
* Begins the update.
*
* @param string[] $project_versions
* The versions of the packages to update to, keyed by package name.
*
* @return string
* The unique ID of the stage.
*
* @throws \InvalidArgumentException
* Thrown if no project version is provided.
*/
public function begin(array $project_versions): string {
if (empty($project_versions)) {
throw new \InvalidArgumentException("No projects to begin the update");
}
$package_versions = [
'production' => [],
'dev' => [],
];
$project_root = $this->pathLocator->getProjectRoot();
$info = $this->composerInspector->getRootPackageInfo($project_root);
$installed_packages = $this->composerInspector->getInstalledPackagesList($project_root);
foreach ($project_versions as $project_name => $version) {
$package = $installed_packages->getPackageByDrupalProjectName($project_name);
if (empty($package)) {
throw new \InvalidArgumentException("The project $project_name is not a Drupal project known to Composer and cannot be updated.");
}
// We don't support updating install profiles.
if ($package->type === 'drupal-profile') {
throw new \InvalidArgumentException("The project $project_name cannot be updated because updating install profiles is not supported.");
}
$group = isset($info['devRequires'][$package->name]) ? 'dev' : 'production';
$package_versions[$group][$package->name] = LegacyVersionUtility::convertToSemanticVersion($version);
}
// Ensure that package versions are available to pre-create event
// subscribers. We can't use ::setMetadata() here because it requires the
// stage to be claimed, but that only happens during ::create().
$this->tempStore->set(static::TEMPSTORE_METADATA_KEY, [
'packages' => $package_versions,
]);
return $this->create();
}
/**
* Returns the package versions that will be required during the update.
*
* @return string[][]
* An array with two sub-arrays: 'production' and 'dev'. Each is a set of
* package versions, where the keys are package names and the values are
* version constraints understood by Composer.
*/
public function getPackageVersions(): array {
return $this->getMetadata('packages');
}
/**
* Stages the update.
*/
public function stage(): void {
$this->checkOwnership();
// Convert an associative array of package versions, keyed by name, to
// command-line arguments in the form `vendor/name:version`.
$map = function (array $versions): array {
$requirements = [];
foreach ($versions as $package => $version) {
$requirements[] = "$package:$version";
}
return $requirements;
};
$versions = array_map($map, $this->getPackageVersions());
$this->require($versions['production'], $versions['dev']);
}
/**
* {@inheritdoc}
*/
protected function getFailureMarkerMessage(): TranslatableMarkup {
return $this->t('Automatic updates failed to apply, and the site is in an indeterminate state. Consider restoring the code and database from a backup.');
}
}
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Form;
use Drupal\automatic_updates\Form\UpdateFormBase;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Exception\StageFailureMarkerException;
use Drupal\package_manager\InstalledPackage;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\ValidationResult;
use Drupal\automatic_updates_extensions\BatchProcessor;
use Drupal\automatic_updates\BatchProcessor as AutoUpdatesBatchProcessor;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\package_manager\Exception\StageException;
use Drupal\package_manager\Exception\StageOwnershipException;
use Drupal\system\SystemManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Defines a form to commit staged updates.
*
* @internal
* Form classes are internal and should not be used by external code.
*/
final class UpdateReady extends UpdateFormBase {
/**
* Constructs a new UpdateReady object.
*
* @param \Drupal\automatic_updates_extensions\ExtensionUpdateStage $stage
* The update stage service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Extension\ModuleExtensionList $moduleList
* The module list service.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
* Event dispatcher service.
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ExtensionUpdateStage $stage,
MessengerInterface $messenger,
private readonly StateInterface $state,
private readonly ModuleExtensionList $moduleList,
private readonly RendererInterface $renderer,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {
$this->setMessenger($messenger);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'automatic_updates_update_ready_form';
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get(ExtensionUpdateStage::class),
$container->get('messenger'),
$container->get('state'),
$container->get('extension.list.module'),
$container->get('renderer'),
$container->get('event_dispatcher'),
$container->get(ComposerInspector::class),
$container->get(PathLocator::class),
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, string $stage_id = NULL) {
try {
$this->stage->claim($stage_id);
}
catch (StageOwnershipException) {
$this->messenger()->addError($this->t('Cannot continue the update because another Composer operation is currently in progress.'));
return $form;
}
catch (StageFailureMarkerException $e) {
$this->messenger()->addError($e->getMessage());
return $form;
}
$messages = [];
// Don't set any messages if the form has been submitted, because we don't
// want them to be set during form submit.
if (!$form_state->getUserInput()) {
foreach ($messages as $type => $messages_of_type) {
foreach ($messages_of_type as $message) {
$this->messenger()->addMessage($message, $type);
}
}
}
$form['actions'] = [
'cancel' => [
'#type' => 'submit',
'#value' => $this->t('Cancel update'),
'#submit' => ['::cancel'],
],
'#type' => 'actions',
];
$form['stage_id'] = [
'#type' => 'value',
'#value' => $stage_id,
];
$form['package_updates'] = $this->showUpdates();
$form['backup'] = [
'#prefix' => '<strong>',
'#type' => 'checkbox',
'#title' => $this->t('Warning: Updating contributed modules or themes may leave your site inoperable or looking wrong.'),
'#description' => $this->t('Back up your database and site before you continue. <a href=":backup_url">Learn how</a>. Each contributed module or theme may follow different standards for backwards compatibility, may or may not have tests, and may add or remove features in any release. For these reasons, it is highly recommended that you test this update in a development environment first.', [':backup_url' => 'https://www.drupal.org/node/22281']),
'#required' => TRUE,
'#default_value' => FALSE,
'#suffix' => '</strong>',
];
$form['maintenance_mode'] = [
'#title' => $this->t('Perform updates with site in maintenance mode (strongly recommended)'),
'#type' => 'checkbox',
'#default_value' => TRUE,
];
// Don't run the status checks once the form has been submitted.
if (!$form_state->getUserInput()) {
$results = $this->runStatusCheck($this->stage, $this->eventDispatcher);
// This will have no effect if $results is empty.
$this->displayResults($results, $this->renderer);
// If any errors occurred, return the form early so the user cannot
// continue.
if (ValidationResult::getOverallSeverity($results) === SystemManager::REQUIREMENT_ERROR) {
return $form;
}
}
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Continue'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Store maintenance_mode setting so we can restore it when done.
$this->getRequest()
->getSession()
->set(AutoUpdatesBatchProcessor::MAINTENANCE_MODE_SESSION_KEY, $this->state->get('system.maintenance_mode'));
if ($form_state->getValue('maintenance_mode')) {
$this->state->set('system.maintenance_mode', TRUE);
}
$stage_id = $form_state->getValue('stage_id');
$batch = (new BatchBuilder())
->setTitle($this->t('Apply updates'))
->setInitMessage($this->t('Preparing to apply updates'))
->addOperation([BatchProcessor::class, 'commit'], [$stage_id])
->addOperation([BatchProcessor::class, 'postApply'], [$stage_id])
->addOperation([BatchProcessor::class, 'clean'], [$stage_id])
->setFinishCallback([BatchProcessor::class, 'finishCommit'])
->toArray();
batch_set($batch);
}
/**
* Cancels the in-progress update.
*/
public function cancel(array &$form, FormStateInterface $form_state): void {
try {
$this->stage->destroy();
$this->messenger()->addStatus($this->t('The update was successfully cancelled.'));
$form_state->setRedirect('automatic_updates_extensions.report_update');
}
catch (StageException $e) {
$this->messenger()->addError($e->getMessage());
}
}
/**
* Displays all projects that will be updated.
*
* @return mixed[][]
* A render array displaying packages that will be updated.
*/
private function showUpdates(): array {
// Get packages that were updated in the stage directory.
$installed_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$staged_packages = $this->composerInspector->getInstalledPackagesList($this->stage->getStageDirectory());
$updated_packages = $staged_packages->getPackagesWithDifferentVersionsIn($installed_packages);
// Build a list of package names that were updated by user request.
$updated_by_request = [];
foreach ($this->stage->getPackageVersions() as $group) {
$updated_by_request = array_merge($updated_by_request, array_keys($group));
}
$updated_by_request_info = [];
$updated_project_info = [];
$supported_package_types = ['drupal-module', 'drupal-theme'];
// Compile an array of relevant information about the packages that will be
// updated.
foreach ($updated_packages as $name => $updated_package) {
// Ignore anything that isn't a module or a theme.
if (!in_array($updated_package->type, $supported_package_types, TRUE)) {
continue;
}
$updated_project_info[$name] = [
'title' => $this->getProjectTitleFromPackage($updated_package),
'installed_version' => $installed_packages[$updated_package->name]->version,
'updated_version' => $updated_package->version,
];
}
foreach ($updated_packages as $name => $updated_package) {
// Sort the updated packages into two groups: the ones that were updated
// at the request of the user, and the ones that got updated anyway
// (probably due to Composer's dependency resolution).
if (in_array($name, $updated_by_request, TRUE)) {
$updated_by_request_info[$name] = $updated_project_info[$name];
unset($updated_project_info[$name]);
}
}
$output = [];
if ($updated_by_request_info) {
// Create the list of messages for the packages updated by request.
$output['requested'] = $this->getUpdatedPackagesItemList($updated_by_request_info, $this->t('The following projects will be updated:'));
}
if ($updated_project_info) {
// Create the list of messages for packages that were updated
// incidentally.
$output['dependencies'] = $this->getUpdatedPackagesItemList($updated_project_info, $this->t('The following dependencies will also be updated:'));
}
return $output;
}
/**
* Gets the human-readable project title for a Composer package.
*
* @param \Drupal\package_manager\InstalledPackage $package
* The installed package.
*
* @return string
* The human-readable title of the project. If no project information is
* available, the package name is returned.
*/
private function getProjectTitleFromPackage(InstalledPackage $package): string {
$project_name = $package->getProjectName();
if (!$project_name) {
return $package->name;
}
$project_info = new ProjectInfo($project_name);
$project_data = $project_info->getProjectInfo();
if ($project_data) {
return $project_data['title'];
}
else {
return $package->name;
}
}
/**
* Generates an item list of packages that will be updated.
*
* @param array[] $updated_packages
* An array of packages that will be updated, each sub-array containing the
* project title, installed version, and target version.
* @param \Drupal\Core\StringTranslation\TranslatableMarkup $item_list_title
* The title of the generated item list.
*
* @return array
* A render array for the generated item list.
*/
private function getUpdatedPackagesItemList(array $updated_packages, TranslatableMarkup $item_list_title): array {
$create_message_for_project = function (array $project): TranslatableMarkup {
return $this->t('@title from @from_version to @to_version', [
'@title' => $project['title'],
'@from_version' => $project['installed_version'],
'@to_version' => $project['updated_version'],
]);
};
return [
'#theme' => 'item_list',
'#prefix' => '<p>' . $item_list_title . '</p>',
'#items' => array_map($create_message_for_project, $updated_packages),
];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Form;
use Drupal\automatic_updates\Form\UpdateFormBase;
use Drupal\automatic_updates_extensions\BatchProcessor;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Exception\StageFailureMarkerException;
use Drupal\package_manager\FailureMarker;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\ValidationResult;
use Drupal\system\SystemManager;
use Drupal\update\ProjectRelease;
use Drupal\update\UpdateManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* A form for selecting extension updates.
*
* @internal
* Form classes are internal and should not be used by external code.
*/
final class UpdaterForm extends UpdateFormBase {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get(ExtensionUpdateStage::class),
$container->get('event_dispatcher'),
$container->get('renderer'),
$container->get('state'),
$container->get(FailureMarker::class),
$container->get(ComposerInspector::class),
$container->get(PathLocator::class),
);
}
/**
* Constructs a new UpdaterForm object.
*
* @param \Drupal\automatic_updates_extensions\ExtensionUpdateStage $stage
* The extension update stage service.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The extension event dispatcher service.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\package_manager\FailureMarker $failureMarker
* The failure marker service.
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ExtensionUpdateStage $stage,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly RendererInterface $renderer,
private readonly StateInterface $state,
private readonly FailureMarker $failureMarker,
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'automatic_updates_extensions_updater_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
try {
$this->failureMarker->assertNotExists();
}
catch (StageFailureMarkerException $e) {
$this->messenger()->addError($e->getMessage());
return $form;
}
$update_projects = $this->getRecommendedModuleUpdates();
$options = [];
$recommended_versions = [];
foreach ($update_projects as $project_name => $update_project) {
switch ($update_project['status']) {
case UpdateManagerInterface::NOT_SECURE:
case UpdateManagerInterface::REVOKED:
$status_message = $this->t('(Security update)');
break;
case UpdateManagerInterface::NOT_SUPPORTED:
$status_message = $this->t('(Unsupported)');
break;
default:
$status_message = '';
}
$project_release = ProjectRelease::createFromArray($update_project['releases'][$update_project['recommended']]);
$options[$project_name] = [
$update_project['title'] . $status_message,
$update_project['existing_version'],
$this->t(
'@version (<a href=":url">Release notes</a>)',
[
'@version' => $project_release->getVersion(),
':url' => $project_release->getReleaseUrl(),
]),
];
$recommended_versions[$project_name] = $update_project['recommended'];
}
$form['recommended_versions'] = [
'#type' => 'value',
'#value' => $recommended_versions,
];
$form['projects'] = [
'#type' => 'tableselect',
'#header' => [
$this->t('Project:'),
$this->t('Current Version:'),
$this->t('Update Version:'),
],
'#options' => $options,
'#empty' => $this->t('There are no available updates.'),
'#attributes' => ['class' => ['update-recommended']],
'#required' => TRUE,
'#required_error' => t('Select one or more projects.'),
];
if ($form_state->getUserInput()) {
$results = [];
}
else {
$results = $this->runStatusCheck($this->stage, $this->eventDispatcher);
}
$this->displayResults($results, $this->renderer);
$security_level = ValidationResult::getOverallSeverity($results);
if ($update_projects && $security_level !== SystemManager::REQUIREMENT_ERROR) {
$form['actions'] = $this->actions($form_state);
}
$form['last_check'] = [
'#theme' => 'update_last_check',
'#last' => $this->state->get('update.last_check', 0),
];
return $form;
}
/**
* Builds the form actions.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return mixed[][]
* The form's actions elements.
*/
private function actions(FormStateInterface $form_state): array {
$actions = ['#type' => 'actions'];
if (!$this->stage->isAvailable()) {
// If the form has been submitted do not display this error message
// because ::deleteExistingUpdate() may run on submit. The message will
// still be displayed on form build if needed.
if (!$form_state->getUserInput()) {
$this->messenger()->addError($this->t('Cannot begin an update because another Composer operation is currently in progress.'));
}
$actions['delete'] = [
'#type' => 'submit',
'#value' => $this->t('Delete existing update'),
'#submit' => ['::deleteExistingUpdate'],
];
}
else {
$actions['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Update'),
];
}
return $actions;
}
/**
* Submit function to delete an existing in-progress update.
*/
public function deleteExistingUpdate(): void {
$this->stage->destroy(TRUE);
$this->messenger()->addMessage($this->t("Staged update deleted"));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$projects = $form_state->getValue('projects');
$selected_projects = array_filter($projects);
$recommended_versions = $form_state->getValue('recommended_versions');
$selected_versions = array_intersect_key($recommended_versions, $selected_projects);
$batch = (new BatchBuilder())
->setTitle($this->t('Downloading updates'))
->setInitMessage($this->t('Preparing to download updates'))
->addOperation(
[BatchProcessor::class, 'begin'],
[$selected_versions]
)
->addOperation([BatchProcessor::class, 'stage'])
->setFinishCallback([BatchProcessor::class, 'finishStage'])
->toArray();
batch_set($batch);
}
/**
* Gets the modules that require updates.
*
* @return mixed[]
* Modules that require updates.
*/
private function getRecommendedModuleUpdates(): array {
$supported_project_types = [
"module", "module-disabled", "theme", "theme-disabled",
];
$available_updates = update_get_available(TRUE);
if (empty($available_updates)) {
$this->messenger()->addError('There was a problem getting update information. Try again later.');
return [];
}
$all_projects_data = update_calculate_project_data($available_updates);
$outdated_modules = [];
$installed_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$non_supported_update_statuses = [];
foreach ($all_projects_data as $project_name => $project_data) {
if (in_array($project_data['project_type'], $supported_project_types, TRUE)) {
if ($project_data['status'] !== UpdateManagerInterface::CURRENT) {
if ($installed_packages->getPackageByDrupalProjectName($project_name) === NULL) {
$non_supported_update_statuses[] = $project_data['status'];
continue;
}
$project_information = new ProjectInfo($project_name);
$installable_versions = array_keys($project_information->getInstallableReleases());
if (!empty($project_data['recommended']) && in_array($project_data['recommended'], $installable_versions, TRUE)) {
$outdated_modules[$project_name] = $project_data;
}
else {
$non_supported_update_statuses[] = $project_data['status'];
}
}
}
}
if ($non_supported_update_statuses) {
$message_status = array_intersect([UpdateManagerInterface::NOT_SECURE, UpdateManagerInterface::NOT_SUPPORTED, UpdateManagerInterface::REVOKED], $non_supported_update_statuses) ?
MessengerInterface::TYPE_ERROR :
MessengerInterface::TYPE_STATUS;
if ($outdated_modules) {
$this->messenger()->addMessage(
$this->t(
'Other updates were found, but they must be performed manually. See <a href=":url">the list of available updates</a> for more information.',
[
':url' => Url::fromRoute('update.status')->toString(),
]
),
$message_status
);
}
else {
$this->messenger()->addMessage(
$this->t(
'Updates were found, but they must be performed manually. See <a href=":url">the list of available updates</a> for more information.',
[
':url' => Url::fromRoute('update.status')->toString(),
]
),
$message_status
);
}
}
return $outdated_modules;
}
}
<?php
declare(strict_types=1);
namespace Drupal\automatic_updates_extensions\Validator;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\InstalledPackagesList;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that no changes were made to Drupal Core packages.
*
* @internal
* This is an internal part of Automatic Updates and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class ForbidCoreChangesValidator implements EventSubscriberInterface {
use StringTranslationTrait;
public function __construct(
private readonly PathLocator $pathLocator,
private readonly ComposerInspector $composerInspector,
) {}
/**
* Validates the staged packages.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent|\Drupal\package_manager\Event\PreApplyEvent $event
* The event object.
*/
public function validateStagedCorePackages(StatusCheckEvent|PreApplyEvent $event): void {
$stage = $event->stage;
// We only want to do this check if the stage belongs to Automatic Updates
// Extensions.
if ($stage->getType() !== 'automatic_updates_extensions:attended' || !$stage->stageDirectoryExists()) {
return;
}
$active_core_packages = $this->getInstalledCorePackages($this->pathLocator->getProjectRoot());
$stage_core_packages = $this->getInstalledCorePackages($stage->getStageDirectory());
$new_packages = $stage_core_packages->getPackagesNotIn($active_core_packages);
$removed_packages = $active_core_packages->getPackagesNotIn($stage_core_packages);
$changed_packages = $active_core_packages->getPackagesWithDifferentVersionsIn($stage_core_packages);
$error_messages = [];
foreach ($new_packages as $new_package) {
$error_messages[] = $this->t("'@name' installed.", ['@name' => $new_package->name]);
}
foreach ($removed_packages as $removed_package) {
$error_messages[] = $this->t("'@name' removed.", ['@name' => $removed_package->name]);
}
foreach ($changed_packages as $name => $updated_package) {
$error_messages[] = $this->t(
"'@name' version changed from @active_version to @staged_version.",
[
'@name' => $updated_package->name,
'@staged_version' => $stage_core_packages[$name]->version,
'@active_version' => $updated_package->version,
]
);
}
if ($error_messages) {
$event->addError($error_messages, $this->t(
'Updating Drupal Core while updating extensions is currently not supported. Use <a href=":url">this form</a> to update Drupal core. The following changes were made to the Drupal core packages:',
[':url' => Url::fromRoute('update.report_update')->toString()]
));
}
}
/**
* Gets all the installed core packages for a given project root.
*
* This method differs from
* \Drupal\package_manager\ComposerInspector::getInstalledPackagesList in that
* it ensures that the 'drupal/core' is included in the list if present.
*
* @param string $composer_root
* The path to the composer root.
*
* @return \Drupal\package_manager\InstalledPackagesList
* The installed core packages.
*/
private function getInstalledCorePackages(string $composer_root): InstalledPackagesList {
$installed_package_list = $this->composerInspector->getInstalledPackagesList($composer_root);
$core_packages = $installed_package_list->getCorePackages();
if (isset($installed_package_list['drupal/core']) && !isset($core_packages['drupal/core'])) {
$core_packages = new InstalledPackagesList(array_merge($core_packages->getArrayCopy(), ['drupal/core' => $installed_package_list['drupal/core']]));
}
return $core_packages;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[StatusCheckEvent::class][] = ['validateStagedCorePackages'];
$events[PreApplyEvent::class][] = ['validateStagedCorePackages'];
return $events;
}
}
<?php
declare(strict_types=1);
namespace Drupal\automatic_updates_extensions\Validator;
use Composer\Semver\Semver;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that requested packages have been updated.
*
* @internal
* This is an internal part of Automatic Updates and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class RequestedUpdateValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a RequestedUpdateValidator object.
*
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {}
/**
* Validates that requested packages have been updated to the right version.
*
* @param \Drupal\package_manager\Event\PreApplyEvent|\Drupal\package_manager\Event\StatusCheckEvent $event
* The pre-apply event.
*/
public function checkRequestedStagedVersion(PreApplyEvent|StatusCheckEvent $event): void {
$stage = $event->stage;
if ($stage->getType() !== 'automatic_updates_extensions:attended' || !$stage->stageDirectoryExists()) {
return;
}
$requested_package_versions = $stage->getPackageVersions();
$active = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$staged = $this->composerInspector->getInstalledPackagesList($event->stage->getStageDirectory());
$changed_stage_packages = $staged->getPackagesWithDifferentVersionsIn($active)->getArrayCopy();
if (empty($changed_stage_packages)) {
$event->addError([$this->t('No updates detected in the staging area.')]);
return;
}
// Check for all changed the packages if they are updated to the requested
// version.
foreach (['production', 'dev'] as $package_type) {
foreach ($requested_package_versions[$package_type] as $requested_package_name => $requested_version) {
if (array_key_exists($requested_package_name, $changed_stage_packages)) {
$staged_version = $changed_stage_packages[$requested_package_name]->version;
if (!Semver::satisfies($staged_version, $requested_version)) {
$event->addError([
$this->t(
"The requested update to '@requested_package_name' to version '@requested_version' does not match the actual staged update to '@staged_version'.",
[
'@requested_package_name' => $requested_package_name,
'@requested_version' => $requested_version,
'@staged_version' => $staged_version,
]
),
]);
}
}
else {
$event->addError([
$this->t(
"The requested update to '@requested_package_name' to version '@requested_version' was not performed.",
[
'@requested_package_name' => $requested_package_name,
'@requested_version' => $requested_version,
]
),
]);
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[StatusCheckEvent::class][] = ['checkRequestedStagedVersion'];
$events[PreApplyEvent::class][] = ['checkRequestedStagedVersion'];
return $events;
}
}
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Validator;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PreCreateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that updated projects are secure and supported.
*
* @internal
* This class is an internal part of the module's update handling and
* should not be used by external code.
*
* @todo Decide if this validator can be removed completely in
* https://www.drupal.org/i/3351091.
*/
final class UpdateReleaseValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs an UpdateReleaseValidator object.
*
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator
) {}
/**
* Checks if the given version of a project is supported.
*
* Checks if the given version of the given project is in the core update
* system's list of known, secure, installable releases of that project.
* considered a supported release by verifying if the project is found in the
* core update system's list of known, secure, and installable releases.
*
* @param string $name
* The name of the project.
* @param string $semantic_version
* A semantic version number for the project.
*
* @return bool
* TRUE if the given version of the project is supported, otherwise FALSE.
* given version is not supported will return FALSE.
*/
private function isSupportedRelease(string $name, string $semantic_version): bool {
$supported_releases = (new ProjectInfo($name))->getInstallableReleases();
if (!$supported_releases) {
return FALSE;
}
// If this version is found in the list of installable releases, it is
// secured and supported.
if (array_key_exists($semantic_version, $supported_releases)) {
return TRUE;
}
// If the semantic version number wasn't in the list of
// installable releases, convert it to a legacy version number and see
// if the version number is in the list.
$legacy_version = LegacyVersionUtility::convertToLegacyVersion($semantic_version);
if ($legacy_version && array_key_exists($legacy_version, $supported_releases)) {
return TRUE;
}
// Neither the semantic version nor the legacy version are in the list
// of installable releases, so the release isn't supported.
return FALSE;
}
/**
* Checks that the update projects are secure and supported.
*
* @param \Drupal\package_manager\Event\PreCreateEvent $event
* The event object.
*/
public function checkRelease(PreCreateEvent $event): void {
$stage = $event->stage;
// This check only works with Automatic Updates Extensions.
if ($stage->getType() !== 'automatic_updates_extensions:attended') {
return;
}
$active_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$all_versions = $stage->getPackageVersions();
$messages = [];
foreach (['production', 'dev'] as $package_type) {
foreach ($all_versions[$package_type] as $package_name => $sematic_version) {
$project_name = $active_packages[$package_name]->getProjectName();
// If the version isn't in the list of installable releases, then it
// isn't secure and supported and the user should receive an error.
if (!$this->isSupportedRelease($project_name, $sematic_version)) {
$messages[] = $this->t('Project @project_name to version @version', [
'@project_name' => $project_name,
'@version' => $sematic_version,
]);
}
}
}
if ($messages) {
$summary = $this->formatPlural(
count($messages),
'Cannot update because the following project version is not in the list of installable releases.',
'Cannot update because the following project versions are not in the list of installable releases.'
);
$event->addError($messages, $summary);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
PreCreateEvent::class => 'checkRelease',
];
}
}
{
"name": "drupal/new_module",
"type": "drupal-module",
"version": "1.0.0"
}
name: 'New module'
type: module
core_version_requirement: ^9.7 || ^10
project: new_module
{
"name": "drupal/new_module",
"type": "drupal-module",
"version": "1.1.0"
}
name: 'New module'
type: module
core_version_requirement: ^9.7 || ^10
project: new_module
<?xml version="1.0" encoding="utf-8"?>
<!--
This fixture is used by \Drupal\Tests\automatic_updates_extensions\Build\ModuleUpdateTest.
Contains metadata about the following (fake) releases of alpha module, all of which are secure, in order:
- 1.1.0
- 1.0.0
-->
<project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Alpha</title>
<short_name>alpha</short_name>
<dc:creator>Drupal</dc:creator>
<supported_branches>1.1.,1.0.</supported_branches>
<project_status>published</project_status>
<link>http://example.com/project/alpha</link>
<terms>
<term><name>Projects</name><value>Alpha project</value></term>
</terms>
<releases>
<release>
<name>Alpha 1.1.0</name>
<version>1.1.0</version>
<tag>1.1.0</tag>
<status>published</status>
<release_link>http://example.com/alpha-1-1-0-release</release_link>
<download_link>http://example.com/alpha-1-1-0.tar.gz</download_link>
<date>1584195300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Alpha 1.0.0</name>
<version>1.0.0</version>
<tag>1.0.0</tag>
<status>published</status>
<release_link>http://example.com/alpha-1-0-0-release</release_link>
<download_link>http://example.com/alpha-1-0-0.tar.gz</download_link>
<date>1581603300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
</releases>
</project>
<?xml version="1.0" encoding="utf-8"?>
<!--
This fixture is used by \Drupal\Tests\automatic_updates_extensions\Functional\UpdaterFormTest.
Contains metadata about the following (fake) releases of automatic_updates_extensions_test_theme, all of which are secure, in order:
- 3.0.0
- 8.x-2.1
- 8.x-2.0
-->
<project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Automatic Updates Extensions Test Theme</title>
<short_name>automatic_updates_extensions_test_theme</short_name>
<dc:creator>drupal</dc:creator>
<type>project_theme</type>
<supported_branches>8.x-2.,3.0.</supported_branches>
<project_status>published</project_status>
<link>http://example.com/project/automatic_updates_extensions_test_theme</link>
<terms>
<term>
<name>Projects</name>
<value>Themes</value>
</term>
<term>
<name>Maintenance status</name>
<value>Minimally maintained</value>
</term>
<term>
<name>Development status</name>
<value>Under active development</value>
</term>
</terms>
<releases>
<release>
<name>automatic_updates_extensions_test_theme 3.0.0</name>
<version>3.0.0</version>
<tag>3.0.0</tag>
<status>published</status>
<release_link>http://example.com/automatic_updates_extensions_test_theme-3-0-0-release</release_link>
<download_link>http://example.com/automatic_updates_extensions_test_theme-3.0.0.tar.gz</download_link>
<date>1597793786</date>
<files>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-3.0.0.tar.gz</url>
<archive_type>tar.gz</archive_type>
<md5>43956be126b27d718ecffd0b17244aa1</md5>
<size>196080</size>
<filedate>1597793786</filedate>
</file>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-3.0.0.zip</url>
<archive_type>zip</archive_type>
<md5>5d24ec88ac4755af6ccbe6ef5ff8b3c9</md5>
<size>200399</size>
<filedate>1597793786</filedate>
</file>
</files>
<security covered="1">Covered by Drupal's security advisory policy</security>
<core_compatibility>^8 || ^9</core_compatibility>
</release>
<release>
<name>automatic_updates_extensions_test_theme 8.x-2.1</name>
<version>8.x-2.1</version>
<tag>8.x-2.1</tag>
<status>published</status>
<release_link>http://example.com/automatic_updates_extensions_test_theme-8-x-2-1-release</release_link>
<download_link>http://example.com/automatic_updates_extensions_test_theme-8.x-2.1.tar.gz</download_link>
<date>1597793786</date>
<files>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-8.x-2.1.tar.gz</url>
<archive_type>tar.gz</archive_type>
<md5>43956be126b27d718ecffd0b17244aa1</md5>
<size>196080</size>
<filedate>1597793786</filedate>
</file>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-8.x-2.1.zip</url>
<archive_type>zip</archive_type>
<md5>5d24ec88ac4755af6ccbe6ef5ff8b3c9</md5>
<size>200399</size>
<filedate>1597793786</filedate>
</file>
</files>
<terms>
<term>
<name>Release type</name>
<value>Bug fixes</value>
</term>
</terms>
<security covered="1">Covered by Drupal's security advisory policy</security>
<core_compatibility>8.x</core_compatibility>
</release>
<release>
<name>automatic_updates_extensions_test_theme 8.x-2.0</name>
<version>8.x-2.0</version>
<tag>8.x-2.0</tag>
<status>published</status>
<release_link>http://example.com/automatic_updates_extensions_test_theme-8-x-2-0-release</release_link>
<download_link>http://example.com/automatic_updates_extensions_test_theme-8.x-2.0.tar.gz</download_link>
<date>1597793786</date>
<files>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-8.x-2.0.tar.gz</url>
<archive_type>tar.gz</archive_type>
<md5>43956be126b27d718ecffd0b17244aa1</md5>
<size>196080</size>
<filedate>1597793786</filedate>
</file>
<file>
<url>http://example.com/automatic_updates_extensions_test_theme-8.x-2.0.zip</url>
<archive_type>zip</archive_type>
<md5>5d24ec88ac4755af6ccbe6ef5ff8b3c9</md5>
<size>200399</size>
<filedate>1597793786</filedate>
</file>
</files>
<terms>
<term>
<name>Release type</name>
<value>Bug fixes</value>
</term>
</terms>
<security covered="1">Covered by Drupal's security advisory policy</security>
<core_compatibility>8.x</core_compatibility>
</release>
</releases>
</project>
<?xml version="1.0" encoding="utf-8"?>
<!--
This fixture is used by \Drupal\Tests\automatic_updates_extensions\Build\ModuleUpdateTest.
Contains metadata about the following (fake) releases of new_module, all of which are secure, in order:
- 1.1.0
- 1.0.0
-->
<project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>New Module</title>
<short_name>new_module</short_name>
<dc:creator>Drupal</dc:creator>
<supported_branches>1.1.,1.0.</supported_branches>
<project_status>published</project_status>
<link>http://example.com/project/new_module</link>
<terms>
<term><name>Projects</name><value>New Module project</value></term>
</terms>
<releases>
<release>
<name>New Module 1.1.0</name>
<version>1.1.0</version>
<tag>1.1.0</tag>
<status>published</status>
<release_link>http://example.com/new_module-1-1-0-release</release_link>
<download_link>http://example.com/new_module-1-1-0.tar.gz</download_link>
<date>1584195300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>New Module 1.0.0</name>
<version>1.0.0</version>
<tag>1.0.0</tag>
<status>published</status>
<release_link>http://example.com/new_module-1-0-0-release</release_link>
<download_link>http://example.com/new_module-1-0-0.tar.gz</download_link>
<date>1581603300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
</releases>
</project>
<?xml version="1.0" encoding="utf-8"?>
<!--
This fixture is used by \Drupal\Tests\automatic_updates_extensions\Functional\UpdaterFormTest.
Contains metadata about the following (fake) releases of semver_test module, all of which are secure, in order:
- 8.2.0, which is in an unsupported branch
- 8.1.1
- 8.1.1-beta1
- 8.1.1-alpha1
- 8.1.0
- 8.1.0-beta1
- 8.1.0-alpha1
- 8.0.1
- 8.0.1-beta1
- 8.0.1-alpha1
- 8.0.0
- 8.0.0-beta1
- 8.0.0-alpha1
-->
<project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Semver Test</title>
<short_name>semver_test</short_name>
<dc:creator>Drupal</dc:creator>
<supported_branches>8.0.,8.1.</supported_branches>
<project_status>published</project_status>
<link>http://example.com/project/semver_test</link>
<terms>
<term><name>Projects</name><value>Semver Test project</value></term>
</terms>
<releases>
<release>
<name>Semver Test 8.2.0</name>
<version>8.2.0</version>
<tag>8.2.0</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-2-0-release</release_link>
<download_link>http://example.com/semver_test-8-2-0.tar.gz</download_link>
<date>1584195300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.1</name>
<version>8.1.1</version>
<tag>8.1.1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-1-release</release_link>
<download_link>http://example.com/semver_test-8-1-1.tar.gz</download_link>
<date>1581603300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.1-beta1</name>
<version>8.1.1-beta1</version>
<tag>8.1.1-beta1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-1-beta1-release</release_link>
<download_link>http://example.com/semver_test-8-1-1-beta1.tar.gz</download_link>
<date>1579011300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.1-alpha1</name>
<version>8.1.1-alpha1</version>
<tag>8.1.1-alpha1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-1-alpha1-release</release_link>
<download_link>http://example.com/semver_test-8-1-1-alpha1.tar.gz</download_link>
<date>1576419300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.0</name>
<version>8.1.0</version>
<tag>8.1.0</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-0-release</release_link>
<download_link>http://example.com/semver_test-8-1-0.tar.gz</download_link>
<date>1573827300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.0-beta1</name>
<version>8.1.0-beta1</version>
<tag>8.1.0-beta1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-0-beta1-release</release_link>
<download_link>http://example.com/semver_test-8-1-0-beta1.tar.gz</download_link>
<date>1571235300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.1.0-alpha1</name>
<version>8.1.0-alpha1</version>
<tag>8.1.0-alpha1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-1-0-alpha1-release</release_link>
<download_link>http://example.com/semver_test-8-1-0-alpha1.tar.gz</download_link>
<date>1568643300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.1</name>
<version>8.0.1</version>
<tag>8.0.1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-1-release</release_link>
<download_link>http://example.com/semver_test-8-0-1.tar.gz</download_link>
<date>1566051300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.1-beta1</name>
<version>8.0.1-beta1</version>
<tag>8.0.1-beta1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-1-beta1-release</release_link>
<download_link>http://example.com/semver_test-8-0-1-beta1.tar.gz</download_link>
<date>1563459300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.1-alpha1</name>
<version>8.0.1-alpha1</version>
<tag>8.0.1-alpha1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-1-alpha1-release</release_link>
<download_link>http://example.com/semver_test-8-0-1-alpha1.tar.gz</download_link>
<date>1560867300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.0</name>
<version>8.0.0</version>
<tag>8.0.0</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-0-release</release_link>
<download_link>http://example.com/semver_test-8-0-0.tar.gz</download_link>
<date>1558275300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.0-beta1</name>
<version>8.0.0-beta1</version>
<tag>8.0.0-beta1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-0-beta1-release</release_link>
<download_link>http://example.com/semver_test-8-0-0-beta1.tar.gz</download_link>
<date>1555683300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
<release>
<name>Semver Test 8.0.0-alpha1</name>
<version>8.0.0-alpha1</version>
<tag>8.0.0-alpha1</tag>
<status>published</status>
<release_link>http://example.com/semver_test-8-0-0-alpha1-release</release_link>
<download_link>http://example.com/semver_test-8-0-0-alpha1.tar.gz</download_link>
<date>1553091300</date>
<terms>
<term><name>Release type</name><value>New features</value></term>
<term><name>Release type</name><value>Bug fixes</value></term>
</terms>
</release>
</releases>
</project>
name: 'Automatic Updates Extensions Tests'
description: 'Provides functional tests for recommended versions'
type: module
package: Testing
dependencies:
- automatic_updates:automatic_updates_extensions
<?php
/**
* @file
* Contains hook implementation for Automatic Updates Extensions Test.
*/
/**
* Implements hook_update_status_alter().
*/
function automatic_updates_extensions_test_update_status_alter(&$projects) {
if (\Drupal::state()->get('testUninstallableRelease') && !empty($projects['semver_test'])) {
$projects['semver_test']['recommended'] = $projects['semver_test']['existing_version'];
}
}
name: 'Automatic Updates Extensions Test API'
description: 'Provides API endpoints for doing stage operations in functional tests.'
type: module
package: Testing
dependencies:
- automatic_updates:automatic_updates_extensions
- automatic_updates:package_manager_test_api
automatic_updates_extensions_test_api:
path: '/automatic-updates-extensions-test-api'
defaults:
_controller: 'Drupal\automatic_updates_extensions_test_api\ApiController::run'
requirements:
_access: 'TRUE'
automatic_updates_extensions_test_api.finish:
path: '/automatic-updates-extensions-test-api/finish/{id}'
defaults:
_controller: 'Drupal\automatic_updates_extensions_test_api\ApiController::finish'
requirements:
_access: 'TRUE'
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions_test_api;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\package_manager\PathLocator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\package_manager_test_api\ApiController as PackageManagerApiController;
/**
* Provides API endpoint to interact with stage directory in functional tests.
*/
class ApiController extends PackageManagerApiController {
/**
* {@inheritdoc}
*/
protected $finishedRoute = 'automatic_updates_extensions_test_api.finish';
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get(ExtensionUpdateStage::class),
$container->get(PathLocator::class),
);
}
/**
* {@inheritdoc}
*/
protected function createAndApplyStage(Request $request): string {
$id = $this->stage->begin($request->get('projects', []));
$this->stage->stage();
$this->stage->apply();
return $id;
}
}