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 466 additions and 313 deletions
file_syncer: php
# The rsync file syncer is currently the only stable file syncer and
# should only be changed for development purposes.
file_syncer: rsync
executables:
composer: ~
rsync: ~
additional_trusted_composer_plugins: []
include_unknown_files_in_project_root: false
......@@ -4,7 +4,7 @@ package_name:
constraints:
Regex:
# @see https://getcomposer.org/schema.json
pattern: '/^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]|-{1,2})?[a-z0-9]+)*$/'
pattern: '/^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9](([_.]|-{1,2})?[a-z0-9]+)*$/'
package_manager.settings:
type: config_object
......@@ -25,3 +25,6 @@ package_manager.settings:
sequence:
type: package_name
label: 'Trusted composer plugin'
include_unknown_files_in_project_root:
type: boolean
label: 'Include unrecognized files and directories in the project root in staging operations'
......@@ -27,7 +27,7 @@
* Only one stage directory can exist at any given time, and it is "owned" by
* the user or session that originally created it. Only the owner can perform
* operations on the stage directory, and only using the same class (i.e.,
* \Drupal\package_manager\Stage or a subclass) they used to create it.
* \Drupal\package_manager\StageBase or a subclass) they used to create it.
*
* Package Manager can run Composer commands in the stage directory to require
* or update packages in it, and then copy those changes back into the live,
......@@ -35,9 +35,9 @@
* stage directory can then be safely deleted. These four distinct operations
* -- create, require, apply, and destroy -- comprise the "stage life cycle."
*
* Package Manager's \Drupal\package_manager\Stage controls the stage life cycle
* and may be subclassed to implement custom behavior. But in most cases, custom
* code should use the event system to interact with the stage.
* Package Manager's \Drupal\package_manager\StageBase controls the stage life
* cycle and is an abstract class that must be subclassed. But in most cases,
* custom code should use the event system to interact with the stage.
*
* @see sec_stage_events Stage API: Events
* Events are dispatched before and after each operation in the stage life
......@@ -97,40 +97,40 @@
*
* - \Drupal\package_manager\Event\PostApplyEvent
* Dispatched after changes in the stage directory have been copied to the
* active directory. This event may be dispatched multiple times during the
* stage life cycle.
*
* - \Drupal\package_manager\Event\PreDestroyEvent
* Dispatched before the stage directory is deleted and the stage releases its
* ownership. This event is dispatched only once during the stage life cycle.
*
* - \Drupal\package_manager\Event\PostDestroyEvent
* Dispatched after the stage directory is deleted and the stage has released
* its ownership. This event is dispatched only once during the stage life
* cycle.
* active directory. It should only be used for cleaning up after other
* operations that happened during the stage life cycle. For example, a
* PostCreateEvent subscriber might have set a state value which is no longer
* needed once the stage has been applied to the active directory -- in such a
* case, a PostApplyEvent subscriber should delete that value.
* `drupal_flush_all_caches()` is called just before this event is dispatched,
* so subscribers shouldn't need to flush any caches or rebuild the service
* container. This event may be dispatched multiple times during the stage
* life cycle, and should *never* be used for schema changes (i.e., operations
* that should happen in `hook_update_N()` or a post-update function).
*
* @section sec_stage_api Stage API: Public methods
* The public API of any stage consists of the following methods:
*
* - \Drupal\package_manager\Stage::create()
* - \Drupal\package_manager\StageBase::create()
* Creates the stage directory, records ownership, and dispatches pre- and
* post-create events. Returns a unique token which calling code must use to
* verify stage ownership before performing operations on the stage
* directory in subsequent requests (when the stage directory is created,
* its ownership is automatically verified for the duration of the current
* request). See \Drupal\package_manager\Stage::claim() for more information.
* request). See \Drupal\package_manager\StageBase::claim() for more
* information.
*
* - \Drupal\package_manager\Stage::require()
* - \Drupal\package_manager\StageBase::require()
* Adds and/or updates packages in the stage directory and dispatches pre-
* and post-require events. The stage must be claimed by its owner to call
* this method.
*
* - \Drupal\package_manager\Stage::apply()
* - \Drupal\package_manager\StageBase::apply()
* Copies changes from the stage directory into the active directory, and
* dispatches the pre-apply event. The stage must be claimed by its owner to
* call this method.
*
* - \Drupal\package_manager\Stage::postApply()
* - \Drupal\package_manager\StageBase::postApply()
* Performs post-apply tasks after changes have been copied from the stage
* directory. This method should be called as soon as possible in a new
* request because the code on disk may no longer match what has been loaded
......@@ -138,10 +138,28 @@
* the service container, and dispatches the post-apply event. The stage must
* be claimed by its owner to call this method.
*
* - \Drupal\package_manager\Stage::destroy()
* Destroys the stage directory, releases ownership, and dispatches pre- and
* post-destroy events. It is possible to destroy the stage without having
* claimed it first, but this shouldn't be done unless absolutely necessary.
* - \Drupal\package_manager\StageBase::destroy()
* Destroys the stage directory, and releases ownership. It is possible to
* destroy the stage without having claimed it first, but this shouldn't be
* done unless absolutely necessary.
*
* - \Drupal\package_manager\StageBase::stageDirectoryExists()
* Determines if the stage directory exists and returns a boolean accordingly.
* This allows validators to directly know if the stage directory exists
* without using \Drupal\package_manager\StageBase::getStageDirectory(), which
* throws an exception if the stage directory does not exist.
*
* - \Drupal\package_manager\StageBase::getStageDirectory()
* Returns the absolute path of the directory where changes should be staged.
* It throws an exception if the stage hasn't been created or claimed yet.
*
* - \Drupal\package_manager\StageBase::isApplying()
* Determines if the staged changes are being applied to the active directory.
* It will return FALSE if more than an hour has passed since the apply
* operation began.
*
* - \Drupal\package_manager\StageBase::isAvailable()
* Determines if a stage directory can be created.
*
* @section sec_stage_exceptions Stage life cycle exceptions
* If problems occur during any point of the stage life cycle, a
......@@ -201,6 +219,14 @@
* secure-http configuration option must be enabled). This is the default
* behavior.
*
* Package Manager also assumes certain things that it does not explicitly
* enforce or check:
*
* - Only Composer operations should be performed on the stage directory. If
* other file operations were performed, any newly created files might not
* be copied back to the active site because of
* \Drupal\package_manager\PathExcluder\UnknownPathExcluder.
*
* Event subscribers which enforce these and other constraints are referred to
* as validators.
*
......@@ -221,12 +247,15 @@
* cycle from proceeding any further. If a validator encounters an exception,
* it can use ::addErrorFromThrowable() instead of ::addError(). During status
* checks, validators can call ::addWarning() for less severe problems --
* warnings will NOT stop the stage life cycle.
* warnings will NOT stop the stage life cycle. All three are convenience
* methods for equivalent \Drupal\package_manager\ValidationResult constructors,
* which can then be added to the event using ::addResult().
*
* @see \Drupal\package_manager\ValidationResult
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addError()
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addErrorFromThrowable()
* @see \Drupal\package_manager\Event\StatusCheckEvent::addWarning()
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addResult()
*
* @section sec_excluded_paths Excluding files from stage operations
* Certain files are never copied into the stage directory because they are
......@@ -248,5 +277,15 @@
* Composer at the command line and get information from it about the
* project's `composer.json`, which packages are installed, etc.
*
* @section sec_package_manager_failure_marker Package Manager failure marker
* A file PACKAGE_MANAGER_FAILURE.yml is placed in the active directory while
* staged code is copied back into it, and then removed after the copying is
* finished. If this file exists, it means that the staged changes failed to be
* applied to the active directory (for example: a file system error, or the
* copying process was interrupted), and the site is therefore in an
* indeterminate state. The only thing you can do is to restore the code and
* database from a backup.
* @see \Drupal\package_manager\FailureMarker
*
* @}
*/
name: 'Package Manager'
type: module
description: 'API module providing functionality to stage package installs and updates with Composer.'
core_version_requirement: ^9.7 || ^10
core_version_requirement: ^10.1
dependencies:
- drupal:update
......@@ -7,7 +7,12 @@
declare(strict_types = 1);
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Exception\StageFailureMarkerException;
use Drupal\package_manager\FailureMarker;
use PhpTuf\ComposerStager\API\Core\BeginnerInterface;
use PhpTuf\ComposerStager\API\Exception\ExceptionInterface;
use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface;
/**
* Implements hook_requirements().
......@@ -15,7 +20,7 @@ use Drupal\package_manager\Exception\StageFailureMarkerException;
function package_manager_requirements(string $phase) {
$requirements = [];
// BEGIN: DELETE FROM CORE MERGE REQUEST
if (!class_exists('\PhpTuf\ComposerStager\Domain\Core\Beginner\Beginner')) {
if (!interface_exists(BeginnerInterface::class)) {
$requirements['package_manager_composer_dependencies'] = [
'title' => t('Missing dependency'),
'description' => t('External dependencies for Package Manager are not available. Composer must be used to download the module with dependencies.'),
......@@ -26,7 +31,7 @@ function package_manager_requirements(string $phase) {
// If we're able to check for the presence of the failure marker at all, do it
// irrespective of the current run phase. If the failure marker is there, the
// site is in an indeterminate state and should be restored from backup ASAP.
$service_id = 'package_manager.failure_marker';
$service_id = FailureMarker::class;
if (\Drupal::hasService($service_id)) {
try {
\Drupal::service($service_id)->assertNotExists(NULL);
......@@ -39,6 +44,39 @@ function package_manager_requirements(string $phase) {
];
}
}
if ($phase !== 'runtime') {
return $requirements;
}
/** @var \PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface $executable_finder */
$executable_finder = \Drupal::service(ExecutableFinderInterface::class);
// Report the Composer version in use, as well as its path.
$title = t('Composer version');
try {
$requirements['package_manager_composer'] = [
'title' => $title,
'description' => t('@version (<code>@path</code>)', [
'@version' => \Drupal::service(ComposerInspector::class)->getVersion(),
'@path' => $executable_finder->find('composer'),
]),
'severity' => REQUIREMENT_INFO,
];
}
catch (\Throwable $e) {
// All Composer Stager exceptions are translatable.
$message = $e instanceof ExceptionInterface
? $e->getTranslatableMessage()
: $e->getMessage();
$requirements['package_manager_composer'] = [
'title' => $title,
'description' => t('Composer was not found. The error message was: @message', [
'@message' => $message,
]),
'severity' => REQUIREMENT_ERROR,
];
}
return $requirements;
}
......
......@@ -26,7 +26,15 @@ function package_manager_help($route_name, RouteMatchInterface $route_match) {
$output .= '<ul>';
$output .= ' <li>' . t("The Drupal application's codebase must be writable in order to use Automatic Updates. This includes Drupal core, modules, themes and the Composer dependencies in the <code>vendor</code> directory. This makes Automatic Updates incompatible with some hosting platforms.") . '</li>';
$output .= ' <li>' . t('Package Manager requires a Composer executable whose version satisfies <code>@version</code>, and PHP must have permission to run it. The path to the executable may be stored in config, or it will be automatically detected. To set the path to Composer, you can add the following line to settings.php:', ['@version' => ComposerInspector::SUPPORTED_VERSION]) . '</li>';
$output .= '</ul>';
$output .= ' <li>' . t("Your Drupal site's <code>composer.json</code> file must be valid according to <code>composer validate</code>. See <a href=\":url\">Composer's documentation</a> for more information.", [':url' => 'https://getcomposer.org/doc/03-cli.md#validate']) . '</li>';
$output .= ' <li>' . t('Composer must be configured for secure downloads. This means that <a href=":disable-tls">the <code>disable-tls</code> option</a> must be <code>false</code>, and <a href=":secure-http">the <code>secure-http</code> option</a> must be <code>true</code> in the <code>config</code> section of your <code>composer.json</code> file. If these options are not set in your <code>composer.json</code>, Composer will behave securely by default. To set these values at the command line, run the following commands:', [
':disable-tls' => 'https://getcomposer.org/doc/06-config.md#disable-tls',
':secure-http' => 'https://getcomposer.org/doc/06-config.md#secure-http',
]);
$output .= '<pre><code>';
$output .= "composer config --unset disable-tls\n";
$output .= "composer config --unset secure-http\n";
$output .= '</code></pre></li></ul>';
$output .= '<h3 id="package-manager-limitations">' . t('Limitations') . '</h3>';
$output .= '<p>' . t("Because Package Manager modifies the current site's code base, it is intentionally limited in certain ways to prevent unexpected changes to the live site:") . '</p>';
......@@ -38,20 +46,56 @@ function package_manager_help($route_name, RouteMatchInterface $route_match) {
$output .= ' <li>' . t('It associates the temporary copy of the site with the user or session that originally created it, and only that user or session can make changes to it.') . '</li>';
$output .= ' <li>' . t('It does not allow modules to be uninstalled while syncing changes into live site.') . '</li>';
$output .= '</ul>';
$output .= '<p>' . t('For more information, see the <a href=":package-manager-documentation">online documentation for the Package Manager module</a>.', [':package-manager-documentation' => 'https://www.drupal.org/docs/8/core/modules/package-manager']) . '</p>';
$output .= '<p>' . t('For more information, see the <a href=":url">online documentation for the Package Manager module</a>.', [':url' => 'https://www.drupal.org/docs/8/core/modules/package-manager']) . '</p>';
$output .= '<h3 id="package-manager-faq">' . t('FAQ') . '</h3>';
$output .= '<h4 id="package-manager-faq-composer-not-found">' . t('What if it says the "composer" executable cannot be found?') . '</h4>';
$output .= '<p>' . t('If the <code>composer</code> executable path cannot be automatically determined, it can be explicitly set in by adding the following line to <code>settings.php</code>:') . '</p>';
$output .= "<pre><code>\$config['package_manager.settings']['executables']['composer'] = '/full/path/to/composer.phar';</code></pre>";
$output .= '<h4 id="package-manager-composer-related-faq">' . t('FAQs related to Composer') . '</h4>';
$output .= '<ul>';
$output .= ' <li>' . t('What if it says the <code>proc_open()</code> function is disabled on your PHP installation?');
$output .= ' <p>' . t('Ask your system administrator to remove <code>proc_open()</code> from the <a href=":url">disable_functions</a> setting in <code>php.ini</code>.', [':url' => 'https://www.php.net/manual/en/ini.core.php#ini.disable-functions']) . '</p>';
$output .= ' </li>';
$output .= ' <li>' . t('What if it says the <code>composer</code> executable cannot be found?');
$output .= ' <p>' . t("If the <code>composer</code> executable's path cannot be automatically determined, it can be explicitly set by adding the following line to <code>settings.php</code>:") . '</p>';
$output .= " <pre><code>\$config['package_manager.settings']['executables']['composer'] = '/full/path/to/composer.phar';</code></pre>";
// BEGIN: DELETE FROM CORE MERGE REQUEST
$output .= '<p>' . t('Alternatively, you can use the following Drush command:') . '</p>';
$output .= '<pre><code>drush config:set package_manager.settings executables.composer /full/path/to/composer.phar</code></pre>';
$output .= ' <p>' . t('Alternatively, you can use the following Drush command:') . '</p>';
$output .= ' <pre><code>drush config:set package_manager.settings executables.composer /full/path/to/composer.phar</code></pre>';
// END: DELETE FROM CORE MERGE REQUEST
$output .= ' </li>';
$output .= ' <li>' . t('What if it says the detected version of Composer is not supported?');
$output .= ' <p>' . t('The version of the <code>composer</code> executable must satisfy <code>@version</code>. See the <a href=":url">the Composer documentation</a> for more information, or use this command to update Composer:', ['@version' => ComposerInspector::SUPPORTED_VERSION, ':url' => 'https://getcomposer.org/doc/03-cli.md#self-update-selfupdate']) . '</p>';
$output .= ' <pre><code>composer self-update</code></pre>';
$output .= ' </li>';
$output .= ' <li>' . t('What if it says the <code>composer validate</code> command failed?');
$output .= ' <p>' . t('Composer detected problems with your <code>composer.json</code> and/or <code>composer.lock</code> files, and the project is not in a completely valid state. See <a href=":url">the Composer documentation</a> for more information.', [':url' => 'https://getcomposer.org/doc/04-schema.md']) . '</p>';
$output .= ' </li>';
$output .= '</ul>';
$output .= '<h4 id="package-manager-faq-rsync">' . t('Using rsync') . '</h4>';
$output .= '<p>' . t('By default, Package Manager expects to be able to run <code>rsync</code> to copy files between the live site and the stage directory. It is strongly recommended to use <code>rsync</code>, because it is more reliable than the built-in PHP file syncer. Package Manager will try to detect the path to <code>rsync</code>, but if it cannot be detected, you can set it explicitly by adding the following line to <code>settings.php</code>:') . '</p>';
$output .= "<pre><code>\$config['package_manager.settings']['executables']['rsync'] = '/full/path/to/rsync';</code></pre>";
$output .= '<p>' . t('If <code>rsync</code> is available but Package Manager is configured to use the built-in PHP file syncer, you should also add the following to <code>settings.php</code>:') . '</p>';
$output .= "<pre><code>\$config['package_manager.settings']['file_syncer'] = 'rsync';</code></pre>";
$output .= '<h4 id="package-manager-tuf-info">' . t('Enabling PHP-TUF protection') . '</h4>';
$output .= '<p>' . t('Package Manager requires <a href=":php-tuf">PHP-TUF</a>, which implements <a href=":tuf">The Update Framework</a> as a way to help secure Composer package downloads via the <a href=":php-tuf-plugin">PHP-TUF Composer integration plugin</a>. This plugin must be installed and configured properly in order to use Package Manager.', [
':php-tuf' => 'https://github.com/php-tuf/php-tuf',
':tuf' => 'https://theupdateframework.io/',
':php-tuf-plugin' => 'https://github.com/php-tuf/composer-integration',
]) . '</p>';
$output .= '<p>' . t('To install and configure the plugin as needed, you can run the following commands:') . '</p>';
$output .= '<pre><code>';
$output .= "composer config allow-plugins.php-tuf/composer-integration true\n";
$output .= "composer require php-tuf/composer-integration";
$output .= '</code></pre>';
$output .= '<p>' . t('Package Manager currently requires the <code>https://packages.drupal.org</code> Composer repository to be defined in your <code>composer.json</code> file, since Drupal.org is currently the only package repository that has support for TUF. To set this up, run the following commands (assuming your site is based on the <code>drupal/recommended-project</code> or <code>drupal/legacy-project</code> templates):') . '</p>';
$output .= '<pre><code>';
$output .= "composer config --unset repositories.0\n";
$output .= "composer config repositories.drupal '{\"type\": \"composer\", \"url\": \"https://packages.drupal.org/8\", \"tuf\": true}'\n";
$output .= '</code></pre>';
$output .= '<h4 id="package-manager-faq-unsupported-composer-plugin">' . t('What if it says I have unsupported Composer plugins in my codebase?') . '</h4>';
$output .= '<p>' . t('A fresh Drupal installation only uses supported Composer plugins, but some modules or themes may depend on additional Composer plugins. Please <a href=":new-issue">create a new issue</a> when you encounter this.', [
$output .= '<p>' . t('A fresh Drupal installation only uses supported Composer plugins, but some modules or themes may depend on additional Composer plugins. <a href=":new-issue">Create a new issue</a> when you encounter this.', [
':new-issue' => 'https://www.drupal.org/node/add/project-issue/automatic_updates',
]) . '</p>';
$output .= '<p>' . t('It is possible to <em>trust</em> additional Composer plugins, but this requires significant expertise: understanding the code of that Composer plugin, what the effects on the file system are and how it affects the Package Manager module. Some Composer plugins could result in a broken site!') . '</p>';
......
......@@ -10,72 +10,66 @@ services:
# Basic infrastructure services for Composer Stager, overridden by us to
# provide additional functionality.
Drupal\package_manager\ProcessFactory:
public: false
Drupal\package_manager\ExecutableFinder:
public: false
decorates: 'PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface'
Drupal\package_manager\FileSyncerFactory:
public: false
PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface:
alias: 'Drupal\package_manager\ExecutableFinder'
PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface:
alias: 'Drupal\package_manager\ProcessFactory'
PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface:
factory: ['@Drupal\package_manager\FileSyncerFactory', 'create']
decorates: 'PhpTuf\ComposerStager\API\FileSyncer\Factory\FileSyncerFactoryInterface'
Drupal\package_manager\ProcessFactory:
public: false
decorates: 'PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface'
Drupal\package_manager\TranslatableStringFactory:
public: false
decorates: 'PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface'
Drupal\package_manager\NoSymlinksPointToADirectory:
public: false
decorates: 'PhpTuf\ComposerStager\API\Precondition\Service\NoSymlinksPointToADirectoryInterface'
PhpTuf\ComposerStager\API\FileSyncer\Service\FileSyncerInterface:
factory: ['@PhpTuf\ComposerStager\API\FileSyncer\Factory\FileSyncerFactoryInterface', 'create']
logger.channel.package_manager:
parent: logger.channel_base
arguments:
- 'package_manager'
logger.channel.package_manager_change_log:
parent: logger.channel_base
arguments:
- 'package_manager_change_log'
# Services provided to Drupal by Package Manager.
package_manager.beginner:
class: PhpTuf\ComposerStager\Domain\Core\Beginner\Beginner
PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface: '@package_manager.beginner'
package_manager.stager:
class: PhpTuf\ComposerStager\Domain\Core\Stager\Stager
PhpTuf\ComposerStager\Domain\Core\Stager\StagerInterface: '@package_manager.stager'
package_manager.committer:
class: PhpTuf\ComposerStager\Domain\Core\Committer\Committer
PhpTuf\ComposerStager\Domain\Core\Committer\CommitterInterface: '@package_manager.committer'
package_manager.path_locator:
class: Drupal\package_manager\PathLocator
Drupal\package_manager\PathLocator:
arguments:
$appRoot: '%app.root%'
Drupal\package_manager\PathLocator: '@package_manager.path_locator'
package_manager.failure_marker:
class: Drupal\package_manager\FailureMarker
Drupal\package_manager\FailureMarker: '@package_manager.failure_marker'
package_manager.update_data_subscriber:
class: Drupal\package_manager\EventSubscriber\UpdateDataSubscriber
Drupal\package_manager\FailureMarker:
tags:
- { name: event_subscriber }
Drupal\package_manager\EventSubscriber\UpdateDataSubscriber:
# @todo Autowire this service when https://drupal.org/i/3325557 lands.
arguments:
- '@update.manager'
# @todo Autowire this service when https://drupal.org/i/3325557 lands.
autowire: false
tags:
- { name: event_subscriber }
package_manager.composer_inspector:
class: Drupal\package_manager\ComposerInspector
Drupal\package_manager\EventSubscriber\ChangeLogger:
calls:
- [setLogger, ['@logger.channel.package_manager_change_log']]
tags:
- { name: event_subscriber }
Drupal\package_manager\ComposerInspector:
calls:
- [setLogger, ['@logger.channel.package_manager']]
Drupal\package_manager\ComposerInspector: '@package_manager.composer_inspector'
# Validators.
package_manager.validator.environment_support:
class: Drupal\package_manager\Validator\EnvironmentSupportValidator
Drupal\package_manager\Validator\EnvironmentSupportValidator:
tags:
- { name: event_subscriber }
Drupal\package_manager\Validator\EnvironmentSupportValidator: '@package_manager.validator.environment_support'
package_manager.validator.composer:
class: Drupal\package_manager\Validator\ComposerValidator
Drupal\package_manager\Validator\ComposerValidator:
tags:
- { name: event_subscriber }
package_manager.validator.disk_space:
class: Drupal\package_manager\Validator\DiskSpaceValidator
Drupal\package_manager\Validator\DiskSpaceValidator:
tags:
- { name: event_subscriber }
Drupal\package_manager\Validator\DiskSpaceValidator: '@package_manager.validator.disk_space'
package_manager.validator.pending_updates:
class: Drupal\package_manager\Validator\PendingUpdatesValidator
Drupal\package_manager\Validator\PendingUpdatesValidator:
arguments:
- '%app.root%'
- '@update.post_update_registry'
......@@ -83,123 +77,100 @@ services:
autowire: false
tags:
- { name: event_subscriber }
package_manager.validator.lock_file:
class: Drupal\package_manager\Validator\LockFileValidator
Drupal\package_manager\Validator\LockFileValidator:
tags:
- { name: event_subscriber }
package_manager.validator.file_system:
class: Drupal\package_manager\Validator\WritableFileSystemValidator
Drupal\package_manager\Validator\WritableFileSystemValidator:
tags:
- { name: event_subscriber }
Drupal\package_manager\Validator\WritableFileSystemValidator: '@package_manager.validator.file_system'
package_manager.validator.composer_settings:
class: Drupal\package_manager\Validator\ComposerSettingsValidator
Drupal\package_manager\Validator\ComposerMinimumStabilityValidator:
tags:
- { name: event_subscriber }
package_manger.validator.composer_minimum_stability:
class: Drupal\package_manager\Validator\ComposerMinimumStabilityValidator
Drupal\package_manager\Validator\MultisiteValidator:
tags:
- { name: event_subscriber }
package_manager.validator.multisite:
class: Drupal\package_manager\Validator\MultisiteValidator
Drupal\package_manager\Validator\SymlinkValidator:
tags:
- { name: event_subscriber }
package_manager.validator.symlink:
class: Drupal\package_manager\Validator\SymlinkValidator
Drupal\package_manager\Validator\DuplicateInfoFileValidator:
tags:
- { name: event_subscriber }
package_manager.validator.duplicate_info_file:
class: Drupal\package_manager\Validator\DuplicateInfoFileValidator
Drupal\package_manager\Validator\EnabledExtensionsValidator:
tags:
- { name: event_subscriber }
package_manager.validator.overwrite_existing_packages:
class: Drupal\package_manager\Validator\OverwriteExistingPackagesValidator
Drupal\package_manager\Validator\OverwriteExistingPackagesValidator:
tags:
- { name: event_subscriber }
package_manager.validator.allowed_scaffold_packages:
class: Drupal\package_manager\Validator\AllowedScaffoldPackagesValidator
Drupal\package_manager\Validator\AllowedScaffoldPackagesValidator:
tags:
- { name: event_subscriber }
package_manager.validator.staged_database_updates:
class: Drupal\package_manager\Validator\StagedDBUpdateValidator
Drupal\package_manager\Validator\StagedDBUpdateValidator:
tags:
- { name: event_subscriber }
Drupal\package_manager\Validator\StagedDBUpdateValidator: '@package_manager.validator.staged_database_updates'
package_manager.test_site_excluder:
class: Drupal\package_manager\PathExcluder\TestSiteExcluder
Drupal\package_manager\PathExcluder\TestSiteExcluder:
tags:
- { name: event_subscriber }
package_manager.vendor_hardening_excluder:
class: Drupal\package_manager\PathExcluder\VendorHardeningExcluder
Drupal\package_manager\PathExcluder\VendorHardeningExcluder:
tags:
- { name: event_subscriber }
package_manager.site_files_excluder:
class: Drupal\package_manager\PathExcluder\SiteFilesExcluder
Drupal\package_manager\PathExcluder\SiteFilesExcluder:
tags:
- { name: event_subscriber }
package_manager.sqlite_excluder:
class: Drupal\package_manager\PathExcluder\SqliteDatabaseExcluder
Drupal\package_manager\PathExcluder\SqliteDatabaseExcluder:
tags:
- { name: event_subscriber }
Drupal\package_manager\PathExcluder\SqliteDatabaseExcluder: '@package_manager.sqlite_excluder'
package_manager.git_excluder:
class: Drupal\package_manager\PathExcluder\GitExcluder
Drupal\package_manager\PathExcluder\GitExcluder:
tags:
- { name: event_subscriber }
package_manager.unknown_path_excluder:
class: Drupal\package_manager\PathExcluder\UnknownPathExcluder
Drupal\package_manager\PathExcluder\UnknownPathExcluder:
tags:
- { name: event_subscriber }
package_manager.site_configuration_excluder:
class: Drupal\package_manager\PathExcluder\SiteConfigurationExcluder
calls:
- [setLogger, ['@logger.channel.package_manager']]
Drupal\package_manager\PathExcluder\SiteConfigurationExcluder:
arguments:
$sitePath: '%site.path%'
tags:
- { name: event_subscriber }
Drupal\package_manager\PathExcluder\SiteConfigurationExcluder: '@package_manager.site_configuration_excluder'
package_manager.node_modules_excluder:
class: Drupal\package_manager\PathExcluder\NodeModulesExcluder
Drupal\package_manager\PathExcluder\NodeModulesExcluder:
tags:
- { name: event_subscriber }
package_manager.uninstall_validator:
class: Drupal\package_manager\PackageManagerUninstallValidator
Drupal\package_manager\PackageManagerUninstallValidator:
tags:
- { name: module_install.uninstall_validator }
parent: container.trait
calls:
- ['setContainer', ['@service_container']]
arguments:
$eventDispatcher: '@event_dispatcher'
lazy: true
package_manager.validator.settings:
class: Drupal\package_manager\Validator\SettingsValidator
Drupal\package_manager\Validator\SettingsValidator:
tags:
- { name: event_subscriber }
package_manager.validator.composer_plugins:
class: Drupal\package_manager\Validator\ComposerPluginsValidator
Drupal\package_manager\Validator\RsyncValidator:
tags:
- { name: event_subscriber }
package_manager.validator.patches:
class: Drupal\package_manager\Validator\ComposerPatchesValidator
Drupal\package_manager\Validator\ComposerPluginsValidator:
tags:
- { name: event_subscriber }
package_manager.validator.base_requirements_fulfilled:
class: Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator
Drupal\package_manager\Validator\ComposerPatchesValidator:
tags:
- { name: event_subscriber }
Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator: '@package_manager.validator.base_requirements_fulfilled'
package_manager.validator.supported_releases:
class: Drupal\package_manager\Validator\SupportedReleaseValidator
Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator:
tags:
- { name: event_subscriber }
package_manager.validator.stage_not_in_active:
class: Drupal\package_manager\Validator\StageNotInActiveValidator
Drupal\package_manager\Validator\SupportedReleaseValidator:
tags:
- { name: event_subscriber }
package_manager.validator.xdebug:
class: Drupal\package_manager\Validator\XdebugValidator
Drupal\package_manager\Validator\StageNotInActiveValidator:
tags:
- { name: event_subscriber }
package_manager.update_processor:
class: Drupal\package_manager\PackageManagerUpdateProcessor
Drupal\package_manager\Validator\PhpExtensionsValidator:
tags:
- { name: event_subscriber }
# @todo Tag this service as an event subscriber in https://drupal.org/i/3358504,
# once packages.drupal.org supports TUF.
Drupal\package_manager\Validator\PhpTufValidator:
arguments:
$baseUrl: 'https://packages.drupal.org'
Drupal\package_manager\PackageManagerUpdateProcessor:
arguments:
# @todo Autowire $update_fetcher when https://drupal.org/i/3325557 lands.
$update_fetcher: '@update.fetcher'
......
......@@ -7,11 +7,11 @@ namespace Drupal\package_manager;
use Composer\Semver\Semver;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\Exception\ComposerNotReadyException;
use PhpTuf\ComposerStager\Domain\Exception\PreconditionException;
use PhpTuf\ComposerStager\Domain\Exception\RuntimeException;
use PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface;
use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface;
use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
use PhpTuf\ComposerStager\API\Exception\PreconditionException;
use PhpTuf\ComposerStager\API\Exception\RuntimeException;
use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
use PhpTuf\ComposerStager\API\Precondition\Service\ComposerIsAvailableInterface;
use PhpTuf\ComposerStager\API\Process\Service\ComposerProcessRunnerInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
......@@ -50,31 +50,27 @@ class ComposerInspector implements LoggerAwareInterface {
/**
* A semantic version constraint for the supported version(s) of Composer.
*
* Only versions supported by Composer are supported: the LTS and the latest
* minor version. Those are currently 2.2 and 2.5.
*
* @see https://endoflife.date/composer
*
* Note that Composer <= 2.2.11 is not supported anymore due to a security
* vulnerability.
*
* @see https://blog.packagist.com/cve-2022-24828-composer-command-injection-vulnerability/
*
* @var string
*/
final public const SUPPORTED_VERSION = '~2.2.12 || ^2.5';
final public const SUPPORTED_VERSION = '^2.6';
/**
* Constructs a ComposerInspector object.
*
* @param \PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface $runner
* @param \PhpTuf\ComposerStager\API\Process\Service\ComposerProcessRunnerInterface $runner
* The Composer runner service from Composer Stager.
* @param \PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface $composerIsAvailable
* @param \PhpTuf\ComposerStager\API\Precondition\Service\ComposerIsAvailableInterface $composerIsAvailable
* The Composer Stager precondition to ensure that Composer is available.
* @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $pathFactory
* @param \PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface $pathFactory
* The path factory service from Composer Stager.
*/
public function __construct(private ComposerRunnerInterface $runner, private ComposerIsAvailableInterface $composerIsAvailable, private PathFactoryInterface $pathFactory) {
public function __construct(
private readonly ComposerProcessRunnerInterface $runner,
private readonly ComposerIsAvailableInterface $composerIsAvailable,
private readonly PathFactoryInterface $pathFactory
) {
$this->processCallback = new ProcessOutputCallback();
$this->setLogger(new NullLogger());
}
......@@ -126,9 +122,7 @@ class ComposerInspector implements LoggerAwareInterface {
'--check-lock',
'--no-check-publish',
'--with-dependencies',
'--no-interaction',
'--ansi',
'--no-cache',
'--no-ansi',
"--working-dir=$working_dir",
]);
}
......@@ -173,7 +167,7 @@ class ComposerInspector implements LoggerAwareInterface {
// The "Composer is available" precondition requires active and stage
// directories, but they don't actually matter to it, nor do path
// exclusions, so dummies can be passed for simplicity.
$active_dir = $this->pathFactory::create(__DIR__);
$active_dir = $this->pathFactory->create(__DIR__);
$stage_dir = $active_dir;
$this->composerIsAvailable->assertIsFulfilled($active_dir, $stage_dir);
......@@ -237,6 +231,7 @@ class ComposerInspector implements LoggerAwareInterface {
* but if it is a boolean, an array or a map, JSON decoding should be
* applied.
*
* @see ::getAllowPluginsConfig()
* @see \Composer\Command\ConfigCommand::execute()
*/
public function getConfig(string $key, string $context): ?string {
......@@ -258,12 +253,7 @@ class ComposerInspector implements LoggerAwareInterface {
catch (RuntimeException $e) {
// Assume any error from `composer config` is about an undefined key-value
// pair which may have a known default value.
// @todo Remove this in https://www.drupal.org/i/3350568.
switch ($key) {
// @see https://getcomposer.org/doc/04-schema.md#minimum-stability
case 'minimum-stability':
return 'stable';
case 'extra':
return '{}';
......@@ -285,7 +275,7 @@ class ComposerInspector implements LoggerAwareInterface {
* @throws \UnexpectedValueException
* Thrown if the Composer version cannot be determined.
*/
private function getVersion(): string {
public function getVersion(): string {
$this->runner->run(['--format=json'], $this->processCallback->reset());
$data = $this->processCallback->parseJsonOutput();
if (isset($data['application']['name'])
......@@ -323,23 +313,33 @@ class ComposerInspector implements LoggerAwareInterface {
$packages_data = $this->getPackageTypes($packages_data, $working_dir);
foreach ($packages_data as $name => $package) {
$path = realpath($package['path']);
$path = $package['path'];
// For packages installed as dev snapshots from certain version control
// systems, `composer show` displays the version like `1.0.x-dev 0a1b2c`,
// which will cause an exception if we try to parse it as a legitimate
// semantic version. Since we don't need the abbreviated commit hash, just
// remove it.
if (str_contains($package['version'], '-dev ')) {
$packages_data[$name]['version'] = explode(' ', $package['version'], 2)[0];
}
// We expect Composer to report that metapackages' install paths are the
// same as the working directory, in which case InstalledPackage::$path
// should be NULL. For all other package types, we consider it invalid
// if the install path is the same as the working directory.
if ($package['type'] === 'metapackage') {
if ($path === $working_dir) {
$packages_data[$name]['path'] = NULL;
}
else {
throw new \UnexpectedValueException("Metapackage '$name' is installed at unexpected path: '$path', expected '$working_dir'");
if (isset($package['type']) && $package['type'] === 'metapackage') {
if ($path !== NULL) {
throw new \UnexpectedValueException("Metapackage '$name' is installed at unexpected path: '$path', expected NULL");
}
$packages_data[$name]['path'] = $path;
}
elseif ($path === $working_dir) {
throw new \UnexpectedValueException("Package '$name' cannot be installed at path: '$path'");
}
else {
$packages_data[$name]['path'] = realpath($path);
}
}
$packages_data = array_map(InstalledPackage::createFromArray(...), $packages_data);
......@@ -471,33 +471,26 @@ class ComposerInspector implements LoggerAwareInterface {
}
/**
* Tries to convert a string value from ::getConfig() to a boolean.
* Returns the value of `allow-plugins` config setting.
*
* For boolean config values, `composer config` returns `true` or `false` as
* of Composer 2.5.5, but older versions return `1` or `0`. This function
* normalizes the discrepancy.
*
* You should call this method if you're calling ::getConfig() to retrieve a
* config value that will be, or may be a boolean (e.g., `allow-plugins`). See
* https://getcomposer.org/doc/06-config.md for documentation on all the
* possible config values that `composer config` can expose.
*
* @param string $value
* The value to convert. Must be one of '1', 'true', '0', or 'false'.
*
* @return bool
* The value, as a boolean.
* @param string $dir
* The directory in which to run Composer.
*
* @see https://getcomposer.org/doc/06-config.md
* @return bool[]|bool
* An array of boolean flags to allow or disallow certain plugins, or TRUE
* if all plugins are allowed.
*
* @throws \UnhandledMatchError
* If the given value cannot be converted to a boolean.
* @see https://getcomposer.org/doc/06-config.md#allow-plugins
*/
public static function toBoolean(string $value): bool {
return match ($value) {
'1', 'true' => TRUE,
'0', 'false' => FALSE,
};
public function getAllowPluginsConfig(string $dir): array|bool {
$value = $this->getConfig('allow-plugins', $dir);
// Try to convert the value we got back to a boolean. If it's not a boolean,
// it should be an array of plugin-specific flags.
$value = json_decode($value, TRUE, flags: JSON_THROW_ON_ERROR);
// An empty array indicates that no plugins are allowed.
return $value ?: [];
}
}
......@@ -5,8 +5,10 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\package_manager\StageBase;
use PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface;
use PhpTuf\ComposerStager\Infrastructure\Value\PathList\PathList;
use Drupal\package_manager\PathLocator;
use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
use PhpTuf\ComposerStager\API\Path\Factory\PathListFactoryInterface;
use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
/**
* Defines an event that collects paths to exclude.
......@@ -14,35 +16,114 @@ use PhpTuf\ComposerStager\Infrastructure\Value\PathList\PathList;
* These paths are excluded by Composer Stager and are never copied into the
* stage directory from the active directory, or vice-versa.
*/
class CollectPathsToExcludeEvent extends StageEvent implements PathListInterface {
final class CollectPathsToExcludeEvent extends StageEvent implements PathListInterface {
/**
* The list of paths to exclude.
* Constructs a CollectPathsToExcludeEvent object.
*
* @var \PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
* @param \PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface $pathFactory
* The path factory service.
* @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface|null $pathList
* (optional) The list of paths to exclude.
*/
protected PathListInterface $pathList;
public function __construct(
StageBase $stage,
private readonly PathLocator $pathLocator,
private readonly PathFactoryInterface $pathFactory,
private ?PathListInterface $pathList = NULL,
) {
parent::__construct($stage);
$this->pathList ??= \Drupal::service(PathListFactoryInterface::class)
->create();
}
/**
* {@inheritdoc}
*/
public function __construct(StageBase $stage) {
parent::__construct($stage);
$this->pathList = new PathList([]);
public function add(string ...$paths): void {
$this->pathList->add(...$paths);
}
/**
* {@inheritdoc}
*/
public function add(array $paths): void {
$this->pathList->add($paths);
public function getAll(): array {
return array_unique($this->pathList->getAll());
}
/**
* {@inheritdoc}
* Flags paths to be ignored, relative to the web root.
*
* This should only be used for paths that, if they exist at all, are
* *guaranteed* to exist within the web root.
*
* @param string[] $paths
* The paths to ignore. These should be relative to the web root, and will
* be made relative to the project root.
*/
public function getAll(): array {
return $this->pathList->getAll();
public function addPathsRelativeToWebRoot(array $paths): void {
$web_root = $this->pathLocator->getWebRoot();
if ($web_root) {
$web_root .= '/';
}
foreach ($paths as $path) {
// Make the path relative to the project root by prefixing the web root.
$this->add($web_root . $path);
}
}
/**
* Flags paths to be ignored, relative to the project root.
*
* @param string[] $paths
* The paths to ignore. Absolute paths will be made relative to the project
* root; relative paths will be assumed to already be relative to the
* project root, and ignored as given.
*
* @throws \LogicException
* If any of the given paths are absolute, but not inside the project root.
*/
public function addPathsRelativeToProjectRoot(array $paths): void {
$project_root = $this->pathLocator->getProjectRoot();
foreach ($paths as $path) {
if ($this->pathFactory->create($path)->isAbsolute()) {
if (!str_starts_with($path, $project_root)) {
throw new \LogicException("$path is not inside the project root: $project_root.");
}
}
// Make absolute paths relative to the project root.
$path = str_replace($project_root, '', $path);
$path = ltrim($path, '/');
$this->add($path);
}
}
/**
* Finds all directories in the project root matching the given name.
*
* @param string $directory_name
* The directory name to scan for.
*
* @return string[]
* All discovered absolute paths matching the given directory name.
*/
public function scanForDirectoriesByName(string $directory_name): array {
$flags = \FilesystemIterator::UNIX_PATHS;
$flags |= \FilesystemIterator::CURRENT_AS_SELF;
$directories_tree = new \RecursiveDirectoryIterator($this->pathLocator->getProjectRoot(), $flags);
$filtered_directories = new \RecursiveIteratorIterator($directories_tree, \RecursiveIteratorIterator::SELF_FIRST);
$matched_directories = new \CallbackFilterIterator($filtered_directories,
fn (\RecursiveDirectoryIterator $current) => $current->isDir() && $current->getFilename() === $directory_name
);
return array_keys(iterator_to_array($matched_directories));
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Event;
/**
* Common functionality for events which can collect excluded paths.
*/
trait ExcludedPathsTrait {
/**
* Paths to exclude from the update.
*
* @var string[]
*/
protected $excludedPaths = [];
/**
* Returns the paths to exclude from the current operation.
*
* @return string[]
* The paths to exclude.
*/
public function getExcludedPaths(): array {
return array_unique($this->excludedPaths);
}
}
......@@ -7,5 +7,5 @@ namespace Drupal\package_manager\Event;
/**
* Event fired after staged changes are synced to the active directory.
*/
class PostApplyEvent extends StageEvent {
final class PostApplyEvent extends StageEvent {
}
......@@ -7,5 +7,5 @@ namespace Drupal\package_manager\Event;
/**
* Event fired after a stage directory has been created.
*/
class PostCreateEvent extends StageEvent {
final class PostCreateEvent extends StageEvent {
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Event;
/**
* Event fired after the stage directory is destroyed.
*
* If the stage is being force destroyed, $this->stage may be an object of a
* different class than the one that originally created the stage directory.
*
* @see \Drupal\package_manager\StageBase::destroy()
*/
class PostDestroyEvent extends StageEvent {
}
......@@ -7,7 +7,7 @@ namespace Drupal\package_manager\Event;
/**
* Event fired after packages are updated to the stage directory.
*/
class PostRequireEvent extends StageEvent {
final class PostRequireEvent extends StageEvent {
use RequireEventTrait;
......
......@@ -4,29 +4,36 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\package_manager\ImmutablePathList;
use Drupal\package_manager\StageBase;
use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
/**
* Event fired before staged changes are synced to the active directory.
*/
class PreApplyEvent extends PreOperationStageEvent {
final class PreApplyEvent extends PreOperationStageEvent {
use ExcludedPathsTrait;
/**
* The list of paths to ignore in the active and stage directories.
*
* @var \Drupal\package_manager\ImmutablePathList
*/
public readonly ImmutablePathList $excludedPaths;
/**
* Constructs a PreApplyEvent object.
*
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param string[] $paths_to_exclude
* @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface $excluded_paths
* The list of paths to exclude. These will not be copied from the stage
* directory to the active directory, nor be deleted from the active
* directory if they exist, when the stage directory is copied back into
* the active directory.
*/
public function __construct(StageBase $stage, array $paths_to_exclude) {
public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
parent::__construct($stage);
$this->excludedPaths = $paths_to_exclude;
$this->excludedPaths = new ImmutablePathList($excluded_paths);
}
}
......@@ -4,27 +4,34 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\package_manager\ImmutablePathList;
use Drupal\package_manager\StageBase;
use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
/**
* Event fired before a stage directory is created.
*/
class PreCreateEvent extends PreOperationStageEvent {
final class PreCreateEvent extends PreOperationStageEvent {
use ExcludedPathsTrait;
/**
* The list of paths to exclude from the stage directory.
*
* @var \Drupal\package_manager\ImmutablePathList
*/
public readonly ImmutablePathList $excludedPaths;
/**
* Constructs a PreCreateEvent object.
*
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param string[] $paths_to_exclude
* @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface $excluded_paths
* The list of paths to exclude. These will not be copied into the stage
* directory when it is created.
*/
public function __construct(StageBase $stage, array $paths_to_exclude) {
public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
parent::__construct($stage);
$this->excludedPaths = $paths_to_exclude;
$this->excludedPaths = new ImmutablePathList($excluded_paths);
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Event;
/**
* Event fired before the stage directory is destroyed.
*
* If the stage is being force destroyed, $this->stage may be an object of a
* different class than the one that originally created the stage directory.
*
* @see \Drupal\package_manager\StageBase::destroy()
*/
class PreDestroyEvent extends PreOperationStageEvent {
}
......@@ -40,7 +40,7 @@ abstract class PreOperationStageEvent extends StageEvent {
}
/**
* Adds error information to the event.
* Convenience method, adds error validation result.
*
* @param \Drupal\Core\StringTranslation\TranslatableMarkup[] $messages
* The error messages.
......@@ -49,11 +49,11 @@ abstract class PreOperationStageEvent extends StageEvent {
* is more than one message.
*/
public function addError(array $messages, ?TranslatableMarkup $summary = NULL): void {
$this->results[] = ValidationResult::createError(array_values($messages), $summary);
$this->addResult(ValidationResult::createError(array_values($messages), $summary));
}
/**
* Adds an error from a throwable.
* Convenience method, adds an error validation result from a throwable.
*
* @param \Throwable $throwable
* The throwable.
......@@ -61,7 +61,24 @@ abstract class PreOperationStageEvent extends StageEvent {
* (optional) The summary of error messages.
*/
public function addErrorFromThrowable(\Throwable $throwable, ?TranslatableMarkup $summary = NULL): void {
$this->results[] = ValidationResult::createErrorFromThrowable($throwable, $summary);
$this->addResult(ValidationResult::createErrorFromThrowable($throwable, $summary));
}
/**
* Adds a validation result to the event.
*
* @param \Drupal\package_manager\ValidationResult $result
* The validation result to add.
*
* @throws \InvalidArgumentException
* Thrown if the validation result is not an error.
*/
public function addResult(ValidationResult $result): void {
// Only errors are allowed for this event.
if ($result->severity !== SystemManager::REQUIREMENT_ERROR) {
throw new \InvalidArgumentException('Only errors are allowed.');
}
$this->results[] = $result;
}
/**
......
......@@ -7,7 +7,7 @@ namespace Drupal\package_manager\Event;
/**
* Event fired before packages are updated to the stage directory.
*/
class PreRequireEvent extends PreOperationStageEvent {
final class PreRequireEvent extends PreOperationStageEvent {
use RequireEventTrait;
......
......@@ -5,9 +5,10 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\package_manager\ImmutablePathList;
use Drupal\package_manager\StageBase;
use Drupal\package_manager\ValidationResult;
use Drupal\system\SystemManager;
use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
/**
* Event fired to check the status of the system to use Package Manager.
......@@ -15,39 +16,41 @@ use Drupal\system\SystemManager;
* The event's stage will be set with the type of stage that will perform the
* operations. The stage may or may not be currently in use.
*/
class StatusCheckEvent extends PreOperationStageEvent {
final class StatusCheckEvent extends PreOperationStageEvent {
/**
* Returns paths to exclude or NULL if a base requirement is not fulfilled.
* The paths to exclude, or NULL if there was an error collecting them.
*
* @return string[]|null
* The paths to exclude, or NULL if a base requirement is not fulfilled.
* @var \Drupal\package_manager\ImmutablePathList|null
*
* @throws \LogicException
* Thrown if the excluded paths are NULL and no errors have been added to
* this event.
* @see ::__construct()
*/
public function getExcludedPaths(): ?array {
if (isset($this->pathsToExclude)) {
return array_unique($this->pathsToExclude);
}
if (empty($this->getResults(SystemManager::REQUIREMENT_ERROR))) {
throw new \LogicException('$paths_to_exclude should only be NULL if the error that caused the paths to not be collected was added to the status check event.');
}
return NULL;
}
public readonly ?ImmutablePathList $excludedPaths;
/**
* Constructs a StatusCheckEvent object.
*
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param string[]|null $pathsToExclude
* The list of paths to exclude, or NULL if they could not be collected.
* @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface|\Throwable $excluded_paths
* The list of paths to exclude or, if an error occurred while they were
* being collected, the throwable from that error. If this is a throwable,
* it will be converted to a validation error.
*/
public function __construct(StageBase $stage, private ?array $pathsToExclude) {
public function __construct(StageBase $stage, PathListInterface|\Throwable $excluded_paths) {
parent::__construct($stage);
// If there was an error collecting the excluded paths, convert it to a
// validation error so we can still run status checks that don't need to
// examine the list of excluded paths.
if ($excluded_paths instanceof \Throwable) {
$this->addErrorFromThrowable($excluded_paths);
$excluded_paths = NULL;
}
else {
$excluded_paths = new ImmutablePathList($excluded_paths);
}
$this->excludedPaths = $excluded_paths;
}
/**
......@@ -60,7 +63,15 @@ class StatusCheckEvent extends PreOperationStageEvent {
* message, optional otherwise.
*/
public function addWarning(array $messages, ?TranslatableMarkup $summary = NULL): void {
$this->results[] = ValidationResult::createWarning($messages, $summary);
$this->addResult(ValidationResult::createWarning($messages, $summary));
}
/**
* {@inheritdoc}
*/
public function addResult(ValidationResult $result): void {
// Override the parent to also allow warnings.
$this->results[] = $result;
}
}