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 340 additions and 137 deletions
......@@ -5,9 +5,15 @@ declare(strict_types = 1);
namespace Drupal\package_manager\PathExcluder;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
......@@ -23,12 +29,22 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
*
* If web root and project root are the same, nothing is excluded.
*
* This excluder can be disabled by changing the config setting
* `package_manager.settings:include_unknown_files_in_project_root` to TRUE.
* This may be needed for sites that have files outside the web root (besides
* the vendor directory) which are nonetheless needed in order for Composer to
* assemble the code base correctly; a classic example would be a directory of
* patch files used by `cweagans/composer-patches`.
*
* @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 UnknownPathExcluder implements EventSubscriberInterface {
final class UnknownPathExcluder implements EventSubscriberInterface, LoggerAwareInterface {
use LoggerAwareTrait;
use StringTranslationTrait;
/**
* Constructs a UnknownPathExcluder object.
......@@ -37,11 +53,16 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory service.
*/
public function __construct(
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {}
private readonly ConfigFactoryInterface $configFactory,
) {
$this->setLogger(new NullLogger());
}
/**
* {@inheritdoc}
......@@ -49,53 +70,110 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
CollectPathsToExcludeEvent::class => 'excludeUnknownPaths',
StatusCheckEvent::class => 'logExcludedPaths',
];
}
/**
* Excludes unknown paths from stage operations.
* Returns the paths to exclude from stage operations.
*
* @param \Drupal\package_manager\Event\CollectPathsToExcludeEvent $event
* The event object.
* @return string[]
* The paths that should be excluded from stage operations, relative to the
* project root.
*
* @throws \Exception
* See \Drupal\package_manager\ComposerInspector::validate().
*/
public function excludeUnknownPaths(CollectPathsToExcludeEvent $event): void {
$project_root = $this->pathLocator->getProjectRoot();
$web_root = $project_root . DIRECTORY_SEPARATOR . $this->pathLocator->getWebRoot();
if (realpath($web_root) === $project_root) {
return;
private function getExcludedPaths(): array {
// If this excluder is disabled, or the project root and web root are the
// same, we are not excluding any paths.
$is_disabled = $this->configFactory->get('package_manager.settings')
->get('include_unknown_files_in_project_root');
$web_root = $this->pathLocator->getWebRoot();
if ($is_disabled || empty($web_root)) {
return [];
}
// To determine the scaffold files to exclude, the installed packages must
// be known, and that requires Composer commands to be able to run. This
// intentionally does not catch exceptions: failed Composer validation in
// the project root implies that this excluder cannot function correctly.
// Note: the call to ComposerInspector::getInstalledPackagesList() would
// To determine the files to include, the installed packages must be known,
// and that requires Composer commands to be able to run. This intentionally
// does not catch exceptions: failed Composer validation in the project root
// implies that this excluder cannot function correctly.
// Note: the call to ComposerInspector::getConfig() would
// also have triggered this, but explicitness is preferred here.
// @see \Drupal\package_manager\StatusCheckTrait::runStatusCheck()
$project_root = $this->pathLocator->getProjectRoot();
$this->composerInspector->validate($project_root);
$vendor_dir = $this->pathLocator->getVendorDirectory();
$scaffold_files_paths = $this->getScaffoldFiles();
// Search for all files (including hidden ones) in project root.
$paths_in_project_root = glob("$project_root/{,.}*", GLOB_BRACE);
$paths = [];
$known_paths = array_merge([$vendor_dir, $web_root, "$project_root/composer.json", "$project_root/composer.lock"], $scaffold_files_paths);
foreach ($paths_in_project_root as $path_in_project_root) {
if (!in_array($path_in_project_root, $known_paths, TRUE)) {
$paths[] = $path_in_project_root;
// The vendor directory and web root are always included in staging
// operations, along with `composer.json`, `composer.lock`, and any scaffold
// files provided by Drupal core.
$always_include = [
$this->composerInspector->getConfig('vendor-dir', $project_root),
$web_root,
'composer.json',
'composer.lock',
];
foreach ($this->getScaffoldFiles() as $scaffold_file_path) {
// The web root is always included in staging operations, so we don't need
// to do anything special for scaffold files that live in it.
if (str_starts_with($scaffold_file_path, '[web-root]')) {
continue;
}
$always_include[] = ltrim($scaffold_file_path, '/');
}
// Search for all files (including hidden ones) in the project root. We need
// to use readdir() and friends here, rather than glob(), since certain
// glob() flags aren't supported on all systems. We also can't use
// \Drupal\Core\File\FileSystemInterface::scanDirectory(), because it
// unconditionally ignores hidden files and directories.
$files_in_project_root = [];
$handle = opendir($project_root);
if (empty($handle)) {
throw new \RuntimeException("Could not scan for files in the project root.");
}
while ($entry = readdir($handle)) {
$files_in_project_root[] = $entry;
}
closedir($handle);
return array_diff($files_in_project_root, $always_include, ['.', '..']);
}
/**
* Excludes unknown paths from stage operations.
*
* @param \Drupal\package_manager\Event\CollectPathsToExcludeEvent $event
* The event object.
*/
public function excludeUnknownPaths(CollectPathsToExcludeEvent $event): void {
// We can exclude the paths as-is; they are already relative to the project
// root.
$event->add(...$this->getExcludedPaths());
}
/**
* Logs the paths that will be excluded from stage operations.
*/
public function logExcludedPaths(): void {
$excluded_paths = $this->getExcludedPaths();
if ($excluded_paths) {
sort($excluded_paths);
$message = $this->t("The following paths in @project_root aren't recognized as part of your Drupal site, so to be safe, Package Manager is excluding them from all stage operations. If these files are not needed for Composer to work properly in your site, no action is needed. Otherwise, you can disable this behavior by setting the <code>package_manager.settings:include_unknown_files_in_project_root</code> config setting to <code>TRUE</code>.\n\n@list", [
'@project_root' => $this->pathLocator->getProjectRoot(),
'@list' => implode("\n", $excluded_paths),
]);
$this->logger->info($message);
}
$event->addPathsRelativeToProjectRoot($paths);
}
/**
* Gets the path of scaffold files, for example 'index.php' and 'robots.txt'.
*
* @return array
* The array of scaffold file paths.
* @return string[]
* The paths of scaffold files provided by `drupal/core`, relative to the
* project root.
*
* @todo Intelligently load scaffold files in https://drupal.org/i/3343802.
*/
......@@ -103,10 +181,9 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
$project_root = $this->pathLocator->getProjectRoot();
$packages = $this->composerInspector->getInstalledPackagesList($project_root);
$extra = Json::decode($this->composerInspector->getConfig('extra', $packages['drupal/core']->path . '/composer.json'));
if (isset($extra['drupal-scaffold']['file-mapping'])) {
return array_keys($extra['drupal-scaffold']['file-mapping']);
}
return [];
$scaffold_files = $extra['drupal-scaffold']['file-mapping'] ?? [];
return str_replace('[project-root]', '', array_keys($scaffold_files));
}
}
<?php
namespace Drupal\package_manager\Plugin\QueueWorker;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Processes a queue of defunct stage directories, deleting them.
*
* @QueueWorker(
* id = "package_manager_cleanup",
* title = @Translation("Stage directory cleaner"),
* cron = {"time" = 30}
* )
*/
final class Cleaner extends QueueWorkerBase implements ContainerFactoryPluginInterface {
/**
* Constructs a new Cleaner instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
* The file system service.
*/
public function __construct(array $configuration, string $plugin_id, mixed $plugin_definition, private readonly FileSystemInterface $fileSystem) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get(FileSystemInterface::class),
);
}
/**
* {@inheritdoc}
*/
public function processItem($dir) {
assert(is_string($dir));
if (file_exists($dir)) {
$this->fileSystem->deleteRecursive($dir, function (string $path): void {
$this->fileSystem->chmod($path, is_dir($path) ? 0700 : 0600);
});
}
}
}
......@@ -7,8 +7,7 @@ namespace Drupal\package_manager;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface;
use PhpTuf\ComposerStager\Internal\Process\Factory\ProcessFactory as StagerProcessFactory;
use Symfony\Component\Process\Process;
use PhpTuf\ComposerStager\API\Process\Service\ProcessInterface;
// cspell:ignore BINDIR
......@@ -29,13 +28,13 @@ final class ProcessFactory implements ProcessFactoryInterface {
* The file system service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory service.
* @param \PhpTuf\ComposerStager\Internal\Process\Factory\ProcessFactory $decorated
* @param \PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface $decorated
* The decorated process factory service.
*/
public function __construct(
private readonly FileSystemInterface $fileSystem,
private readonly ConfigFactoryInterface $configFactory,
private readonly StagerProcessFactory $decorated,
private readonly ProcessFactoryInterface $decorated,
) {}
/**
......@@ -57,7 +56,7 @@ final class ProcessFactory implements ProcessFactoryInterface {
/**
* {@inheritdoc}
*/
public function create(array $command): Process {
public function create(array $command): ProcessInterface {
$process = $this->decorated->create($command);
$env = $process->getEnv();
......
......@@ -4,7 +4,8 @@ declare(strict_types = 1);
namespace Drupal\package_manager;
use PhpTuf\ComposerStager\API\Process\Service\ProcessOutputCallbackInterface;
use PhpTuf\ComposerStager\API\Process\Service\OutputCallbackInterface;
use PhpTuf\ComposerStager\API\Process\Value\OutputTypeEnum;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
......@@ -19,7 +20,7 @@ use Psr\Log\NullLogger;
* at any time without warning. External code should not interact with this
* class.
*/
final class ProcessOutputCallback implements ProcessOutputCallbackInterface, LoggerAwareInterface {
final class ProcessOutputCallback implements OutputCallbackInterface, LoggerAwareInterface {
use LoggerAwareTrait;
......@@ -47,20 +48,14 @@ final class ProcessOutputCallback implements ProcessOutputCallbackInterface, Log
/**
* {@inheritdoc}
*/
public function __invoke(string $type, string $buffer): void {
// \Symfony\Component\Process\Process defines the output types in lowercase,
// but Composer Stager uses uppercase.
$type = strtoupper($type);
public function __invoke(OutputTypeEnum $type, string $buffer): void {
if ($type === self::OUT) {
if ($type === OutputTypeEnum::OUT) {
$this->outBuffer .= $buffer;
}
elseif ($type === self::ERR) {
elseif ($type === OutputTypeEnum::ERR) {
$this->errorBuffer .= $buffer;
}
else {
throw new \InvalidArgumentException("Unsupported output type: '$type'");
}
}
/**
......
......@@ -192,7 +192,7 @@ final class ProjectInfo {
// update processor service.
if (!isset($available_projects[$this->name])) {
/** @var \Drupal\package_manager\PackageManagerUpdateProcessor $update_processor */
$update_processor = \Drupal::service('package_manager.update_processor');
$update_processor = \Drupal::service(PackageManagerUpdateProcessor::class);
if ($project_data = $update_processor->getProjectData($this->name)) {
$available_projects[$this->name] = $project_data;
}
......
......@@ -6,21 +6,19 @@ namespace Drupal\package_manager;
use Composer\Semver\VersionParser;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Component\Utility\Random;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TempStore\SharedTempStore;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\Utility\Error;
use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
use Drupal\package_manager\Event\PostApplyEvent;
use Drupal\package_manager\Event\PostCreateEvent;
use Drupal\package_manager\Event\PostDestroyEvent;
use Drupal\package_manager\Event\PostRequireEvent;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreDestroyEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\PreRequireEvent;
use Drupal\package_manager\Event\StageEvent;
......@@ -34,7 +32,7 @@ use PhpTuf\ComposerStager\API\Core\StagerInterface;
use PhpTuf\ComposerStager\API\Exception\InvalidArgumentException;
use PhpTuf\ComposerStager\API\Exception\PreconditionException;
use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
use PhpTuf\ComposerStager\API\Path\Value\PathList;
use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
......@@ -161,6 +159,17 @@ abstract class StageBase implements LoggerAwareInterface {
*/
protected SharedTempStore $tempStore;
/**
* The stage type.
*
* To ensure that stage classes do not unintentionally use another stage's
* type, all concrete subclasses MUST explicitly define this property.
* The recommended pattern is `MODULE:TYPE`.
*
* @var string
*/
protected string $type;
/**
* Constructs a new Stage object.
*
......@@ -172,8 +181,8 @@ abstract class StageBase implements LoggerAwareInterface {
* The stager service.
* @param \PhpTuf\ComposerStager\API\Core\CommitterInterface $committer
* The committer service.
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
* The file system service.
* @param \Drupal\Core\Queue\QueueFactory $queueFactory
* The queue factory.
* @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The event dispatcher service.
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempStoreFactory
......@@ -190,7 +199,7 @@ abstract class StageBase implements LoggerAwareInterface {
protected readonly BeginnerInterface $beginner,
protected readonly StagerInterface $stager,
protected readonly CommitterInterface $committer,
protected readonly FileSystemInterface $fileSystem,
protected readonly QueueFactory $queueFactory,
protected EventDispatcherInterface $eventDispatcher,
protected readonly SharedTempStoreFactory $tempStoreFactory,
protected readonly TimeInterface $time,
......@@ -201,6 +210,31 @@ abstract class StageBase implements LoggerAwareInterface {
$this->setLogger(new NullLogger());
}
/**
* Gets the stage type.
*
* The stage type can be used by stage event subscribers to implement logic
* specific to certain stages, without relying on the class name (which may
* not be part of module's public API).
*
* @return string
* The stage type.
*
* @throws \LogicException
* Thrown if $this->type is not explicitly overridden.
*/
final public function getType(): string {
$reflector = new \ReflectionProperty($this, 'type');
// The $type property must ALWAYS be overridden. This means that different
// subclasses can return the same value (thus allowing one stage to
// impersonate another one), but if that happens, it is intentional.
if ($reflector->getDeclaringClass()->getName() === static::class) {
return $this->type;
}
throw new \LogicException(static::class . ' must explicitly override the $type property.');
}
/**
* Determines if the stage directory can be created.
*
......@@ -254,7 +288,7 @@ abstract class StageBase implements LoggerAwareInterface {
/**
* Collects paths that Composer Stager should exclude.
*
* @return string[]
* @return \PhpTuf\ComposerStager\API\Path\Value\PathListInterface
* A list of paths that Composer Stager should exclude when creating the
* stage directory and applying staged changes to the active directory.
*
......@@ -264,15 +298,14 @@ abstract class StageBase implements LoggerAwareInterface {
* @see ::create()
* @see ::apply()
*/
protected function getPathsToExclude(): array {
protected function getPathsToExclude(): PathListInterface {
$event = new CollectPathsToExcludeEvent($this, $this->pathLocator, $this->pathFactory);
try {
$this->eventDispatcher->dispatch($event);
return $this->eventDispatcher->dispatch($event);
}
catch (\Throwable $e) {
$this->rethrowAsStageException($e);
}
return $event->getAll();
}
/**
......@@ -311,26 +344,40 @@ abstract class StageBase implements LoggerAwareInterface {
// to create a stage directory at around the same time. If an error occurs
// while the event is being processed, the stage is marked as available.
// @see ::dispatch()
$id = Crypt::randomBytesBase64();
// We specifically generate a random 32-character alphanumeric name in order
// to guarantee that the the stage ID won't start with -, which could cause
// it to be interpreted as an option if it's used as a command-line
// argument. (For example,
// \Drupal\Component\Utility\Crypt::randomBytesBase64() would be vulnerable
// to this; the stage ID needs to be unique, but not cryptographically so.)
$id = (new Random())->name(32);
// Re-acquire the tempstore to ensure that the lock is written by whoever is
// actually logged in (or not) right now, since it's possible that the stage
// was instantiated (i.e., __construct() was called) by a different session,
// which would result in the lock having the wrong owner and the stage not
// being claimable by whoever is actually creating it.
$this->tempStore = $this->tempStoreFactory->get('package_manager_stage');
$this->tempStore->set(static::TEMPSTORE_LOCK_KEY, [$id, static::class]);
// For the lock value, we use both the stage's class and its type in order
// to prevent a stage from being manipulated by two different classes during
// a single life cycle.
$this->tempStore->set(static::TEMPSTORE_LOCK_KEY, [
$id,
static::class,
$this->getType(),
]);
$this->claim($id);
$active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
$stage_dir = $this->pathFactory->create($this->getStageDirectory());
$event = new PreCreateEvent($this, $this->getPathsToExclude());
$excluded_paths = $this->getPathsToExclude();
$event = new PreCreateEvent($this, $excluded_paths);
// If an error occurs and we won't be able to create the stage, mark it as
// available.
$this->dispatch($event, [$this, 'markAsAvailable']);
try {
$this->beginner->begin($active_dir, $stage_dir, new PathList(...$event->getExcludedPaths()), NULL, $timeout);
$this->beginner->begin($active_dir, $stage_dir, $excluded_paths, NULL, $timeout);
}
catch (\Throwable $error) {
$this->destroy();
......@@ -409,7 +456,7 @@ abstract class StageBase implements LoggerAwareInterface {
// If constraints were changed, update those packages.
if ($runtime || $dev) {
$command = array_merge(['update', '--with-all-dependencies'], $runtime, $dev);
$command = array_merge(['update', '--with-all-dependencies', '--optimize-autoloader'], $runtime, $dev);
$do_stage($command);
}
$this->dispatch(new PostRequireEvent($this, $runtime, $dev));
......@@ -442,23 +489,19 @@ abstract class StageBase implements LoggerAwareInterface {
$active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
$stage_dir = $this->pathFactory->create($this->getStageDirectory());
$excluded_paths = $this->getPathsToExclude();
$event = new PreApplyEvent($this, $excluded_paths);
// If an error occurs while dispatching the events, ensure that ::destroy()
// doesn't think we're in the middle of applying the staged changes to the
// active directory.
$event = new PreApplyEvent($this, $this->getPathsToExclude());
$this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime());
$this->dispatch($event, $this->setNotApplying(...));
// Create a marker file so that we can tell later on if the commit failed.
$this->failureMarker->write($this, $this->getFailureMarkerMessage());
// Exclude the failure file from the commit operation.
$paths_to_exclude = new PathList(...$event->getExcludedPaths());
$paths_to_exclude->add(
str_replace($this->pathLocator->getProjectRoot() . DIRECTORY_SEPARATOR, '', $this->failureMarker->getPath()),
);
try {
$this->committer->commit($stage_dir, $active_dir, $paths_to_exclude, NULL, $timeout);
$this->committer->commit($stage_dir, $active_dir, $excluded_paths, NULL, $timeout);
}
catch (InvalidArgumentException | PreconditionException $e) {
// The commit operation has not started yet, so we can clear the failure
......@@ -536,26 +579,16 @@ abstract class StageBase implements LoggerAwareInterface {
throw new StageException($this, 'Cannot destroy the stage directory while it is being applied to the active directory.');
}
$this->dispatch(new PreDestroyEvent($this));
$staging_root = $this->getStagingRoot();
// If the stage root directory exists, delete it and everything in it.
if (file_exists($staging_root)) {
try {
$this->fileSystem->deleteRecursive($staging_root, function (string $path): void {
$this->fileSystem->chmod($path, 0777);
});
}
catch (FileException) {
// Deliberately swallow the exception so that the stage will be marked
// as available and the post-destroy event will be fired, even if the
// stage directory can't actually be deleted. The file system service
// logs the exception, so we don't need to do anything else here.
}
// If the stage directory exists, queue it to be automatically cleaned up
// later by a queue (which may or may not happen during cron).
// @see \Drupal\package_manager\Plugin\QueueWorker\Cleaner
if ($this->stageDirectoryExists()) {
$this->queueFactory->get('package_manager_cleanup')
->createItem($this->getStageDirectory());
}
$this->storeDestroyInfo($force, $message);
$this->markAsAvailable();
$this->dispatch(new PostDestroyEvent($this));
}
/**
......@@ -595,6 +628,8 @@ abstract class StageBase implements LoggerAwareInterface {
}
if (isset($error)) {
// Ensure the error is logged for post-mortem diagnostics.
Error::logException($this->logger, $error);
if ($on_error) {
$on_error();
}
......@@ -648,7 +683,7 @@ abstract class StageBase implements LoggerAwareInterface {
)->render());
}
if ($stored_lock === [$unique_id, static::class]) {
if ($stored_lock === [$unique_id, static::class, $this->getType()]) {
$this->lock = $stored_lock;
return $this;
}
......
......@@ -42,22 +42,12 @@ trait StatusCheckTrait {
try {
$paths_to_exclude_event = new CollectPathsToExcludeEvent($stage, $path_locator, $path_factory);
$event_dispatcher->dispatch($paths_to_exclude_event);
$event = new StatusCheckEvent($stage, $paths_to_exclude_event->getAll());
}
catch (\Throwable $throwable) {
// We can dispatch the status check event without the paths to exclude,
// but it must be set explicitly to NULL, to allow those status checks to
// run that do not need the paths to exclude.
$event = new StatusCheckEvent($stage, NULL);
// Add the error that was encountered so that regardless of any other
// validation errors BaseRequirementsFulfilledValidator will stop the
// event propagation after the base requirement validators have run.
// @see \Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator
$event->addErrorFromThrowable($throwable, t('Unable to collect the paths to exclude.'));
$paths_to_exclude_event = $throwable;
}
$event_dispatcher->dispatch($event);
return $event->getResults();
$event = new StatusCheckEvent($stage, $paths_to_exclude_event);
return $event_dispatcher->dispatch($event)->getResults();
}
}
......@@ -36,7 +36,7 @@ final class TranslatableStringAdapter extends TranslatableMarkup implements Tran
/**
* {@inheritdoc}
*/
public function trans(TranslatorInterface $translator, ?string $locale = NULL): string {
public function trans(?TranslatorInterface $translator = NULL, ?string $locale = NULL): string {
// This method is NEVER used by Drupal to translate the underlying string;
// it exists solely for Composer Stager's translation system to
// transparently translate Drupal strings using its own architecture.
......
......@@ -9,7 +9,6 @@ use PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface;
use PhpTuf\ComposerStager\API\Translation\Service\DomainOptionsInterface;
use PhpTuf\ComposerStager\API\Translation\Value\TranslatableInterface;
use PhpTuf\ComposerStager\API\Translation\Value\TranslationParametersInterface;
use PhpTuf\ComposerStager\Internal\Translation\Factory\TranslatableFactory as StagerTranslatableFactory;
/**
* Creates translatable strings that can interoperate with Composer Stager.
......@@ -24,13 +23,13 @@ final class TranslatableStringFactory implements TranslatableFactoryInterface {
/**
* Constructs a TranslatableStringFactory object.
*
* @param \PhpTuf\ComposerStager\Internal\Translation\Factory\TranslatableFactory $decorated
* @param \PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface $decorated
* The decorated translatable factory service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The string translation service.
*/
public function __construct(
private readonly StagerTranslatableFactory $decorated,
private readonly TranslatableFactoryInterface $decorated,
private readonly TranslationInterface $translation,
) {}
......
......@@ -4,6 +4,7 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Validator;
use Composer\Semver\Semver;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
......@@ -155,15 +156,26 @@ final class ComposerPatchesValidator implements EventSubscriberInterface {
*/
private function computePatcherStatus(string $working_dir): array {
$list = $this->composerInspector->getInstalledPackagesList($working_dir);
$is_installed = isset($list[static::PLUGIN_NAME]);
$installed_version = $list[static::PLUGIN_NAME]?->version;
$info = $this->composerInspector->getRootPackageInfo($working_dir);
$is_root_requirement = array_key_exists(static::PLUGIN_NAME, $info['requires'] ?? []) || array_key_exists(static::PLUGIN_NAME, $info['devRequires'] ?? []);
$extra = Json::decode($this->composerInspector->getConfig('extra', $working_dir));
$exit_on_failure = $extra['composer-exit-on-patch-failure'] ?? FALSE;
// The 2.x version of the plugin always exits with an error if a patch can't
// be applied.
if ($installed_version && Semver::satisfies($installed_version, '^2')) {
$exit_on_failure = TRUE;
}
else {
$extra = Json::decode($this->composerInspector->getConfig('extra', $working_dir));
$exit_on_failure = $extra['composer-exit-on-patch-failure'] ?? FALSE;
}
return [$is_installed, $is_root_requirement, $exit_on_failure];
return [
is_string($installed_version),
$is_root_requirement,
$exit_on_failure,
];
}
/**
......
......@@ -64,9 +64,10 @@ final class ComposerPluginsValidator implements EventSubscriberInterface {
private const SUPPORTED_PLUGINS_THAT_DO_MODIFY = [
// cSpell:disable
// @see \Drupal\package_manager\Validator\ComposerPatchesValidator
'cweagans/composer-patches' => '^1.7.3',
'cweagans/composer-patches' => '^1.7.3 || ^2',
// @see \Drupal\package_manager\PathExcluder\VendorHardeningExcluder
'drupal/core-vendor-hardening' => '*',
'php-http/discovery' => '*',
// cSpell:enable
];
......
......@@ -6,7 +6,7 @@ namespace Drupal\package_manager\Validator;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Event\PostDestroyEvent;
use Drupal\package_manager\Event\PostApplyEvent;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
......@@ -176,7 +176,7 @@ final class LockFileValidator implements EventSubscriberInterface {
PreRequireEvent::class => 'validate',
PreApplyEvent::class => 'validate',
StatusCheckEvent::class => 'validate',
PostDestroyEvent::class => 'deleteHash',
PostApplyEvent::class => 'deleteHash',
];
}
......
......@@ -6,6 +6,9 @@ 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;
......@@ -59,17 +62,17 @@ class PhpExtensionsValidator implements EventSubscriberInterface {
}
/**
* Flags a warning if the OpenSSL extension is not installed.
* Flags an error if the OpenSSL extension is not installed.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
*/
public function validateOpenSsl(StatusCheckEvent $event): void {
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->addWarning([$message]);
$event->addError([$message]);
}
}
......@@ -97,6 +100,8 @@ class PhpExtensionsValidator implements EventSubscriberInterface {
['validateXdebug'],
['validateOpenSsl'],
],
PreCreateEvent::class => ['validateOpenSsl'],
PreApplyEvent::class => ['validateOpenSsl'],
];
}
......
......@@ -59,11 +59,15 @@ final class PhpTufValidator implements EventSubscriberInterface {
* 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 ModuleHandlerInterface $moduleHandler,
private readonly string $baseUrl,
) {}
/**
......@@ -147,13 +151,12 @@ final class PhpTufValidator implements EventSubscriberInterface {
$messages[] = $message;
}
// Get the defined repositories that use packages.drupal.org.
// 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'], 'https://packages.drupal.org')
fn (array $r): bool => str_starts_with($r['url'], $this->baseUrl)
);
// All packages.drupal.org repositories must have TUF protection.
foreach ($repositories as $repository) {
if (empty($repository['tuf'])) {
$messages[] = $this->t('TUF is not enabled for the @url repository.', [
......@@ -162,10 +165,11 @@ final class PhpTufValidator implements EventSubscriberInterface {
}
}
// There must be at least one repository using packages.drupal.org, since
// that's the only repository which supports TUF right now.
// There must be at least one repository using the base URL.
if (empty($repositories)) {
$message = $this->t('The <code>https://packages.drupal.org</code> Composer repository must be defined in <code>composer.json</code>.');
$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,
......
......@@ -72,7 +72,7 @@ final class RsyncValidator implements EventSubscriberInterface {
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. It is strongly recommended to switch back to the default <em>rsync</em> file syncer instead.');
$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]);
......
......@@ -121,12 +121,16 @@ final class SupportedReleaseValidator implements EventSubscriberInterface {
$event->addError($unsupported_packages, $summary);
}
if ($unknown_packages) {
$summary = $this->formatPlural(
count($unknown_packages),
'Cannot update because the following new or updated Drupal package does not have project information.',
'Cannot update because the following new or updated Drupal packages do not have project information.',
);
$event->addError($unknown_packages, $summary);
$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),
],
),
]);
}
}
......
......@@ -9,7 +9,7 @@ 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\Value\PathList;
use PhpTuf\ComposerStager\API\Path\Factory\PathListFactoryInterface;
use PhpTuf\ComposerStager\API\Precondition\Service\NoUnsupportedLinksExistInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
......@@ -36,11 +36,14 @@ final class SymlinkValidator implements EventSubscriberInterface {
* 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,
) {}
/**
......@@ -65,17 +68,20 @@ final class SymlinkValidator implements EventSubscriberInterface {
}
$stage_dir = $this->pathFactory->create($stage_dir);
$excluded_paths = $event->getExcludedPaths();
// 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 ($excluded_paths === NULL) {
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, new PathList(...$excluded_paths));
$this->precondition->assertIsFulfilled($active_dir, $stage_dir, $exclusions);
}
catch (PreconditionException $e) {
$event->addErrorFromThrowable($e);
......
{
"name": "drupal/main_module",
"type": "drupal-module",
"version": "1.0.0"
}
name: Main module
type: module
core_version_requirement: ^9 || ^10
project: main_module
name: Main Module Submodule
type: module
core_version_requirement: ^9 || ^10
project: main_module