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 1189 additions and 125 deletions
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Checks that the current site is not part of a multisite.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class MultisiteValidator implements PreOperationStageValidatorInterface {
final class MultisiteValidator implements EventSubscriberInterface {
use BaseRequirementValidatorTrait;
use StringTranslationTrait;
/**
* The path locator service.
*
* @var \Drupal\package_manager\PathLocator
*/
protected $pathLocator;
/**
* Constructs a new MultisiteValidator.
*
* @param \Drupal\package_manager\PathLocator $path_locator
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The string translation service.
*/
public function __construct(PathLocator $path_locator, TranslationInterface $translation) {
$this->pathLocator = $path_locator;
$this->setStringTranslation($translation);
public function __construct(private readonly PathLocator $pathLocator) {
}
/**
* {@inheritdoc}
* Validates that the current site is not part of a multisite.
*/
public function validateStagePreOperation(PreOperationStageEvent $event): void {
public function validate(PreOperationStageEvent $event): void {
if ($this->isMultisite()) {
$event->addError([
$this->t('Multisites are not supported by Package Manager.'),
$this->t('Drupal multisite is not supported by Package Manager.'),
]);
}
}
......@@ -51,24 +47,23 @@ class MultisiteValidator implements PreOperationStageValidatorInterface {
*
* @return bool
* TRUE if the current site is part of a multisite, otherwise FALSE.
*
* @todo Make this smarter in https://www.drupal.org/node/3267646.
*/
protected function isMultisite(): bool {
private function isMultisite(): bool {
$web_root = $this->pathLocator->getWebRoot();
if ($web_root) {
$web_root .= '/';
}
return file_exists($this->pathLocator->getProjectRoot() . '/' . $web_root . 'sites/sites.php');
}
$sites_php_path = $this->pathLocator->getProjectRoot() . '/' . $web_root . 'sites/sites.php';
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
PreCreateEvent::class => 'validateStagePreOperation',
];
if (!file_exists($sites_php_path)) {
return FALSE;
}
// @see \Drupal\Core\DrupalKernel::findSitePath()
$sites = [];
include $sites_php_path;
// @see example.sites.php
return count(array_unique($sites)) > 1;
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that newly installed packages don't overwrite existing directories.
*
* Whether a new package in the stage directory would overwrite an existing
* directory in the active directory when the operation is applied is determined
* by inspecting the `path` property of the staged package.
*
* Certain packages, such as those with the `metapackage` type, don't have a
* `path` property and are ignored by this validator. The Composer facade at
* https://packages.drupal.org/8 currently uses the `metapackage` type for
* submodules of Drupal projects.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*
* @link https://getcomposer.org/doc/04-schema.md#type
*/
final class OverwriteExistingPackagesValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a OverwriteExistingPackagesValidator object.
*
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
*/
public function __construct(
private readonly PathLocator $pathLocator,
private readonly ComposerInspector $composerInspector
) {}
/**
* Validates that new installed packages don't overwrite existing directories.
*
* @param \Drupal\package_manager\Event\PreApplyEvent $event
* The event being handled.
*/
public function validate(PreApplyEvent $event): void {
$active_dir = $this->pathLocator->getProjectRoot();
$stage_dir = $event->stage->getStageDirectory();
$active_packages = $this->composerInspector->getInstalledPackagesList($active_dir);
$new_packages = $this->composerInspector->getInstalledPackagesList($stage_dir)
->getPackagesNotIn($active_packages);
foreach ($new_packages as $package) {
if (empty($package->path)) {
// Packages without a `path` cannot overwrite existing directories.
continue;
}
$relative_path = str_replace($stage_dir, '', $package->path);
if (is_dir($active_dir . DIRECTORY_SEPARATOR . $relative_path)) {
$event->addError([
$this->t('The new package @package will be installed in the directory @path, which already exists but is not managed by Composer.', [
'@package' => $package->name,
'@path' => $relative_path,
]),
]);
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
PreApplyEvent::class => 'validate',
];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Update\UpdateRegistry;
use Drupal\Core\Url;
use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that there are no pending database updates.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class PendingUpdatesValidator implements PreOperationStageValidatorInterface {
final class PendingUpdatesValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* The Drupal root.
*
* @var string
*/
protected $appRoot;
/**
* The update registry service.
*
* @var \Drupal\Core\Update\UpdateRegistry
*/
protected $updateRegistry;
/**
* Constructs an PendingUpdatesValidator object.
*
* @param string $app_root
* @param string $appRoot
* The Drupal root.
* @param \Drupal\Core\Update\UpdateRegistry $update_registry
* @param \Drupal\Core\Update\UpdateRegistry $updateRegistry
* The update registry service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The translation service.
*/
public function __construct(string $app_root, UpdateRegistry $update_registry, TranslationInterface $translation) {
$this->appRoot = $app_root;
$this->updateRegistry = $update_registry;
$this->setStringTranslation($translation);
}
public function __construct(
private readonly string $appRoot,
private readonly UpdateRegistry $updateRegistry
) {}
/**
* {@inheritdoc}
* Validates that there are no pending database updates.
*/
public function validateStagePreOperation(PreOperationStageEvent $event): void {
public function validate(PreOperationStageEvent $event): void {
if ($this->updatesExist()) {
$message = $this->t('Some modules have database schema updates to install. You should run the <a href=":update">database update script</a> immediately.', [
':update' => Url::fromRoute('system.db_update')->toString(),
......@@ -79,9 +71,11 @@ class PendingUpdatesValidator implements PreOperationStageValidatorInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
public static function getSubscribedEvents(): array {
return [
PreCreateEvent::class => 'validateStagePreOperation',
PreCreateEvent::class => 'validate',
StatusCheckEvent::class => 'validate',
PreApplyEvent::class => 'validate',
];
}
......
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Component\FileSystem\FileSystem as DrupalFilesystem;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Performs validation if certain PHP extensions are enabled.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class PhpExtensionsValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Indicates if a particular PHP extension is loaded.
*
* @param string $name
* The name of the PHP extension to check for.
*
* @return bool
* TRUE if the given extension is loaded, FALSE otherwise.
*/
final protected function isExtensionLoaded(string $name): bool {
// If and ONLY if we're currently running a test, allow the list of loaded
// extensions to be overridden by a state variable.
if (self::insideTest()) {
// By default, assume OpenSSL is enabled and Xdebug isn't. This allows us
// to run tests in environments that we might not support in production,
// such as Drupal CI.
$loaded_extensions = \Drupal::state()
->get('package_manager_loaded_php_extensions', ['openssl']);
return in_array($name, $loaded_extensions, TRUE);
}
return extension_loaded($name);
}
/**
* Flags a warning if Xdebug is enabled.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* The event object.
*/
public function validateXdebug(StatusCheckEvent $event): void {
if ($this->isExtensionLoaded('xdebug')) {
$event->addWarning([
$this->t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]);
}
}
/**
* Flags an error if the OpenSSL extension is not installed.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
*/
public function validateOpenSsl(PreOperationStageEvent $event): void {
if (!$this->isExtensionLoaded('openssl')) {
$message = $this->t('The OpenSSL extension is not enabled, which is a security risk. See <a href=":url">the PHP documentation</a> for information on how to enable this extension.', [
':url' => 'https://www.php.net/manual/en/openssl.installation.php',
]);
$event->addError([$message]);
}
}
/**
* Whether this validator is running inside a test.
*
* @return bool
*/
private static function insideTest(): bool {
// @see \Drupal\Core\CoreServiceProvider::registerTest()
$in_functional_test = drupal_valid_test_ua();
// @see \Drupal\Core\DependencyInjection\DependencySerializationTrait::__wakeup()
$in_kernel_test = isset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
// @see \Drupal\BuildTests\Framework\BuildTestBase::setUp()
$in_build_test = str_contains(__FILE__, DrupalFilesystem::getOsTemporaryDirectory() . '/build_workspace_');
return $in_functional_test || $in_kernel_test || $in_build_test;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => [
['validateXdebug'],
['validateOpenSsl'],
],
PreCreateEvent::class => ['validateOpenSsl'],
PreApplyEvent::class => ['validateOpenSsl'],
];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\PreRequireEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that PHP-TUF is installed and correctly configured.
*
* In both the active and stage directories, this checks for the following
* conditions:
* - The PHP-TUF plugin is installed.
* - The plugin is not explicitly blocked by Composer's `allow-plugins`
* configuration.
* - Composer is aware of at least one repository hosted at
* packages.drupal.org (since that's currently the only server that supports
* TUF), and that those repositories have TUF support explicitly enabled.
*
* Note that this validator is currently not active, because the service
* definition is not tagged as an event subscriber. This will be changed in
* https://drupal.org/i/3358504, once TUF support is rolled out on
* packages.drupal.org.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class PhpTufValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* The name of the PHP-TUF Composer integration plugin.
*
* @var string
*/
public const PLUGIN_NAME = 'php-tuf/composer-integration';
/**
* Constructs a PhpTufValidator object.
*
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler service.
* @param string $baseUrl
* The base URL of the repository, or repositories, defined in
* `composer.json`that must be protected by TUF.
*/
public function __construct(
private readonly PathLocator $pathLocator,
private readonly ComposerInspector $composerInspector,
private readonly ModuleHandlerInterface $moduleHandler,
private readonly string $baseUrl,
) {}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => 'validate',
PreCreateEvent::class => 'validate',
PreRequireEvent::class => 'validate',
PreApplyEvent::class => 'validate',
];
}
/**
* Reacts to a stage event by validating PHP-TUF configuration as needed.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
*/
public function validate(PreOperationStageEvent $event): void {
$messages = $this->validateTuf($this->pathLocator->getProjectRoot());
if ($messages) {
$event->addError($messages, $this->t('The active directory is not protected by PHP-TUF, which is required to use Package Manager securely.'));
}
$stage = $event->stage;
if ($stage->stageDirectoryExists()) {
$messages = $this->validateTuf($stage->getStageDirectory());
if ($messages) {
$event->addError($messages, $this->t('The stage directory is not protected by PHP-TUF, which is required to use Package Manager securely.'));
}
}
}
/**
* Flags messages if PHP-TUF is not installed and configured properly.
*
* @param string $dir
* The directory to examine.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
* The error messages, if any.
*/
private function validateTuf(string $dir): array {
$messages = [];
if ($this->moduleHandler->moduleExists('help')) {
$help_url = Url::fromRoute('help.page', ['name' => 'package_manager'])
->setOption('fragment', 'package-manager-tuf-info')
->toString();
}
// The Composer plugin must be installed.
$installed_packages = $this->composerInspector->getInstalledPackagesList($dir);
if (!isset($installed_packages[static::PLUGIN_NAME])) {
$message = $this->t('The <code>@plugin</code> plugin is not installed.', [
'@plugin' => static::PLUGIN_NAME,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to install the plugin.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
// And it has to be explicitly enabled.
$allowed_plugins = $this->composerInspector->getAllowPluginsConfig($dir);
if ($allowed_plugins !== TRUE && empty($allowed_plugins[static::PLUGIN_NAME])) {
$message = $this->t('The <code>@plugin</code> plugin is not listed as an allowed plugin.', [
'@plugin' => static::PLUGIN_NAME,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to configure the plugin.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
// Get the defined repositories that live at the base URL, and confirm that
// they have all opted into TUF protection.
$repositories = array_filter(
Json::decode($this->composerInspector->getConfig('repositories', $dir)),
fn (array $r): bool => str_starts_with($r['url'], $this->baseUrl)
);
foreach ($repositories as $repository) {
if (empty($repository['tuf'])) {
$messages[] = $this->t('TUF is not enabled for the @url repository.', [
'@url' => $repository['url'],
]);
}
}
// There must be at least one repository using the base URL.
if (empty($repositories)) {
$message = $this->t('The <code>@url</code> Composer repository must be defined in <code>composer.json</code>.', [
'@url' => $this->baseUrl,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to set up this repository.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
return $messages;
}
}
<?php
namespace Drupal\package_manager\Validator;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Defines an interface for classes that validate a stage before an operation.
*/
interface PreOperationStageValidatorInterface extends EventSubscriberInterface {
/**
* Validates a stage before an operation.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The stage event.
*/
public function validateStagePreOperation(PreOperationStageEvent $event): void;
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use PhpTuf\ComposerStager\API\Exception\LogicException;
use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Checks that rsync is available, if it is the configured file syncer.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class RsyncValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs an RsyncValidator object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory.
* @param \PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface $executableFinder
* The executable finder service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler service.
*/
public function __construct(
private readonly ConfigFactoryInterface $configFactory,
private readonly ExecutableFinderInterface $executableFinder,
private readonly ModuleHandlerInterface $moduleHandler,
) {}
/**
* Checks that rsync is being used, if it's available.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event being handled.
*/
public function validate(PreOperationStageEvent $event): void {
try {
$this->executableFinder->find('rsync');
$rsync_found = TRUE;
}
catch (LogicException) {
$rsync_found = FALSE;
}
if ($this->moduleHandler->moduleExists('help')) {
$help_url = Url::fromRoute('help.page')
->setRouteParameter('name', 'package_manager')
->setOption('fragment', 'package-manager-faq-rsync')
->toString();
}
$configured_syncer = $this->configFactory->get('package_manager.settings')
->get('file_syncer');
// If the PHP file syncer is selected, warn that we don't recommend it.
if ($configured_syncer === 'php') {
// Only status checks support warnings.
if ($event instanceof StatusCheckEvent) {
$message = $this->t('You are currently using the PHP file syncer, which has known problems and is not stable. <strong>The PHP file syncer will be removed in Package Manager 3.1.0.</strong> Switch back to the default <em>rsync</em> file syncer to continue using Package Manager.');
if (!$rsync_found) {
$message = $this->t('@message <code>rsync</code> was not found on your system.', ['@message' => $message]);
}
if (isset($help_url)) {
$message = $this->t('@message See the <a href=":url">Package Manager help</a> for more information on how to resolve this.', [
':url' => $help_url,
'@message' => $message,
]);
}
$event->addWarning([$message]);
}
// If we're not doing a status check, well, I guess we'll be using the
// PHP file syncer. There's nothing else to do.
return;
}
if ($rsync_found === FALSE) {
$message = $this->t('<code>rsync</code> is not available.');
if (isset($help_url)) {
$message = $this->t('@message See the <a href=":url">Package Manager help</a> for more information on how to resolve this.', [
'@message' => $message,
':url' => $help_url,
]);
}
$event->addError([$message]);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => 'validate',
PreCreateEvent::class => 'validate',
];
}
}
<?php
namespace Drupal\automatic_updates\Validator;
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\automatic_updates\Event\ReadinessCheckEvent;
use Drupal\automatic_updates\Updater;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SettingsValidator implements EventSubscriberInterface {
/**
* Checks that Drupal's settings are valid for Package Manager.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class SettingsValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a SettingsValidator object.
*
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The string translation service.
*/
public function __construct(TranslationInterface $translation) {
$this->setStringTranslation($translation);
}
/**
* Validates site settings before an update starts.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
* Checks that Drupal's settings are valid for Package Manager.
*/
public function checkSettings(PreOperationStageEvent $event): void {
if ($event->getStage() instanceof Updater && Settings::get('update_fetch_with_http_fallback')) {
public function validate(PreOperationStageEvent $event): void {
if (Settings::get('update_fetch_with_http_fallback')) {
$event->addError([
$this->t('The <code>update_fetch_with_http_fallback</code> setting must be disabled for automatic updates.'),
$this->t('The <code>update_fetch_with_http_fallback</code> setting must be disabled.'),
]);
}
}
......@@ -42,10 +38,11 @@ class SettingsValidator implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
public static function getSubscribedEvents(): array {
return [
ReadinessCheckEvent::class => 'checkSettings',
PreCreateEvent::class => 'checkSettings',
PreCreateEvent::class => 'validate',
PreApplyEvent::class => 'validate',
StatusCheckEvent::class => 'validate',
];
}
......
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates staging root is not a subdirectory of active.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class StageNotInActiveValidator implements EventSubscriberInterface {
use BaseRequirementValidatorTrait {
getSubscribedEvents as private getSubscribedEventsFromTrait;
}
use StringTranslationTrait;
/**
* Constructs a new StageNotInActiveValidator object.
*
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(private readonly PathLocator $pathLocator) {
}
/**
* Check if staging root is a subdirectory of active.
*/
public function validate(PreOperationStageEvent $event): void {
$project_root = $this->pathLocator->getProjectRoot();
$staging_root = $this->pathLocator->getStagingRoot();
if (str_starts_with($staging_root, $project_root)) {
$message = $this->t("Stage directory is a subdirectory of the active directory.");
$event->addError([$message]);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events = static::getSubscribedEventsFromTrait();
// We don't need to listen to PreApplyEvent because once the stage directory
// has been created, it's not going to be moved.
unset($events[PreApplyEvent::class]);
return $events;
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\Component\Assertion\Inspector;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Flags a warning if there are database updates in a staged update.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class StagedDBUpdateValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a StagedDBUpdateValidator object.
*
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \Drupal\Core\Extension\ModuleExtensionList $moduleList
* The module list service.
* @param \Drupal\Core\Extension\ThemeExtensionList $themeList
* The theme list service.
*/
public function __construct(
private readonly PathLocator $pathLocator,
private readonly ModuleExtensionList $moduleList,
private readonly ThemeExtensionList $themeList,
) {}
/**
* Checks that the staged update does not have changes to its install files.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* The event object.
*/
public function checkForStagedDatabaseUpdates(StatusCheckEvent $event): void {
if (!$event->stage->stageDirectoryExists()) {
return;
}
$stage_dir = $event->stage->getStageDirectory();
$extensions_with_updates = $this->getExtensionsWithDatabaseUpdates($stage_dir);
if ($extensions_with_updates) {
$extensions_with_updates = array_map($this->t(...), $extensions_with_updates);
$event->addWarning($extensions_with_updates, $this->t('Database updates have been detected in the following extensions.'));
}
}
/**
* Determines if a staged extension has changed update functions.
*
* @param string $stage_dir
* The path of the stage directory.
* @param \Drupal\Core\Extension\Extension $extension
* The extension to check.
*
* @return bool
* TRUE if the staged copy of the extension has changed update functions
* compared to the active copy, FALSE otherwise.
*
* @todo In https://drupal.org/i/3253828 use a more sophisticated method to
* detect changes in the staged extension. Right now, we just compare hashes
* of the .install and .post_update.php files in both copies of the given
* extension, but this will cause false positives for changes to comments,
* whitespace, or runtime code like requirements checks. It would be
* preferable to use a static analyzer to detect new or changed functions
* that are actually executed during an update. No matter what, this method
* must NEVER cause false negatives, since that could result in code which
* is incompatible with the current database schema being copied to the
* active directory.
*/
public function hasStagedUpdates(string $stage_dir, Extension $extension): bool {
$active_dir = $this->pathLocator->getProjectRoot();
$web_root = $this->pathLocator->getWebRoot();
if ($web_root) {
$active_dir .= DIRECTORY_SEPARATOR . $web_root;
$stage_dir .= DIRECTORY_SEPARATOR . $web_root;
}
$active_functions = $this->getUpdateFunctions($active_dir, $extension);
$staged_functions = $this->getUpdateFunctions($stage_dir, $extension);
return (bool) array_diff($staged_functions, $active_functions);
}
/**
* Returns a list of all update functions for a module.
*
* @param string $root_dir
* The root directory of the Drupal code base.
* @param \Drupal\Core\Extension\Extension $extension
* The module to check.
*
* @return string[]
* The names of the update functions in the module's .install and
* .post_update.php files.
*/
protected function getUpdateFunctions(string $root_dir, Extension $extension): array {
$name = $extension->getName();
$path = implode(DIRECTORY_SEPARATOR, [
$root_dir,
$extension->getPath(),
$name,
]);
$function_names = [];
$patterns = [
'.install' => '/^' . $name . '_update_[0-9]+$/i',
'.post_update.php' => '/^' . $name . '_post_update_.+$/i',
];
foreach ($patterns as $suffix => $pattern) {
$file = $path . $suffix;
if (!file_exists($file)) {
continue;
}
// Parse the file and scan for named functions which match the pattern.
$code = file_get_contents($file);
$tokens = token_get_all($code);
for ($i = 0; $i < count($tokens); $i++) {
$chunk = array_slice($tokens, $i, 3);
if ($this->tokensMatchFunctionNamePattern($chunk, $pattern)) {
$function_names[] = $chunk[2][1];
}
}
}
return $function_names;
}
/**
* Determines if a set of tokens contain a function name matching a pattern.
*
* @param array[] $tokens
* A set of three tokens, part of a stream returned by token_get_all().
* @param string $pattern
* If the tokens declare a named function, a regular expression to test the
* function name against.
*
* @return bool
* TRUE if the given tokens declare a function whose name matches the given
* pattern; FALSE otherwise.
*
* @see token_get_all()
*/
private function tokensMatchFunctionNamePattern(array $tokens, string $pattern): bool {
if (count($tokens) !== 3 || !Inspector::assertAllStrictArrays($tokens)) {
return FALSE;
}
// A named function declaration will always be a T_FUNCTION (the word
// `function`), followed by T_WHITESPACE (or the code would be syntactically
// invalid), followed by a T_STRING (the function name). This will ignore
// anonymous functions, but match class methods (although class methods are
// highly unlikely to match the naming patterns of update hooks).
$names = array_map('token_name', array_column($tokens, 0));
if ($names === ['T_FUNCTION', 'T_WHITESPACE', 'T_STRING']) {
return (bool) preg_match($pattern, $tokens[2][1]);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => 'checkForStagedDatabaseUpdates',
];
}
/**
* Gets extensions that have database updates in the stage directory.
*
* @param string $stage_dir
* The path of the stage directory.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
* The names of the extensions that have database updates.
*/
public function getExtensionsWithDatabaseUpdates(string $stage_dir): array {
$extensions_with_updates = [];
// Check all installed extensions for database updates.
$lists = [$this->moduleList, $this->themeList];
foreach ($lists as $list) {
foreach ($list->getAllInstalledInfo() as $name => $info) {
if ($this->hasStagedUpdates($stage_dir, $list->get($name))) {
$extensions_with_updates[] = $info['name'];
}
}
}
return $extensions_with_updates;
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PreApplyEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that updated projects are secure and supported.
*
* @internal
* This class is an internal part of the module's update handling and
* should not be used by external code.
*/
final class SupportedReleaseValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a SupportedReleaseValidator object.
*
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator
) {}
/**
* Checks if the given version of a project is supported.
*
* Checks if the given version of the given project is in the core update
* system's list of known, secure, installable releases of that project.
* considered a supported release by verifying if the project is found in the
* core update system's list of known, secure, and installable releases.
*
* @param string $name
* The name of the project.
* @param string $semantic_version
* A semantic version number for the project.
*
* @return bool
* TRUE if the given version of the project is supported, otherwise FALSE.
* given version is not supported will return FALSE.
*/
private function isSupportedRelease(string $name, string $semantic_version): bool {
$supported_releases = (new ProjectInfo($name))->getInstallableReleases();
if (!$supported_releases) {
return FALSE;
}
// If this version is found in the list of installable releases, it is
// secured and supported.
if (array_key_exists($semantic_version, $supported_releases)) {
return TRUE;
}
// If the semantic version number wasn't in the list of
// installable releases, convert it to a legacy version number and see
// if the version number is in the list.
$legacy_version = LegacyVersionUtility::convertToLegacyVersion($semantic_version);
if ($legacy_version && array_key_exists($legacy_version, $supported_releases)) {
return TRUE;
}
// Neither the semantic version nor the legacy version are in the list
// of installable releases, so the release isn't supported.
return FALSE;
}
/**
* Checks that the packages are secure and supported.
*
* @param \Drupal\package_manager\Event\PreApplyEvent $event
* The event object.
*/
public function validate(PreApplyEvent $event): void {
$active = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$staged = $this->composerInspector->getInstalledPackagesList($event->stage->getStageDirectory());
$updated_packages = array_merge(
$staged->getPackagesNotIn($active)->getArrayCopy(),
$staged->getPackagesWithDifferentVersionsIn($active)->getArrayCopy()
);
$unknown_packages = [];
$unsupported_packages = [];
foreach ($updated_packages as $package_name => $staged_package) {
// Only packages of the types 'drupal-module' or 'drupal-theme' that
// start with 'drupal/' will have update XML from drupal.org.
if (!in_array($staged_package->type, ['drupal-module', 'drupal-theme'], TRUE)
|| !str_starts_with($package_name, 'drupal/')) {
continue;
}
$project_name = $staged[$package_name]->getProjectName();
if (empty($project_name)) {
$unknown_packages[] = $package_name;
continue;
}
$semantic_version = $staged_package->version;
if (!$this->isSupportedRelease($project_name, $semantic_version)) {
$unsupported_packages[] = $this->t('@project_name (@package_name) @version', [
'@project_name' => $project_name,
'@package_name' => $package_name,
'@version' => $semantic_version,
]);
}
}
if ($unsupported_packages) {
$summary = $this->formatPlural(
count($unsupported_packages),
'Cannot update because the following project version is not in the list of installable releases.',
'Cannot update because the following project versions are not in the list of installable releases.'
);
$event->addError($unsupported_packages, $summary);
}
if ($unknown_packages) {
$event->addError([
$this->formatPlural(
count($unknown_packages),
'Cannot update because the following new or updated Drupal package does not have project information: @unknown_packages',
'Cannot update because the following new or updated Drupal packages do not have project information: @unknown_packages',
[
'@unknown_packages' => implode(', ', $unknown_packages),
],
),
]);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
PreApplyEvent::class => 'validate',
];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\PreRequireEvent;
use Drupal\package_manager\PathLocator;
use PhpTuf\ComposerStager\API\Exception\PreconditionException;
use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
use PhpTuf\ComposerStager\API\Path\Factory\PathListFactoryInterface;
use PhpTuf\ComposerStager\API\Precondition\Service\NoUnsupportedLinksExistInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Flags errors if unsupported symbolic links are detected.
*
* @see https://github.com/php-tuf/composer-stager/tree/develop/src/Domain/Service/Precondition#symlinks
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class SymlinkValidator implements EventSubscriberInterface {
use BaseRequirementValidatorTrait;
/**
* Constructs a SymlinkValidator object.
*
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \PhpTuf\ComposerStager\API\Precondition\Service\NoUnsupportedLinksExistInterface $precondition
* The Composer Stager precondition that this validator wraps.
* @param \PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface $pathFactory
* The path factory service.
* @param \PhpTuf\ComposerStager\API\Path\Factory\PathListFactoryInterface $pathListFactory
* The path list factory service.
*/
public function __construct(
private readonly PathLocator $pathLocator,
private readonly NoUnsupportedLinksExistInterface $precondition,
private readonly PathFactoryInterface $pathFactory,
private readonly PathListFactoryInterface $pathListFactory,
) {}
/**
* Flags errors if the project root or stage directory contain symbolic links.
*/
public function validate(PreOperationStageEvent $event): void {
if ($event instanceof PreRequireEvent) {
// We don't need to check symlinks again during PreRequireEvent; this was
// already just validated during PreCreateEvent.
return;
}
$active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
// The precondition requires us to pass both an active and stage directory,
// so if the stage hasn't been created or claimed yet, use the directory
// that contains this file, which contains only a few files and no symlinks,
// as the stage directory. The precondition itself doesn't care if the
// directory actually exists or not.
$stage_dir = __DIR__;
if ($event->stage->stageDirectoryExists()) {
$stage_dir = $event->stage->getStageDirectory();
}
$stage_dir = $this->pathFactory->create($stage_dir);
// Return early if no excluded paths were collected because this validator
// is dependent on knowing which paths to exclude when searching for
// symlinks.
// @see \Drupal\package_manager\StatusCheckTrait::runStatusCheck()
if ($event->excludedPaths === NULL) {
return;
}
// The list of excluded paths is immutable, but the precondition may need to
// mutate it, so convert it back to a normal, mutable path list.
$exclusions = $this->pathListFactory->create(...$event->excludedPaths->getAll());
try {
$this->precondition->assertIsFulfilled($active_dir, $stage_dir, $exclusions);
}
catch (PreconditionException $e) {
$event->addErrorFromThrowable($e);
}
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Checks that the file system is writable.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class WritableFileSystemValidator implements PreOperationStageValidatorInterface {
class WritableFileSystemValidator implements EventSubscriberInterface {
use BaseRequirementValidatorTrait;
use StringTranslationTrait;
/**
* The path locator service.
*
* @var \Drupal\package_manager\PathLocator
*/
protected $pathLocator;
/**
* The Drupal root.
*
* @var string
*/
protected $appRoot;
/**
* Constructs a WritableFileSystemValidator object.
*
* @param \Drupal\package_manager\PathLocator $path_locator
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param string $app_root
* The Drupal root.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The translation service.
*/
public function __construct(PathLocator $path_locator, string $app_root, TranslationInterface $translation) {
$this->pathLocator = $path_locator;
$this->appRoot = $app_root;
$this->setStringTranslation($translation);
public function __construct(private readonly PathLocator $pathLocator) {
}
/**
* {@inheritdoc}
* Checks that the file system is writable.
*
* @todo It might make sense to use a more sophisticated method of testing
* writability than is_writable(), since it's not clear if that can return
* false negatives/positives due to things like SELinux, exotic file
* systems, and so forth.
* @todo Determine if 'is_writable()' is a sufficiently robust test across
* different operating systems in https://drupal.org/i/3348253.
*/
public function validateStagePreOperation(PreOperationStageEvent $event): void {
public function validate(PreOperationStageEvent $event): void {
$messages = [];
if (!is_writable($this->appRoot)) {
$messages[] = $this->t('The Drupal directory "@dir" is not writable.', [
'@dir' => $this->appRoot,
$project_root = $this->pathLocator->getProjectRoot();
// If the web (Drupal) root and project root are different, validate the
// web root separately.
$web_root = $this->pathLocator->getWebRoot();
if ($web_root) {
$drupal_root = $project_root . DIRECTORY_SEPARATOR . $web_root;
if (!is_writable($drupal_root)) {
$messages[] = $this->t('The Drupal directory "@dir" is not writable.', [
'@dir' => $drupal_root,
]);
}
}
if (!is_writable($project_root)) {
$messages[] = $this->t('The project root directory "@dir" is not writable.', [
'@dir' => $project_root,
]);
}
......@@ -67,18 +66,33 @@ class WritableFileSystemValidator implements PreOperationStageValidatorInterface
$messages[] = $this->t('The vendor directory "@dir" is not writable.', ['@dir' => $dir]);
}
// During pre-apply don't check whether the staging root is writable.
if ($event instanceof PreApplyEvent) {
if ($messages) {
$event->addError($messages, $this->t('The file system is not writable.'));
}
return;
}
// Ensure the staging root is writable. If it doesn't exist, ensure we will
// be able to create it.
$dir = $this->pathLocator->getStagingRoot();
if (!file_exists($dir)) {
$dir = dirname($dir);
if (!is_writable($dir)) {
$messages[] = $this->t('The stage root directory will not able to be created at "@dir".', [
'@dir' => $dir,
]);
}
}
elseif (!is_writable($dir)) {
$messages[] = $this->t('The stage root directory "@dir" is not writable.', [
'@dir' => $dir,
]);
}
if ($messages) {
$event->addError($messages, $this->t('The file system is not writable.'));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
PreCreateEvent::class => 'validateStagePreOperation',
];
}
}
# Why do we need the `updated_module` fixtures?
Because there is a need to thoroughly test the updating of a module. See `\Drupal\Tests\package_manager\Build\PackageUpdateTest`.
This requires 2 versions (`1.0.0` and `1.1.0`) of the same module (`updated_module`), each with a different code bases.
The test updates from one version to the next, and verifies the updated module's code base is actually used after the update: it verifies the updated logic of version of `\Drupal\updated_module\PostApplySubscriber` is being executed.
`\Drupal\fixture_manipulator\FixtureManipulator` cannot manipulate code nor does it modify the file system: it only creates a "skeleton" extension. (See `\Drupal\fixture_manipulator\FixtureManipulator::addProjectAtPath()`.)
# Why do we need the `alpha` fixtures?
To be able to test that `php-tuf/composer-stager` indeed only updates the package for which an update was requested (even though more updates are available), no fixture manipulation is allowed to occur. This requires updating a `path` composer package repository to first serve contain one version of a package, and then another. That is what these fixtures are used for.
name: Alpha
type: module
core_version_requirement: ^9.7 || ^10
project: alpha
name: Alpha
type: module
core_version_requirement: ^9.7 || ^10
project: alpha
{
"name": "drupal/main_module",
"type": "drupal-module",
"version": "1.0.0"
}
name: Main module
type: module
core_version_requirement: ^9 || ^10
project: main_module