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 1249 additions and 704 deletions
<?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>
......
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'];
}
}
......@@ -4,3 +4,4 @@ type: module
package: Testing
dependencies:
- automatic_updates:automatic_updates_extensions
- automatic_updates:package_manager_test_api
......@@ -4,3 +4,9 @@ automatic_updates_extensions_test_api:
_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\ExtensionUpdater;
use Drupal\Core\Controller\ControllerBase;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\package_manager\PathLocator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\package_manager_test_api\ApiController as PackageManagerApiController;
/**
* Provides API endpoints to interact with a staging area in functional tests.
* Provides API endpoint to interact with stage directory in functional tests.
*/
class ApiController extends ControllerBase {
/**
* The extension updater.
*
* @var \Drupal\automatic_updates_extensions\ExtensionUpdater
*/
private $extensionUpdater;
/**
* The path locator service.
*
* @var \Drupal\package_manager\PathLocator
*/
private $pathLocator;
class ApiController extends PackageManagerApiController {
/**
* Constructs an ApiController object.
*
* @param \Drupal\automatic_updates_extensions\ExtensionUpdater $extensionUpdater
* The updater.
* @param \Drupal\package_manager\PathLocator $path_locator
* The path locator service.
* {@inheritdoc}
*/
public function __construct(ExtensionUpdater $extensionUpdater, PathLocator $path_locator) {
$this->extensionUpdater = $extensionUpdater;
$this->pathLocator = $path_locator;
}
protected $finishedRoute = 'automatic_updates_extensions_test_api.finish';
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('automatic_updates_extensions.updater'),
$container->get('package_manager.path_locator')
$container->get(ExtensionUpdateStage::class),
$container->get(PathLocator::class),
);
}
/**
* Runs a complete stage life cycle.
*
* Creates a staging area, requires packages into it, applies changes to the
* active directory, and destroys the stage.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request. The runtime and dev dependencies are expected to be in
* either the query string or request body, under the 'runtime' and 'dev'
* keys, respectively. There may also be a 'files_to_return' key, which
* contains an array of file paths, relative to the project root, whose
* contents should be returned in the response.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A JSON response containing an associative array of the contents of the
* files listed in the 'files_to_return' request key. The array will be
* keyed by path, relative to the project root.
* {@inheritdoc}
*/
public function run(Request $request): JsonResponse {
$this->extensionUpdater->begin($request->get('projects', []));
$this->extensionUpdater->stage();
$this->extensionUpdater->apply();
$this->extensionUpdater->postApply();
$this->extensionUpdater->destroy();
$dir = $this->pathLocator->getProjectRoot();
$file_contents = [];
foreach ($request->get('files_to_return', []) as $path) {
$file_contents[$path] = file_get_contents($dir . '/' . $path);
}
return new JsonResponse($file_contents);
protected function createAndApplyStage(Request $request): string {
$id = $this->stage->begin($request->get('projects', []));
$this->stage->stage();
$this->stage->apply();
return $id;
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Build;
use Drupal\Tests\automatic_updates\Build\UpdateTestBase;
......@@ -7,11 +9,12 @@ use Drupal\Tests\automatic_updates_extensions\Traits\FormTestTrait;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
/**
* Tests updating modules in a staging area.
* Tests updating modules in a stage directory.
*
* @group automatic_updates_extensions
* @internal
*/
class ModuleUpdateTest extends UpdateTestBase {
final class ModuleUpdateTest extends UpdateTestBase {
use FormTestTrait;
......@@ -21,7 +24,7 @@ class ModuleUpdateTest extends UpdateTestBase {
protected function createTestProject(string $template): void {
parent::createTestProject($template);
$this->setReleaseMetadata([
'drupal' => __DIR__ . '/../../../../tests/fixtures/release-history/drupal.9.8.1-security.xml',
'drupal' => __DIR__ . '/../../../../package_manager/tests/fixtures/release-history/drupal.9.8.2.xml',
'alpha' => __DIR__ . '/../../fixtures/release-history/alpha.1.1.0.xml',
'new_module' => __DIR__ . '/../../fixtures/release-history/new_module.1.1.0.xml',
]);
......@@ -37,32 +40,35 @@ class ModuleUpdateTest extends UpdateTestBase {
\$config['update_test.settings']['system_info'] = $system_info;
END;
$this->writeSettings($code);
$this->addRepository('alpha', __DIR__ . '/../../../../package_manager/tests/fixtures/alpha/1.0.0');
$this->runComposer('COMPOSER_MIRROR_PATH_REPOS=1 composer require drupal/alpha --update-with-all-dependencies', 'project');
$alpha_repo_path = $this->copyFixtureToTempDirectory(__DIR__ . '/../../../../package_manager/tests/fixtures/build_test_projects/alpha/1.0.0');
$this->addRepository('alpha', $alpha_repo_path);
$this->runComposer('composer require drupal/alpha --update-with-all-dependencies', 'project');
$this->assertModuleVersion('alpha', '1.0.0');
$fs = new SymfonyFilesystem();
$fs->mirror(__DIR__ . '/../../fixtures/new_module', $this->getWorkspaceDirectory() . '/project/web/modules');
$fs->mirror($this->copyFixtureToTempDirectory(__DIR__ . '/../../fixtures/new_module'), $this->getWorkspaceDirectory() . '/project/web/modules');
$this->installModules([
'automatic_updates_extensions_test_api',
'alpha',
'new_module',
]);
// Change both modules' upstream version.
$this->addRepository('alpha', __DIR__ . '/../../../../package_manager/tests/fixtures/alpha/1.1.0');
// Change the module's upstream version.
static::copyFixtureFilesTo(__DIR__ . '/../../../../package_manager/tests/fixtures/build_test_projects/alpha/1.1.0', $alpha_repo_path);
// Ensure that none of the above changes have caused any status check or
// other errors on the status report.
$this->checkForUpdates();
$this->assertStatusReportChecksSuccessful();
}
/**
* Tests updating a module in a staging area via the API.
* Tests updating a module in a stage directory via the API.
*/
public function testApi(): void {
$this->createTestProject('RecommendedProject');
// Use the API endpoint to create a stage and update the 'new_module' module
// to 1.1.0.
// @see \Drupal\automatic_updates_extensions_test_api\ApiController::run()
// There will be error in updating as this module is not installed
// via composer @see \Drupal\Tests\automatic_updates_extensions\Kernel\Validator\PackagesInstalledWithComposerValidatorTest.
$query = http_build_query([
'projects' => [
'new_module' => '1.1.0',
......@@ -72,36 +78,33 @@ END;
$mink = $this->getMink();
$mink->assertSession()->statusCodeEquals(500);
$page_text = $mink->getSession()->getPage()->getText();
$this->assertStringContainsString('Automatic Updates can only update projects that were installed via Composer. The following packages are not installed through composer:', $page_text);
$this->assertStringContainsString('The project new_module is not a Drupal project known to Composer and cannot be updated.', $page_text);
$this->assertStringContainsString('new_module', $page_text);
// Use the API endpoint to create a stage and update the 'alpha' module to
// 1.1.0. We ask the API to return the contents of the module's
// composer.json file, so we can assert that they were updated to the
// version we expect.
// @see \Drupal\automatic_updates_extensions_test_api\ApiController::run()
$query = http_build_query([
'projects' => [
'alpha' => '1.1.0',
],
'files_to_return' => [
'web/modules/contrib/alpha/composer.json',
],
]);
$this->visit("/automatic-updates-extensions-test-api?$query");
$mink = $this->getMink();
$mink->assertSession()->statusCodeEquals(200);
$file_contents = $mink->getSession()->getPage()->getContent();
$file_contents = json_decode($file_contents, TRUE);
$module_composer_json = json_decode($file_contents['web/modules/contrib/alpha/composer.json']);
$this->assertSame('1.1.0', $module_composer_json->version);
// 1.1.0.
$this->makePackageManagerTestApiRequest(
'/automatic-updates-extensions-test-api',
[
'projects' => [
'alpha' => '1.1.0',
],
]
);
$updated_composer_json = $this->getWebRoot() . 'modules/contrib/alpha/composer.json';
// Assert the module was updated.
$this->assertFileEquals(
__DIR__ . '/../../../../package_manager/tests/fixtures/build_test_projects/alpha/1.1.0/composer.json',
$updated_composer_json,
);
$this->assertRequestedChangesWereLogged(['Update drupal/alpha from 1.0.0 to 1.1.0']);
$this->assertAppliedChangesWereLogged(['Updated drupal/alpha from 1.0.0 to 1.1.0']);
}
/**
* Tests updating a module in a staging area via the UI.
* Tests updating a module in a stage directory via the UI.
*/
public function testUi() {
public function testUi(): void {
$this->createTestProject('RecommendedProject');
$mink = $this->getMink();
......@@ -110,26 +113,61 @@ END;
$assert_session = $mink->assertSession();
$this->visit('/admin/reports/updates');
// Confirm that 'New Module' project, which is not installed via Composer,
// has a 1.1.0 update release on the 'Available Updates' page.
$this->assertReportProjectUpdateVersion('New Module', '1.1.0');
$page->clickLink('Update Extensions');
$this->assertUpdateTableRow($assert_session, 'Alpha', '1.0.0', '1.1.0', 2);
$this->assertUpdateTableRow($assert_session, 'New module', '1.0.0', '1.1.0', 1);
$page->checkField('projects[new_module]');
$page->pressButton('Update');
$this->waitForBatchJob();
$page_text = $page->getText();
// There will be error in updating 'new_module' as it is not installed via
// composer @see \Drupal\Tests\automatic_updates_extensions\Kernel\Validator\PackagesInstalledWithComposerValidatorTest.
$this->assertStringContainsString('Automatic Updates can only update projects that were installed via Composer. The following packages are not installed through composer:', $page_text);
$this->assertStringContainsString('new_module', $page_text);
$page->clickLink('error page');
$this->assertUpdateTableRow($assert_session, 'Alpha', '1.0.0', '1.1.0', 1);
// Confirm that a 'New Module' project does not appear on the form.
$assert_session->pageTextContains('Other updates were found, but they must be performed manually.');
$assert_session->fieldNotExists('projects[new_module]');
// Ensure test failures provide helpful debug output when failing readiness
// checks prevent updates.
// @see \Drupal\Tests\WebAssert::buildStatusMessageSelector()
if ($error_message = $session->getPage()->find('xpath', '//div[@data-drupal-messages]//div[@aria-label="Error message"]')) {
/** @var \Behat\Mink\Element\NodeElement $error_message */
$this->assertSame('', $error_message->getText());
}
$page->checkField('projects[alpha]');
$page->pressButton('Update');
$this->waitForBatchJob();
$assert_session->pageTextContains('Ready to update');
$page->checkField('backup');
$page->pressButton('Continue');
$this->waitForBatchJob();
$assert_session->pageTextContains('Update complete!');
$this->assertModuleVersion('alpha', '1.1.0');
$this->assertRequestedChangesWereLogged(['Update drupal/alpha from 1.0.0 to 1.1.0']);
$this->assertAppliedChangesWereLogged(['Updated drupal/alpha from 1.0.0 to 1.1.0']);
}
/**
* {@inheritdoc}
*/
public function copyCodebase(\Iterator $iterator = NULL, $working_dir = NULL): void {
parent::copyCodebase($iterator, $working_dir);
// Ensure that we will install Drupal 9.8.0 (a fake version that should
// never exist in real life) initially.
$this->setUpstreamCoreVersion('9.8.0');
}
/**
* Assert a project version is on the Available Updates page.
*
* @param string $project_title
* The project title.
* @param string $expected_version
* The expected version.
*/
protected function assertReportProjectUpdateVersion(string $project_title, string $expected_version): void {
$mink = $this->getMink();
$session = $mink->getSession();
$title_element = $session->getPage()
->find('css', ".project-update__title:contains(\"$project_title\")");
$this->assertNotNull($title_element, "Title element found for $project_title");
$version_element = $title_element->getParent()->find('css', '.project-update__version-details');
$this->assertStringContainsString($expected_version, $version_element->getText());
}
/**
......@@ -140,7 +178,7 @@ END;
* @param string $version
* The expected version.
*/
private function assertModuleVersion(string $module_name, string $version) {
private function assertModuleVersion(string $module_name, string $version): void {
$web_root = $this->getWebRoot();
$composer_json = file_get_contents("$web_root/modules/contrib/$module_name/composer.json");
$data = json_decode($composer_json, TRUE);
......
<?php
declare(strict_types=1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\package_manager_bypass\LoggingBeginner;
use Drupal\package_manager_bypass\LoggingCommitter;
use Drupal\package_manager_bypass\NoOpStager;
use PhpTuf\ComposerStager\API\Exception\InvalidArgumentException;
use PhpTuf\ComposerStager\API\Exception\LogicException;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
class ComposerStagerOperationFailureTest extends UpdaterFormTestBase {
/**
* Tests Composer operation failure is handled properly.
*
* @param string $exception_class
* The exception class.
* @param string $service_class
* The Composer Stager service which should throw an exception.
*
* @dataProvider providerComposerOperationFailure
*/
public function testComposerOperationFailure(string $exception_class, string $service_class): void {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$this->setReleaseMetadata(__DIR__ . '/../../../../package_manager/tests/fixtures/release-history/drupal.9.8.2.xml');
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
// Navigate to the update form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates(
'Semver Test',
'8.1.0',
'8.1.1',
);
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test_package_name', '8.1.1');
$this->assertUpdatesCount(1);
$page->checkField('projects[semver_test]');
// Make the specified Composer Stager operation class throw an exception.
$message = $this->createComposeStagerMessage("Failure from inside $service_class");
$exception = new $exception_class($message);
call_user_func([$service_class, 'setException'], $exception);
// Start the update.
$page->pressButton('Update');
$this->checkForMetaRefresh();
// We can't continue the update after an error in the committer.
if ($service_class === LoggingCommitter::class) {
$this->acceptWarningAndUpdate();
$this->clickLink('the error page');
$assert_session->statusCodeEquals(200);
$assert_session->statusMessageContains('Automatic updates failed to apply, and the site is in an indeterminate state. Consider restoring the code and database from a backup.');
return;
}
$this->clickLink('the error page');
$assert_session->statusMessageContains($exception->getMessage());
// Make the same Composer Stager operation class NOT throw an exception.
call_user_func([$service_class, 'setException'], NULL);
// Stage should be automatically deleted when an error occurs.
$assert_session->buttonNotExists('Delete existing update');
// This ensures that we can still update after the exception no longer
// exists.
$page->checkField('projects[semver_test]');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test_package_name', '8.1.1');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->acceptWarningAndUpdate();
$assert_session->statusMessageContains('Update complete!');
}
/**
* Data provider for testComposerOperationFailure().
*
* @return string[][]
* The test cases.
*/
public function providerComposerOperationFailure(): array {
return [
'LogicException from Beginner' => [LogicException::class, LoggingBeginner::class],
'LogicException from Stager' => [LogicException::class, NoOpStager::class],
'InvalidArgumentException from Stager' => [InvalidArgumentException::class, NoOpStager::class],
'LogicException from Committer' => [LogicException::class, LoggingCommitter::class],
];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class DisplayUpdatesTest extends UpdaterFormTestBase {
/**
* Data provider for testDisplayUpdates().
*
* @return array[]
* The test cases.
*/
public function providerDisplayUpdates(): array {
return [
'with unrequested updates' => [TRUE],
'without unrequested updates' => [FALSE],
];
}
/**
* Tests the form displays the correct projects which will be updated.
*
* @param bool $unrequested_updates
* Whether unrequested updates are present during update.
*
* @dataProvider providerDisplayUpdates
*/
public function testDisplayUpdates(bool $unrequested_updates): void {
$this->container->get('theme_installer')->install(['automatic_updates_theme_with_updates']);
$this->setReleaseMetadata(__DIR__ . '/../../../../package_manager/tests/fixtures/release-history/drupal.9.8.2.xml');
$this->setReleaseMetadata(__DIR__ . "/../../fixtures/release-history/semver_test.1.1.xml");
$this->setReleaseMetadata(__DIR__ . "/../../../../package_manager/tests/fixtures/release-history/aaa_update_test.1.1.xml");
$this->setProjectInstalledVersion([
'semver_test' => '8.1.0',
'aaa_update_test' => '8.x-2.0',
]);
$this->checkForUpdates();
$state = $this->container->get('state');
$page = $this->getSession()->getPage();
// Navigate to the automatic updates form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates(
'AAA Update test',
'8.x-2.0',
'8.x-2.1',
);
$this->assertTableShowsUpdates(
'Semver Test',
'8.1.0',
'8.1.1',
2
);
// User will choose both the projects to update and there will be no
// unrequested updates.
if ($unrequested_updates === FALSE) {
$page->checkField('projects[aaa_update_test]');
}
$page->checkField('projects[semver_test]');
$this->getStageFixtureManipulator()
->setVersion('drupal/aaa_update_test', '2.1.0')
->setVersion('drupal/semver_test_package_name', '8.1.1');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
$assert_session = $this->assertSession();
// Both projects will be shown as requested updates if there are no
// unrequested updates, otherwise one project which user chose will be shown
// as requested update and other one will be shown as unrequested update.
if ($unrequested_updates === FALSE) {
$assert_session->pageTextNotContains('The following dependencies will also be updated:');
}
else {
$assert_session->pageTextContains('The following dependencies will also be updated:');
}
$assert_session->pageTextContains('The following projects will be updated:');
$assert_session->pageTextContains('Semver Test from 8.1.0 to 8.1.1');
$assert_session->pageTextContains('AAA Update test from 2.0.0 to 2.1.0');
}
}
<?php
declare(strict_types=1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\fixture_manipulator\ActiveFixtureManipulator;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class PreUpdateTest extends UpdaterFormTestBase {
/**
* Tests the form when modules requiring an update not installed via composer.
*/
public function testNonComposerProjects(): void {
$this->setReleaseMetadata(__DIR__ . '/../../../../package_manager/tests/fixtures/release-history/aaa_update_test.1.1.xml');
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$this->config('update.settings')
->set('fetch.url', $this->baseUrl . '/test-release-history')
->save();
$this->setProjectInstalledVersion(
[
'aaa_update_test' => '8.x-2.0',
'semver_test' => '8.1.0',
]
);
// One module not installed through composer.
(new ActiveFixtureManipulator())
->removePackage('drupal/aaa_update_test')
->commitChanges();
$assert = $this->assertSession();
$user = $this->createUser(
[
'administer site configuration',
'administer software updates',
]
);
$this->drupalLogin($user);
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$assert->pageTextContains('Other updates were found, but they must be performed manually. See the list of available updates for more information.');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->assertUpdatesCount(1);
// Both of the modules not installed through composer.
(new ActiveFixtureManipulator())
->removePackage('drupal/semver_test_package_name')
->commitChanges();
$this->getSession()->reload();
$assert->pageTextContains('Updates were found, but they must be performed manually. See the list of available updates for more information.');
$this->assertNoUpdates();
}
/**
* Tests the form when a module requires an update.
*/
public function testHasUpdate(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$assert = $this->assertSession();
$user = $this->createUser(['administer site configuration']);
$this->drupalLogin($user);
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$assert->pageTextContains('Access Denied');
$assert->pageTextNotContains('Automatic Updates Form');
$user = $this->createUser(['administer software updates', 'administer site configuration']);
$this->drupalLogin($user);
$this->checkForUpdates();
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->assertUpdatesCount(1);
$assert->pageTextContains('Automatic Updates Form');
$assert->buttonExists('Update');
}
/**
* Tests the form when there are no available updates.
*/
public function testNoUpdate(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$user = $this->createUser([
'administer site configuration',
'administer software updates',
]);
$this->drupalLogin($user);
$this->setProjectInstalledVersion(['semver_test' => '8.1.1']);
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->assertNoUpdates();
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\ValidationResult;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class StatusCheckerRunAfterUpdateTest extends UpdaterFormTestBase {
/**
* Data provider for testStatusCheckerRunAfterUpdate().
*
* @return bool[][]
* The test cases.
*/
public function providerStatusCheckerRunAfterUpdate(): array {
return [
'has database updates' => [TRUE],
'does not have database updates' => [FALSE],
];
}
/**
* Tests status checks are run after an update.
*
* @param bool $has_database_updates
* Whether the site has database updates or not.
*
* @dataProvider providerStatusCheckerRunAfterUpdate
*/
public function testStatusCheckerRunAfterUpdate(bool $has_database_updates): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
$page = $this->getSession()->getPage();
// Navigate to the automatic updates form.
$this->drupalGet('/admin/modules/automatic-update-extensions');
$assert_session = $this->assertSession();
$assert_session->pageTextNotContains(static::$errorsExplanation);
$assert_session->pageTextNotContains(static::$warningsExplanation);
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test_package_name', '8.1.1');
$this->assertUpdatesCount(1);
$page->checkField('projects[semver_test]');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
// Set an error before completing the update. This error should be visible
// on admin pages after completing the update without having to explicitly
// run the status checks.
TestSubscriber1::setTestResult([ValidationResult::createError([t('Error before continue.')])], StatusCheckEvent::class);
if ($has_database_updates) {
// Simulate a staged database update in the automatic_updates_test module.
// We must do this after the update has started, because the pending
// updates validator will prevent an update from starting.
$this->container->get('state')->set('automatic_updates_test.new_update', TRUE);
$this->acceptWarningAndUpdate();
$assert_session->pageTextContainsOnce('An error has occurred.');
$assert_session->pageTextContainsOnce('Continue to the error page');
$page->clickLink('the error page');
$assert_session->pageTextContains('Some modules have database schema updates to install. You should run the database update script immediately.');
$assert_session->linkExists('database update script');
$assert_session->linkByHrefExists('/update.php');
$page->clickLink('database update script');
$this->assertSession()->addressEquals('/update.php');
$assert_session->pageTextNotContains('Possible database updates have been detected in the following extension');
$page->clickLink('Continue');
// @see automatic_updates_update_1191934()
$assert_session->pageTextContains('Dynamic automatic_updates_update_1191934');
$page->clickLink('Apply pending updates');
$this->checkForMetaRefresh();
$assert_session->pageTextContains('Updates were attempted.');
}
else {
$this->acceptWarningAndUpdate();
$assert_session->addressEquals('/admin/reports/updates');
$assert_session->pageTextContainsOnce('Update complete!');
}
// Status checks should display errors on admin page.
$this->drupalGet('/admin');
// Confirm that the status checks were run and the new error is displayed.
$assert_session->statusMessageContains('Error before continue.', 'error');
$assert_session->statusMessageContains('Your site does not pass some readiness checks for automatic updates. It cannot be automatically updated until further action is performed.', 'error');
$assert_session->pageTextNotContains('Your site has not recently run an update readiness check. Rerun readiness checks now.');
}
}
<?php
declare(strict_types=1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\package_manager_test_validation\StagedDatabaseUpdateValidator;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class SuccessfulUpdateTest extends UpdaterFormTestBase {
/**
* Data provider for testSuccessfulUpdate().
*
* @return mixed[][]
* The test cases.
*/
public function providerSuccessfulUpdate(): array {
return [
'maintenance mode on, semver module' => [
TRUE, 'semver_test', 'drupal/semver_test_package_name', 'Semver Test', '8.1.0', '8.1.1',
],
'maintenance mode off, legacy module' => [
FALSE, 'aaa_update_test', 'drupal/aaa_update_test', 'AAA Update test', '8.x-2.0', '8.x-2.1',
],
'maintenance mode off, legacy theme' => [
FALSE, 'automatic_updates_extensions_test_theme', 'drupal/automatic_updates_extensions_test_theme', 'Automatic Updates Extensions Test Theme', '8.x-2.0', '8.x-2.1',
],
];
}
/**
* Tests an update that has no errors or special conditions.
*
* @param bool $maintenance_mode_on
* Whether maintenance should be on at the beginning of the update.
* @param string $project_name
* The project name.
* @param string $package_name
* The package name.
* @param string $project_title
* The project title.
* @param string $installed_version
* The installed version.
* @param string $target_version
* The target version.
*
* @dataProvider providerSuccessfulUpdate
*/
public function testSuccessfulUpdate(bool $maintenance_mode_on, string $project_name, string $package_name, string $project_title, string $installed_version, string $target_version): void {
$this->container->get('theme_installer')->install(['automatic_updates_theme_with_updates']);
// By default, the Update module only checks for updates of installed
// modules and themes. The two modules we're testing here (semver_test and
// aaa_update_test) are already installed by static::$modules.
$this->container->get('theme_installer')->install(['automatic_updates_extensions_test_theme']);
$this->setReleaseMetadata(__DIR__ . '/../../../../package_manager/tests/fixtures/release-history/drupal.9.8.2.xml');
$path_to_fixtures_folder = $project_name === 'aaa_update_test' ? '/../../../../package_manager/tests' : '/../..';
$this->setReleaseMetadata(__DIR__ . $path_to_fixtures_folder . '/fixtures/release-history/' . $project_name . '.1.1.xml');
$this->setProjectInstalledVersion([$project_name => $installed_version]);
$this->getStageFixtureManipulator()->setVersion($package_name, LegacyVersionUtility::convertToSemanticVersion($target_version));
$this->checkForUpdates();
$state = $this->container->get('state');
$state->set('system.maintenance_mode', $maintenance_mode_on);
StagedDatabaseUpdateValidator::setExtensionsWithUpdates([
'system',
'automatic_updates_theme_with_updates',
]);
$page = $this->getSession()->getPage();
// Navigate to the automatic updates form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates(
$project_title,
$installed_version,
$target_version
);
$assert_session = $this->assertSession();
$this->assertUpdatesCount(1);
// Submit without selecting a project.
$page->pressButton('Update');
$assert_session->pageTextContains('Select one or more projects.');
// Submit with a project selected.
$page->checkField('projects[' . $project_name . ']');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
// Confirm that the site was put into maintenance mode if needed.
$this->assertSame($state->get('system.maintenance_mode'), $maintenance_mode_on);
$assert_session->pageTextNotContains('The following dependencies will also be updated:');
// Ensure that a list of pending database updates is visible, along with a
// short explanation, in the warning messages.
$warning_messages = $assert_session->elementExists('xpath', '//div[@data-drupal-messages]//div[@aria-label="Warning message"]');
$this->assertStringContainsString('Database updates have been detected in the following extensions.<ul><li>System</li><li>Automatic Updates Theme With Updates</li></ul>', $warning_messages->getHtml());
$this->acceptWarningAndUpdate();
$assert_session->addressEquals('/admin/reports/updates');
// Confirm that the site was in maintenance before the update was applied.
// @see \Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber::handleEvent()
$this->assertTrue($state->get(PreApplyEvent::class . '.system.maintenance_mode'));
$assert_session->pageTextContainsOnce('Update complete!');
// Confirm the site was returned to the original maintenance mode state.
$this->assertSame($state->get('system.maintenance_mode'), $maintenance_mode_on);
// Confirm that the apply and post-apply operations happened in
// separate requests.
// @see \Drupal\automatic_updates_test\EventSubscriber\RequestTimeRecorder
$pre_apply_time = $state->get('Drupal\package_manager\Event\PreApplyEvent time');
$post_apply_time = $state->get('Drupal\package_manager\Event\PostApplyEvent time');
$this->assertNotEmpty($pre_apply_time);
$this->assertNotEmpty($post_apply_time);
$this->assertNotSame($pre_apply_time, $post_apply_time);
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\ValidationResult;
use Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class UnsuccessfulUpdateTest extends UpdaterFormTestBase {
/**
* Test the form for warning messages.
*/
public function testWarnings(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$assert = $this->assertSession();
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
$message = t("Warning! Updating this module may cause an error.");
$warning = ValidationResult::createWarning([$message]);
TestSubscriber1::setTestResult([$warning], StatusCheckEvent::class);
// Navigate to the automatic updates form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test_package_name', '8.1.1');
$this->assertUpdatesCount(1);
$this->checkForMetaRefresh();
$assert->pageTextNotContains(static::$errorsExplanation);
$assert->elementExists('css', '#edit-projects-semver-test')->check();
$assert->checkboxChecked('edit-projects-semver-test');
// No explanation is given for warnings.
$assert->pageTextNotContains(static::$warningsExplanation);
$assert->buttonExists('Update');
// Add warnings from StatusCheckEvent.
$summary_status_check_event = t('Some summary');
$messages_status_check_event = [
t("The only thing we're allowed to do is to"),
t("believe that we won't regret the choice"),
t("we made."),
];
$warning_status_check_event = ValidationResult::createWarning($messages_status_check_event, $summary_status_check_event);
TestSubscriber::setTestResult([$warning_status_check_event], StatusCheckEvent::class);
$this->getSession()->getPage()->pressButton('Update');
$this->checkForMetaRefresh();
$assert->buttonExists('Continue');
$assert->pageTextContains((string) $summary_status_check_event);
foreach ($messages_status_check_event as $message) {
$assert->pageTextContains((string) $message);
}
}
/**
* Tests the form when an uninstallable module requires an update.
*/
public function testUninstallableRelease(): void {
$this->container->get('state')->set('testUninstallableRelease', TRUE);
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$user = $this->createUser(['administer software updates', 'administer site configuration']);
$this->drupalLogin($user);
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->checkForUpdates();
$this->assertNoUpdates();
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\ValidationResult;
use Drupal\package_manager_bypass\LoggingCommitter;
use Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber;
/**
* @covers \Drupal\automatic_updates_extensions\Form\UpdaterForm
* @group automatic_updates_extensions
* @internal
*/
final class UpdateErrorTest extends UpdaterFormTestBase {
/**
* Tests that an exception is thrown if a previous apply failed.
*/
public function testMarkerFileFailure(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
$page = $this->getSession()->getPage();
// Navigate to the automatic updates form.
$this->drupalGet('/admin/modules/automatic-update-extensions');
$assert_session = $this->assertSession();
$assert_session->pageTextNotContains(static::$errorsExplanation);
$assert_session->pageTextNotContains(static::$warningsExplanation);
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test_package_name', '8.1.1');
$this->assertUpdatesCount(1);
$page->checkField('projects[semver_test]');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
$assert_session->pageTextNotContains('The following dependencies will also be updated:');
LoggingCommitter::setException(new \Exception('failed at committer'));
$this->acceptWarningAndUpdate();
$failure_message = 'Automatic updates failed to apply, and the site is in an indeterminate state. Consider restoring the code and database from a backup.';
$assert_session->pageTextContainsOnce('An error has occurred.');
$assert_session->pageTextContains($failure_message);
$page->clickLink('the error page');
// We should be on the form (i.e., 200 response code), but unable to
// continue the update.
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains($failure_message);
$assert_session->buttonNotExists('Continue');
// The same thing should be true if we try to start from the beginning.
$this->drupalGet('/admin/modules/automatic-update-extensions');
$assert_session->statusCodeEquals(200);
$assert_session->pageTextContains($failure_message);
$assert_session->buttonNotExists('Update');
}
/**
* Test the form for errors.
*/
public function testErrors(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$assert = $this->assertSession();
$user = $this->createUser([
'administer site configuration',
'administer software updates',
]);
$this->drupalLogin($user);
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->assertUpdatesCount(1);
$message = t("You've not experienced Shakespeare until you have read him in the original Klingon.");
$error = ValidationResult::createError([$message]);
TestSubscriber1::setTestResult([$error], StatusCheckEvent::class);
$this->getSession()->reload();
$assert->pageTextContains((string) $message);
$assert->pageTextContains(static::$errorsExplanation);
$assert->pageTextNotContains(static::$warningsExplanation);
$assert->buttonNotExists('Update');
}
/**
* Tests that messages from StatusCheckEvent are shown on the confirmation form.
*/
public function testStatusErrorMessages(): void {
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$assert = $this->assertSession();
$this->setProjectInstalledVersion(['semver_test' => '8.1.0']);
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->assertUpdatesCount(1);
$this->getSession()->reload();
$assert->elementExists('css', '#edit-projects-semver-test')->check();
$assert->checkboxChecked('edit-projects-semver-test');
$assert->buttonExists('Update');
$messages = [
t("The only thing we're allowed to do is to"),
t("believe that we won't regret the choice"),
t("we made."),
];
$summary = t('Some summary');
$error = ValidationResult::createError($messages, $summary);
TestSubscriber::setTestResult([$error], StatusCheckEvent::class);
$this->getSession()->getPage()->pressButton('Update');
$this->checkForMetaRefresh();
$assert->pageTextContains(static::$errorsExplanation);
$assert->pageTextNotContains(static::$warningsExplanation);
$assert->pageTextContains((string) $summary);
foreach ($messages as $message) {
$assert->pageTextContains((string) $message);
}
$assert->buttonNotExists('Continue');
}
}
<?php
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\automatic_updates\Event\ReadinessCheckEvent;
use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\ValidationResult;
use Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase;
use Drupal\Tests\automatic_updates\Traits\ValidationTestTrait;
use Drupal\Tests\automatic_updates_extensions\Traits\FormTestTrait;
use Drupal\Tests\package_manager\Traits\PackageManagerBypassTestTrait;
/**
* Tests updating using the form.
*
* @group automatic_updates_extensions
*/
class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
use ValidationTestTrait;
use FormTestTrait;
use PackageManagerBypassTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $modules = [
'automatic_updates_test',
'automatic_updates_extensions',
'block',
'semver_test',
'aaa_update_test',
];
/**
* Project to test updates.
*
* @var string
*/
protected $updateProject = 'semver_test';
/**
* Data provider for testSuccessfulUpdate().
*
* @return array[]
* The test cases.
*/
public function providerSuccessfulUpdate() {
return [
'maintiance_mode_on, semver' => [TRUE, 'semver_test', '8.1.0', '8.1.1'],
'maintiance_mode_off, legacy' => [FALSE, 'aaa_update_test', '8.x-2.0', '8.x-2.1'],
];
}
/**
* {@inheritdoc}
*/
protected function setUp(): void {
// In this test class, some modules are added and this validator will
// complain because these are not installed via composer. This validator
// already has test coverage.
// @see \Drupal\Tests\automatic_updates_extensions\Build\ModuleUpdateTest
$this->disableValidators[] = 'automatic_updates_extensions.validator.packages_installed_with_composer';
parent::setUp();
$this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml');
$user = $this->createUser([
'administer site configuration',
'administer software updates',
'access site in maintenance mode',
]);
$this->drupalLogin($user);
$this->drupalPlaceBlock('local_tasks_block', ['primary' => TRUE]);
}
/**
* Sets installed project version.
*
* @todo This is copied from core. We need to file a core issue so we do not
* have to copy this.
*/
protected function setProjectInstalledVersion($version) {
$this->config('update.settings')
->set('fetch.url', $this->baseUrl . '/test-release-history')
->save();
$system_info = [
$this->updateProject => [
'project' => $this->updateProject,
'version' => $version,
'hidden' => FALSE,
],
// Ensure Drupal core on the same version for all test runs.
'drupal' => [
'project' => 'drupal',
'version' => '8.0.0',
'hidden' => FALSE,
],
];
$this->config('update_test.settings')
->set('system_info', $system_info)
->save();
}
/**
* Asserts the table shows the updates.
*
* @param string $expected_project_title
* The expected project title.
* @param string $expected_installed_version
* The expected installed version.
* @param string $expected_target_version
* The expected target version.
*/
private function assertTableShowsUpdates(string $expected_project_title, string $expected_installed_version, string $expected_target_version): void {
$this->assertUpdateTableRow($this->assertSession(), $expected_project_title, $expected_installed_version, $expected_target_version);
}
/**
* Tests an update that has no errors or special conditions.
*
* @param bool $maintenance_mode_on
* Whether maintenance should be on at the beginning of the update.
* @param string $project_name
* The project name.
* @param string $installed_version
* The installed version.
* @param string $target_version
* The target version.
*
* @dataProvider providerSuccessfulUpdate
*/
public function testSuccessfulUpdate(bool $maintenance_mode_on, string $project_name, string $installed_version, string $target_version): void {
// Disable the scaffold file permissions validator because it will try to
// read composer.json from the staging area, which won't exist because
// Package Manager is bypassed.
$this->disableValidators(['automatic_updates.validator.scaffold_file_permissions']);
$this->updateProject = $project_name;
$this->setReleaseMetadata(__DIR__ . '/../../../../tests/fixtures/release-history/drupal.9.8.2.xml');
$this->setReleaseMetadata(__DIR__ . "/../../fixtures/release-history/$project_name.1.1.xml");
$this->setProjectInstalledVersion($installed_version);
$this->checkForUpdates();
$state = $this->container->get('state');
$state->set('system.maintenance_mode', $maintenance_mode_on);
$page = $this->getSession()->getPage();
// Navigate to the automatic updates form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates(
$project_name === 'semver_test' ? 'Semver Test' : 'AAA Update test',
$installed_version,
$target_version
);
$page->checkField('projects[' . $this->updateProject . ']');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
// Confirm that the site was put into maintenance mode if needed.
$this->assertSame($state->get('system.maintenance_mode'), $maintenance_mode_on);
$page->pressButton('Continue');
$this->checkForMetaRefresh();
$assert_session = $this->assertSession();
$assert_session->addressEquals('/admin/reports/updates');
// Confirm that the site was in maintenance before the update was applied.
// @see \Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber::handleEvent()
$this->assertTrue($state->get(PreApplyEvent::class . '.system.maintenance_mode'));
$assert_session->pageTextContainsOnce('Update complete!');
// Confirm the site was returned to the original maintenance mode state.
$this->assertSame($state->get('system.maintenance_mode'), $maintenance_mode_on);
}
/**
* Tests the form when a module requires an update.
*/
public function testHasUpdate(): void {
$assert = $this->assertSession();
$user = $this->createUser(['administer site configuration']);
$this->drupalLogin($user);
$this->setProjectInstalledVersion('8.1.0');
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$assert->pageTextContains('Access Denied');
$assert->pageTextNotContains('Automatic Updates Form');
$user = $this->createUser(['administer software updates']);
$this->drupalLogin($user);
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$assert->pageTextContains('Automatic Updates Form');
$assert->buttonExists('Update');
}
/**
* Tests the form when there are no available updates.
*/
public function testNoUpdate(): void {
$assert = $this->assertSession();
$user = $this->createUser([
'administer site configuration',
'administer software updates',
]);
$this->drupalLogin($user);
$this->setProjectInstalledVersion('8.1.1');
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$assert->pageTextContains('There are no available updates.');
$assert->buttonNotExists('Update');
}
/**
* Test the form for errors.
*/
public function testErrors(): void {
$assert = $this->assertSession();
$user = $this->createUser([
'administer site configuration',
'administer software updates',
]);
$this->drupalLogin($user);
$this->setProjectInstalledVersion('8.1.0');
$this->checkForUpdates();
$this->drupalGet('admin/reports/updates/automatic-update-extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$message = t("You've not experienced Shakespeare until you have read him in the original Klingon.");
$error = ValidationResult::createError([$message]);
TestSubscriber1::setTestResult([$error], ReadinessCheckEvent::class);
$this->getSession()->reload();
$assert->pageTextContains($message);
$assert->pageTextContains(static::$errorsExplanation);
$assert->pageTextNotContains(static::$warningsExplanation);
$assert->buttonNotExists('Update');
}
/**
* Test the form for warning messages.
*/
public function testWarnings(): void {
$assert = $this->assertSession();
$this->setProjectInstalledVersion('8.1.0');
$this->checkForUpdates();
$message = t("Warning! Updating this module may cause an error.");
$warning = ValidationResult::createWarning([$message]);
TestSubscriber1::setTestResult([$warning], ReadinessCheckEvent::class);
// Navigate to the automatic updates form.
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$assert->pageTextContains(static::$warningsExplanation);
$assert->pageTextNotContains(static::$errorsExplanation);
$assert->buttonExists('Update');
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\fixture_manipulator\ActiveFixtureManipulator;
use Drupal\package_manager\PathLocator;
use Drupal\Tests\automatic_updates_extensions\Traits\FormTestTrait;
use Drupal\Tests\automatic_updates\Functional\UpdaterFormTestBase as UpdaterFormFunctionalTestBase;
/**
* Base class for functional tests of updater form.
*
* @internal
* This class is an internal part of the module's testing infrastructure and
* should not be used by external code.
*/
abstract class UpdaterFormTestBase extends UpdaterFormFunctionalTestBase {
use FormTestTrait;
/**
* {@inheritdoc}
*/
protected static $errorsExplanation = 'Your site cannot be automatically updated until further action is performed.';
/**
* The path of the test project's active directory.
*
* @var string
*/
private $activeDir;
/**
* {@inheritdoc}
*/
protected static $modules = [
'automatic_updates_extensions',
'semver_test',
'aaa_update_test',
'automatic_updates_extensions_test',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->activeDir = $this->container->get(PathLocator::class)->getProjectRoot();
(new ActiveFixtureManipulator())
// Add a package where the Composer package name,
// drupal/semver_test_package_name, does not match the project name,
// semver_test.
->addPackage(
[
'name' => 'drupal/semver_test_package_name',
'version' => '8.1.0',
'type' => 'drupal-module',
],
extra_files: [
'semver_test.info.yml' => '{name: "Semver Test", project: "semver_test", type: "module"}',
]
)
->addPackage([
'name' => 'drupal/aaa_update_test',
'version' => '2.0.0',
'type' => 'drupal-module',
])
->addPackage([
'name' => 'drupal/automatic_updates_extensions_test_theme',
'version' => '2.0.0',
'type' => 'drupal-theme',
])
->commitChanges();
$this->drupalPlaceBlock('local_tasks_block', ['primary' => TRUE]);
}
/**
* Sets installed project version.
*
* @todo Remove this function with use of the trait from the Update module in
* https://drupal.org/i/3348234.
*/
protected function setProjectInstalledVersion($project_versions): void {
$this->config('update.settings')
->set('fetch.url', $this->baseUrl . '/test-release-history')
->save();
$system_info = [];
foreach ($project_versions as $project_name => $version) {
$system_info[$project_name] = [
'project' => $project_name,
'version' => $version,
'hidden' => FALSE,
];
}
$system_info['drupal'] = [
'project' => 'drupal',
'version' => '8.0.0',
'hidden' => FALSE,
];
$this->config('update_test.settings')
->set('system_info', $system_info)
->save();
}
/**
* Asserts the table shows the updates.
*
* @param string $expected_project_title
* The expected project title.
* @param string $expected_installed_version
* The expected installed version.
* @param string $expected_target_version
* The expected target version.
* @param int $row
* The row number.
*/
protected function assertTableShowsUpdates(string $expected_project_title, string $expected_installed_version, string $expected_target_version, int $row = 1): void {
$this->assertUpdateTableRow($this->assertSession(), $expected_project_title, $expected_installed_version, $expected_target_version, $row);
}
/**
* Asserts the form shows no updates.
*/
protected function assertNoUpdates(): void {
$assert = $this->assertSession();
$assert->buttonNotExists('Update');
$assert->pageTextContains('There are no available updates.');
}
/**
* {@inheritdoc}
*/
protected function checkForUpdates(): void {
$this->drupalGet('/admin/modules/automatic-update-extensions');
$this->clickLink('Check manually');
$this->checkForMetaRefresh();
}
/**
* Continue the update after checking the backup warning checkbox.
*/
protected function acceptWarningAndUpdate(): void {
$page = $this->getSession()->getPage();
$page->pressButton('Continue');
$this->assertSession()->statusMessageContains('Warning: Updating contributed modules or themes may leave your site inoperable or looking wrong. field is required.', 'error');
$page->checkField('backup');
$page->pressButton('Continue');
$this->checkForMetaRefresh();
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates_extensions\Kernel;
use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\automatic_updates_extensions\Validator\UpdateReleaseValidator;
use Drupal\Tests\user\Traits\UserCreationTrait;
use PhpTuf\ComposerStager\API\Core\StagerInterface;
/**
* @coversDefaultClass \Drupal\automatic_updates_extensions\ExtensionUpdater
*
* @coversDefaultClass \Drupal\automatic_updates_extensions\ExtensionUpdateStage
* @group automatic_updates_extensions
* @internal
*/
class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
final class ExtensionUpdateStageTest extends AutomaticUpdatesExtensionsKernelTestBase {
use UserCreationTrait;
......@@ -31,40 +35,35 @@ class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
// This test doesn't need to validate that the test projects used are in the
// codebase. Therefore, we need to disable the following validators that
// require real Drupal projects.
$this->disableValidators[] = 'automatic_updates_extensions.validator.target_release';
$this->disableValidators[] = 'automatic_updates_extensions.validator.packages_type';
$this->disableValidators[] = UpdateReleaseValidator::class;
parent::setUp();
$this->installEntitySchema('user');
// Create a user who will own the stage even after the container is rebuilt.
$user = $this->createUser([], NULL, TRUE, ['uid' => 2]);
$this->setCurrentUser($user);
}
/**
* Tests that correct versions are staged after calling ::begin().
*/
public function testCorrectVersionsStaged(): void {
// Create a user who will own the stage even after the container is rebuilt.
$user = $this->createUser([], NULL, TRUE, ['uid' => 2]);
$this->setCurrentUser($user);
$fixture_dir = __DIR__ . '/../../fixtures/fake-site';
$locator = $this->mockPathLocator($fixture_dir, $fixture_dir);
$id = $this->container->get('automatic_updates_extensions.updater')->begin([
$id = $this->container->get(ExtensionUpdateStage::class)->begin([
'my_module' => '9.8.1',
// Use a legacy version number to ensure they are converted to semantic
// version numbers which will work with the drupal.org Composer facade.
'my_dev_module' => '8.x-1.2-alpha1',
'my_dev_module' => '8.x-1.2-alpha1@alpha',
]);
$user = $this->container->get('current_user')->getAccount();
// Rebuild the container to ensure the package versions are persisted.
/** @var \Drupal\Core\DrupalKernel $kernel */
$kernel = $this->container->get('kernel');
$kernel->rebuildContainer();
$this->container = $kernel->getContainer();
// Keep using the mocked path locator and current user.
$this->container->set('package_manager.path_locator', $locator);
// Keep using the user account we created.
$this->setCurrentUser($user);
$extension_updater = $this->container->get('automatic_updates_extensions.updater');
$stage = $this->container->get(ExtensionUpdateStage::class);
// Ensure that the target package versions are what we expect.
$expected_versions = [
......@@ -72,12 +71,12 @@ class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
'drupal/my_module' => '9.8.1',
],
'dev' => [
'drupal/my_dev_module' => '1.2.0-alpha1',
'drupal/my_dev_module' => '1.2.0-alpha1@alpha',
],
];
$this->assertSame($expected_versions, $extension_updater->claim($id)->getPackageVersions());
$this->assertSame($expected_versions, $stage->claim($id)->getPackageVersions());
// When we call ExtensionUpdater::stage(), the stored project versions
// When we call ExtensionUpdateStage::stage(), the stored project versions
// should be read from state and passed to Composer Stager's Stager service,
// in the form of a Composer command. This is done using
// package_manager_bypass's invocation recorder, rather than a regular mock,
......@@ -96,24 +95,58 @@ class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
'require',
'--dev',
'--no-update',
'drupal/my_dev_module:1.2.0-alpha1',
'drupal/my_dev_module:1.2.0-alpha1@alpha',
],
[
'update',
'--with-all-dependencies',
'--optimize-autoloader',
'drupal/my_module:9.8.1',
'drupal/my_dev_module:1.2.0-alpha1',
'drupal/my_dev_module:1.2.0-alpha1@alpha',
],
];
$extension_updater->stage();
$stage->stage();
$actual_arguments = $this->container->get('package_manager.stager')
$actual_arguments = $this->container->get(StagerInterface::class)
->getInvocationArguments();
$this->assertSame(count($expected_arguments), count($actual_arguments));
$this->assertCount(count($expected_arguments), $actual_arguments);
foreach ($actual_arguments as $i => [$arguments]) {
$this->assertSame($expected_arguments[$i], $arguments);
}
}
/**
* Tests that attempting to update an install profile throws an exception.
*/
public function testUpdatingInstallProfile(): void {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("The project contrib_profile1 cannot be updated because updating install profiles is not supported.");
$this->container->get(ExtensionUpdateStage::class)
->begin([
'contrib_profile1' => '1.1.0',
]);
}
/**
* Tests that an exception is thrown when calling begin() with no projects.
*/
public function testNoProjectsInBegin(): void {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('No projects to begin the update');
$this->container->get(ExtensionUpdateStage::class)->begin([]);
}
/**
* Tests exception if a Drupal project unknown to composer sent to ::begin().
*/
public function testUnknownDrupalProject(): void {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("The project my_module_unknown is not a Drupal project known to Composer and cannot be updated.");
$this->container->get(ExtensionUpdateStage::class)->begin([
'my_module_unknown' => '9.8.1',
]);
}
}