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 744 additions and 490 deletions
<?php <?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Form; 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_extensions\BatchProcessor;
use Drupal\automatic_updates\BatchProcessor as AutoUpdatesBatchProcessor; use Drupal\automatic_updates\BatchProcessor as AutoUpdatesBatchProcessor;
use Drupal\automatic_updates_extensions\ExtensionUpdater; use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\Core\Batch\BatchBuilder; use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\package_manager\Exception\StageException; use Drupal\package_manager\Exception\StageException;
use Drupal\package_manager\Exception\StageOwnershipException; use Drupal\package_manager\Exception\StageOwnershipException;
use Drupal\system\SystemManager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/** /**
* Defines a form to commit staged updates. * Defines a form to commit staged updates.
* *
* @internal * @internal
* Form classes are internal. * Form classes are internal and should not be used by external code.
*/ */
final class UpdateReady extends FormBase { final class UpdateReady extends UpdateFormBase {
/**
* The updater service.
*
* @var \Drupal\automatic_updates_extensions\ExtensionUpdater
*/
protected $updater;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The module list service.
*
* @var \Drupal\Core\Extension\ModuleExtensionList
*/
protected $moduleList;
/** /**
* Constructs a new UpdateReady object. * Constructs a new UpdateReady object.
* *
* @param \Drupal\automatic_updates_extensions\ExtensionUpdater $updater * @param \Drupal\automatic_updates_extensions\ExtensionUpdateStage $stage
* The updater service. * The update stage service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger * @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service. * The messenger service.
* @param \Drupal\Core\State\StateInterface $state * @param \Drupal\Core\State\StateInterface $state
* The state service. * The state service.
* @param \Drupal\Core\Extension\ModuleExtensionList $module_list * @param \Drupal\Core\Extension\ModuleExtensionList $moduleList
* The module list service. * 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(ExtensionUpdater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list) { public function __construct(
$this->updater = $updater; 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); $this->setMessenger($messenger);
$this->state = $state;
$this->moduleList = $module_list;
} }
/** /**
...@@ -75,10 +80,14 @@ final class UpdateReady extends FormBase { ...@@ -75,10 +80,14 @@ final class UpdateReady extends FormBase {
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( return new static(
$container->get('automatic_updates_extensions.updater'), $container->get(ExtensionUpdateStage::class),
$container->get('messenger'), $container->get('messenger'),
$container->get('state'), $container->get('state'),
$container->get('extension.list.module'), $container->get('extension.list.module'),
$container->get('renderer'),
$container->get('event_dispatcher'),
$container->get(ComposerInspector::class),
$container->get(PathLocator::class),
); );
} }
...@@ -87,21 +96,19 @@ final class UpdateReady extends FormBase { ...@@ -87,21 +96,19 @@ final class UpdateReady extends FormBase {
*/ */
public function buildForm(array $form, FormStateInterface $form_state, string $stage_id = NULL) { public function buildForm(array $form, FormStateInterface $form_state, string $stage_id = NULL) {
try { try {
$this->updater->claim($stage_id); $this->stage->claim($stage_id);
} }
catch (StageOwnershipException $e) { catch (StageOwnershipException) {
$this->messenger()->addError($this->t('Cannot continue the update because another Composer operation is currently in progress.')); $this->messenger()->addError($this->t('Cannot continue the update because another Composer operation is currently in progress.'));
return $form; return $form;
} }
catch (StageFailureMarkerException $e) {
$this->messenger()->addError($e->getMessage());
return $form;
}
$messages = []; $messages = [];
// @todo Add logic to warn about possible new database updates. Determine if
// \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator
// should be duplicated or changed so that it can work with other stages.
// @see \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator
// @see \Drupal\automatic_updates\Form\UpdateReady::buildForm()
// Don't set any messages if the form has been submitted, because we don't // Don't set any messages if the form has been submitted, because we don't
// want them to be set during form submit. // want them to be set during form submit.
if (!$form_state->getUserInput()) { if (!$form_state->getUserInput()) {
...@@ -124,12 +131,14 @@ final class UpdateReady extends FormBase { ...@@ -124,12 +131,14 @@ final class UpdateReady extends FormBase {
'#type' => 'value', '#type' => 'value',
'#value' => $stage_id, '#value' => $stage_id,
]; ];
$form['package_updates'] = $this->showUpdates();
// @todo Display the project versions that will be update including any
// dependencies that are Drupal projects.
$form['backup'] = [ $form['backup'] = [
'#prefix' => '<strong>', '#prefix' => '<strong>',
'#markup' => $this->t('Back up your database and site before you continue. <a href=":backup_url">Learn how</a>.', [':backup_url' => 'https://www.drupal.org/node/22281']), '#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>', '#suffix' => '</strong>',
]; ];
$form['maintenance_mode'] = [ $form['maintenance_mode'] = [
...@@ -137,6 +146,18 @@ final class UpdateReady extends FormBase { ...@@ -137,6 +146,18 @@ final class UpdateReady extends FormBase {
'#type' => 'checkbox', '#type' => 'checkbox',
'#default_value' => TRUE, '#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'] = [ $form['actions']['submit'] = [
'#type' => 'submit', '#type' => 'submit',
'#value' => $this->t('Continue'), '#value' => $this->t('Continue'),
...@@ -162,6 +183,7 @@ final class UpdateReady extends FormBase { ...@@ -162,6 +183,7 @@ final class UpdateReady extends FormBase {
->setTitle($this->t('Apply updates')) ->setTitle($this->t('Apply updates'))
->setInitMessage($this->t('Preparing to apply updates')) ->setInitMessage($this->t('Preparing to apply updates'))
->addOperation([BatchProcessor::class, 'commit'], [$stage_id]) ->addOperation([BatchProcessor::class, 'commit'], [$stage_id])
->addOperation([BatchProcessor::class, 'postApply'], [$stage_id])
->addOperation([BatchProcessor::class, 'clean'], [$stage_id]) ->addOperation([BatchProcessor::class, 'clean'], [$stage_id])
->setFinishCallback([BatchProcessor::class, 'finishCommit']) ->setFinishCallback([BatchProcessor::class, 'finishCommit'])
->toArray(); ->toArray();
...@@ -174,7 +196,7 @@ final class UpdateReady extends FormBase { ...@@ -174,7 +196,7 @@ final class UpdateReady extends FormBase {
*/ */
public function cancel(array &$form, FormStateInterface $form_state): void { public function cancel(array &$form, FormStateInterface $form_state): void {
try { try {
$this->updater->destroy(); $this->stage->destroy();
$this->messenger()->addStatus($this->t('The update was successfully cancelled.')); $this->messenger()->addStatus($this->t('The update was successfully cancelled.'));
$form_state->setRedirect('automatic_updates_extensions.report_update'); $form_state->setRedirect('automatic_updates_extensions.report_update');
} }
...@@ -183,4 +205,115 @@ final class UpdateReady extends FormBase { ...@@ -183,4 +205,115 @@ final class UpdateReady extends FormBase {
} }
} }
/**
* 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 <?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Form; namespace Drupal\automatic_updates_extensions\Form;
use Drupal\automatic_updates\Event\ReadinessCheckEvent; use Drupal\automatic_updates\Form\UpdateFormBase;
use Drupal\automatic_updates\Validation\ReadinessTrait;
use Drupal\automatic_updates_extensions\BatchProcessor; use Drupal\automatic_updates_extensions\BatchProcessor;
use Drupal\automatic_updates_extensions\ExtensionUpdater; use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\Core\Batch\BatchBuilder; use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface; 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\system\SystemManager;
use Drupal\update\ProjectRelease;
use Drupal\update\UpdateManagerInterface; use Drupal\update\UpdateManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
...@@ -19,59 +29,52 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; ...@@ -19,59 +29,52 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
* A form for selecting extension updates. * A form for selecting extension updates.
* *
* @internal * @internal
* Form classes are internal. * Form classes are internal and should not be used by external code.
*/ */
final class UpdaterForm extends FormBase { final class UpdaterForm extends UpdateFormBase {
use ReadinessTrait;
/**
* The extension updater service.
*
* @var \Drupal\automatic_updates_extensions\ExtensionUpdater
*/
private $extensionUpdater;
/**
* The event dispatcher service.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $eventDispatcher;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
private $renderer;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( return new static(
$container->get('automatic_updates_extensions.updater'), $container->get(ExtensionUpdateStage::class),
$container->get('event_dispatcher'), $container->get('event_dispatcher'),
$container->get('renderer') $container->get('renderer'),
$container->get('state'),
$container->get(FailureMarker::class),
$container->get(ComposerInspector::class),
$container->get(PathLocator::class),
); );
} }
/** /**
* Constructs a new UpdaterForm object. * Constructs a new UpdaterForm object.
* *
* @param \Drupal\automatic_updates_extensions\ExtensionUpdater $extension_updater * @param \Drupal\automatic_updates_extensions\ExtensionUpdateStage $stage
* The extension updater service. * The extension update stage service.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The extension event dispatcher service. * The extension event dispatcher service.
* @param \Drupal\Core\Render\RendererInterface $renderer * @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service. * 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(ExtensionUpdater $extension_updater, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer) { public function __construct(
$this->extensionUpdater = $extension_updater; private readonly ExtensionUpdateStage $stage,
$this->eventDispatcher = $event_dispatcher; private readonly EventDispatcherInterface $eventDispatcher,
$this->renderer = $renderer; private readonly RendererInterface $renderer,
} private readonly StateInterface $state,
private readonly FailureMarker $failureMarker,
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {}
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -84,6 +87,13 @@ final class UpdaterForm extends FormBase { ...@@ -84,6 +87,13 @@ final class UpdaterForm extends FormBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function buildForm(array $form, FormStateInterface $form_state) { 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(); $update_projects = $this->getRecommendedModuleUpdates();
$options = []; $options = [];
$recommended_versions = []; $recommended_versions = [];
...@@ -101,10 +111,16 @@ final class UpdaterForm extends FormBase { ...@@ -101,10 +111,16 @@ final class UpdaterForm extends FormBase {
default: default:
$status_message = ''; $status_message = '';
} }
$project_release = ProjectRelease::createFromArray($update_project['releases'][$update_project['recommended']]);
$options[$project_name] = [ $options[$project_name] = [
$update_project['title'] . $status_message, $update_project['title'] . $status_message,
$update_project['existing_version'], $update_project['existing_version'],
$update_project['recommended'], $this->t(
'@version (<a href=":url">Release notes</a>)',
[
'@version' => $project_release->getVersion(),
':url' => $project_release->getReleaseUrl(),
]),
]; ];
$recommended_versions[$project_name] = $update_project['recommended']; $recommended_versions[$project_name] = $update_project['recommended'];
} }
...@@ -122,23 +138,27 @@ final class UpdaterForm extends FormBase { ...@@ -122,23 +138,27 @@ final class UpdaterForm extends FormBase {
'#options' => $options, '#options' => $options,
'#empty' => $this->t('There are no available updates.'), '#empty' => $this->t('There are no available updates.'),
'#attributes' => ['class' => ['update-recommended']], '#attributes' => ['class' => ['update-recommended']],
'#required' => TRUE,
'#required_error' => t('Select one or more projects.'),
]; ];
if ($form_state->getUserInput()) { if ($form_state->getUserInput()) {
$results = []; $results = [];
} }
else { else {
$event = new ReadinessCheckEvent($this->extensionUpdater); $results = $this->runStatusCheck($this->stage, $this->eventDispatcher);
$this->eventDispatcher->dispatch($event);
$results = $event->getResults();
} }
$this->displayResults($results, $this->messenger(), $this->renderer); $this->displayResults($results, $this->renderer);
$security_level = $this->getOverallSeverity($results); $security_level = ValidationResult::getOverallSeverity($results);
if ($update_projects && $security_level !== SystemManager::REQUIREMENT_ERROR) { if ($update_projects && $security_level !== SystemManager::REQUIREMENT_ERROR) {
$form['actions'] = $this->actions($form_state); $form['actions'] = $this->actions($form_state);
} }
$form['last_check'] = [
'#theme' => 'update_last_check',
'#last' => $this->state->get('update.last_check', 0),
];
return $form; return $form;
} }
...@@ -151,9 +171,9 @@ final class UpdaterForm extends FormBase { ...@@ -151,9 +171,9 @@ final class UpdaterForm extends FormBase {
* @return mixed[][] * @return mixed[][]
* The form's actions elements. * The form's actions elements.
*/ */
protected function actions(FormStateInterface $form_state): array { private function actions(FormStateInterface $form_state): array {
$actions = ['#type' => 'actions']; $actions = ['#type' => 'actions'];
if (!$this->extensionUpdater->isAvailable()) { if (!$this->stage->isAvailable()) {
// If the form has been submitted do not display this error message // If the form has been submitted do not display this error message
// because ::deleteExistingUpdate() may run on submit. The message will // because ::deleteExistingUpdate() may run on submit. The message will
// still be displayed on form build if needed. // still be displayed on form build if needed.
...@@ -179,7 +199,7 @@ final class UpdaterForm extends FormBase { ...@@ -179,7 +199,7 @@ final class UpdaterForm extends FormBase {
* Submit function to delete an existing in-progress update. * Submit function to delete an existing in-progress update.
*/ */
public function deleteExistingUpdate(): void { public function deleteExistingUpdate(): void {
$this->extensionUpdater->destroy(TRUE); $this->stage->destroy(TRUE);
$this->messenger()->addMessage($this->t("Staged update deleted")); $this->messenger()->addMessage($this->t("Staged update deleted"));
} }
...@@ -208,27 +228,68 @@ final class UpdaterForm extends FormBase { ...@@ -208,27 +228,68 @@ final class UpdaterForm extends FormBase {
/** /**
* Gets the modules that require updates. * Gets the modules that require updates.
* *
* @return array * @return mixed[]
* Modules that require updates. * Modules that require updates.
*/ */
private function getRecommendedModuleUpdates(): array { private function getRecommendedModuleUpdates(): array {
$supported_project_types = [
"module", "module-disabled", "theme", "theme-disabled",
];
$available_updates = update_get_available(TRUE); $available_updates = update_get_available(TRUE);
if (empty($available_updates)) { if (empty($available_updates)) {
$this->messenger()->addError('There was a problem getting update information. Try again later.'); $this->messenger()->addError('There was a problem getting update information. Try again later.');
return []; return [];
} }
$project_data = update_calculate_project_data($available_updates); $all_projects_data = update_calculate_project_data($available_updates);
$outdated_modules = []; $outdated_modules = [];
foreach ($project_data as $project_name => $project_info) { $installed_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
if ($project_info['project_type'] === 'module' || $project_info['project_type'] === 'module-disabled') { $non_supported_update_statuses = [];
if ($project_info['status'] !== UpdateManagerInterface::CURRENT) { foreach ($all_projects_data as $project_name => $project_data) {
if (!empty($project_info['recommended'])) { if (in_array($project_data['project_type'], $supported_project_types, TRUE)) {
$outdated_modules[$project_name] = $project_info; 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; 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
namespace Drupal\automatic_updates_extensions\Validator;
use Composer\Package\PackageInterface;
use Drupal\automatic_updates_extensions\ExtensionUpdater;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates packages are installed via Composer.
*/
class PackagesInstalledWithComposerValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a InstalledPackagesValidator object.
*
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The translation service.
*/
public function __construct(TranslationInterface $translation) {
$this->setStringTranslation($translation);
}
/**
* Validates that packages are installed with composer or not.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
*/
public function checkPackagesInstalledWithComposer(PreOperationStageEvent $event): void {
$stage = $event->getStage();
if ($stage instanceof ExtensionUpdater) {
$active_composer = $stage->getActiveComposer();
$installed_packages = $active_composer->getInstalledPackages();
$missing_packages = [];
if ($event instanceof PreCreateEvent) {
$package_versions = $stage->getPackageVersions();
foreach (['production', 'dev'] as $package_type) {
$missing_packages = array_merge($missing_packages, array_diff_key($package_versions[$package_type], $installed_packages));
}
}
else {
$missing_packages = $stage->getStageComposer()
->getPackagesNotIn($active_composer);
// For new dependency added in the stage will are only concerned with
// ones that are Drupal projects that have Update XML from Drupal.org
// Since the Update module does allow use to check any of these projects
// if they don't exist in the active code base. Other types of projects
// even if they are in the 'drupal/' namespace they would not have
// Update XML on Drupal.org so it doesn't matter if they are in the
// active codebase or not.
$types = [
'drupal-module',
'drupal-theme',
'drupal-profile',
];
$filter = function (PackageInterface $package) use ($types): bool {
return in_array($package->getType(), $types);
};
$missing_packages = array_filter($missing_packages, $filter);
// Saving only the packages whose name starts with drupal/.
$missing_packages = array_filter($missing_packages, function (string $key) {
return strpos($key, 'drupal/') === 0;
}, ARRAY_FILTER_USE_KEY);
}
if ($missing_packages) {
$missing_projects = [];
foreach ($missing_packages as $package => $version) {
// Removing drupal/ from package name for better user presentation.
$project = str_replace('drupal/', '', $package);
$missing_projects[] = $project;
}
if ($missing_projects) {
$event->addError($missing_projects, $this->t('Automatic Updates can only update projects that were installed via Composer. The following packages are not installed through composer:'));
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
PreCreateEvent::class => 'checkPackagesInstalledWithComposer',
PreApplyEvent::class => 'checkPackagesInstalledWithComposer',
];
}
}
<?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 <?php
declare(strict_types = 1);
namespace Drupal\automatic_updates_extensions\Validator; namespace Drupal\automatic_updates_extensions\Validator;
use Drupal\automatic_updates\ProjectInfo; use Drupal\package_manager\ComposerInspector;
use Drupal\automatic_updates_extensions\ExtensionUpdater; use Drupal\package_manager\PathLocator;
use Drupal\automatic_updates\LegacyVersionUtility; use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\Event\PreCreateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
...@@ -15,11 +18,67 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; ...@@ -15,11 +18,67 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
* @internal * @internal
* This class is an internal part of the module's update handling and * This class is an internal part of the module's update handling and
* should not be used by external code. * 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 { final class UpdateReleaseValidator implements EventSubscriberInterface {
use StringTranslationTrait; 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. * Checks that the update projects are secure and supported.
* *
...@@ -27,47 +86,26 @@ final class UpdateReleaseValidator implements EventSubscriberInterface { ...@@ -27,47 +86,26 @@ final class UpdateReleaseValidator implements EventSubscriberInterface {
* The event object. * The event object.
*/ */
public function checkRelease(PreCreateEvent $event): void { public function checkRelease(PreCreateEvent $event): void {
$stage = $event->getStage(); $stage = $event->stage;
// This check only works with Automatic Updates Extensions. // This check only works with Automatic Updates Extensions.
if (!$stage instanceof ExtensionUpdater) { if ($stage->getType() !== 'automatic_updates_extensions:attended') {
return; return;
} }
$active_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$all_versions = $stage->getPackageVersions(); $all_versions = $stage->getPackageVersions();
$messages = []; $messages = [];
foreach (['production', 'dev'] as $package_type) { foreach (['production', 'dev'] as $package_type) {
foreach ($all_versions[$package_type] as $package_name => $sematic_version) { foreach ($all_versions[$package_type] as $package_name => $sematic_version) {
$package_parts = explode('/', $package_name); $project_name = $active_packages[$package_name]->getProjectName();
$project_name = $package_parts[1];
// If the version isn't in the list of installable releases, then it // 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. // isn't secure and supported and the user should receive an error.
$releases = (new ProjectInfo($project_name))->getInstallableReleases(); if (!$this->isSupportedRelease($project_name, $sematic_version)) {
$is_missing_version = FALSE;
if (empty($releases)) {
$is_missing_version = TRUE;
}
elseif (!array_key_exists($sematic_version, $releases)) {
$legacy_version = LegacyVersionUtility::convertToLegacyVersion($sematic_version);
if ($legacy_version) {
if (!array_key_exists($legacy_version, $releases)) {
// If we cannot find the version using semantic or legacy then the
// version is missing.
$is_missing_version = TRUE;
}
}
else {
// If we cannot convert the semantic version into a legacy version
// then the version is missing.
$is_missing_version = TRUE;
}
}
if ($is_missing_version) {
$messages[] = $this->t('Project @project_name to version @version', [ $messages[] = $this->t('Project @project_name to version @version', [
'@project_name' => $project_name, '@project_name' => $project_name,
'@version' => $sematic_version, '@version' => $sematic_version,
]); ]);
} }
} }
} }
if ($messages) { if ($messages) {
...@@ -83,7 +121,7 @@ final class UpdateReleaseValidator implements EventSubscriberInterface { ...@@ -83,7 +121,7 @@ final class UpdateReleaseValidator implements EventSubscriberInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function getSubscribedEvents() { public static function getSubscribedEvents(): array {
return [ return [
PreCreateEvent::class => 'checkRelease', PreCreateEvent::class => 'checkRelease',
]; ];
......
{
"require": {
"drupal/core-recommended": "^9",
"drupal/my_module": "^9"
},
"require-dev": {
"drupal/my_dev_module": "^9"
}
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/my_module",
"version": "9.8.0"
}
],
"packages-dev": [
{
"name": "drupal/my_dev_module",
"version": "9.8.1"
}
]
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0",
"extra": {
"drupal-scaffold": {
"file-mapping": {}
}
}
},
{
"name": "drupal/my_module",
"version": "9.8.0"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
name: 'New module' name: 'New module'
type: module type: module
core_version_requirement: ^9 core_version_requirement: ^9.7 || ^10
project: new_module
name: 'New module' name: 'New module'
type: module type: module
core_version_requirement: ^9 core_version_requirement: ^9.7 || ^10
project: new_module
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
},
{
"name": "drupal/my_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1",
"type": "drupal-module"
},
{
"name": "drupal/existing_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/existing_theme",
"version": "9.8.0",
"type": "drupal-theme"
},
{
"name": "drupal/existing_profile",
"version": "9.8.0",
"type": "drupal-profile"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
},
{
"name": "drupal/my_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1",
"type": "drupal-module"
},
{
"name": "drupal/new_module",
"version": "9.8.0",
"type": "drupal-module"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
},
{
"name": "drupal/my_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1",
"type": "drupal-module"
},
{
"name": "drupal/new_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "not-drupal/new_module1",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/new_theme",
"version": "9.8.0",
"type": "drupal-theme"
},
{
"name": "drupal/new_profile",
"version": "9.8.0",
"type": "drupal-profile"
},
{
"name": "drupal/new_dependency",
"version": "9.8.0",
"type": "drupal-library"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
},
{
"name": "drupal/my_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1",
"type": "drupal-module"
},
{
"name": "drupal/new_profile",
"version": "9.8.0",
"type": "drupal-profile"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
{
"packages": [
{
"name": "drupal/core-recommended",
"version": "9.8.0",
"require": {
"drupal/core": "9.8.0"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
},
{
"name": "drupal/my_module",
"version": "9.8.0",
"type": "drupal-module"
},
{
"name": "drupal/my_dev_module",
"version": "9.8.1",
"type": "drupal-module"
},
{
"name": "drupal/new_theme",
"version": "9.8.0",
"type": "drupal-theme"
}
],
"dev": true,
"dev-package-names": [
"drupal/my_dev_module"
]
}
<?xml version="1.0" encoding="utf-8"?> <?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/"> <project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Alpha</title> <title>Alpha</title>
<short_name>alpha</short_name> <short_name>alpha</short_name>
......
<?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"?> <?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/"> <project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>New Module</title> <title>New Module</title>
<short_name>new_module</short_name> <short_name>new_module</short_name>
......
<?xml version="1.0" encoding="utf-8"?> <?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/"> <project xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Semver Test</title> <title>Semver Test</title>
<short_name>semver_test</short_name> <short_name>semver_test</short_name>
......