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 503 additions and 416 deletions
# https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
# Copied from https://git.drupalcode.org/project/ckeditor5/-/blob/c68be57fbc2c29969e9726ec932af3f0e80c3dd9/drupalci.yml
# cspell:disable
build:
assessment:
# validate_codebase:
# automatic_updates code quality checking matches that of Drupal core: it is checked by container_command.commit_checks.
testing:
# Run code quality checks.
container_command.commit-checks:
commands:
# @todo Replace in favor of commit-code-check.sh once https://www.drupal.org/project/drupal/issues/3314100 lands.
- modules/contrib/automatic_updates/scripts/commit-code-check.sh --drupalci
halt-on-fail: true
# run_tests task is executed several times in order of performance speeds.
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
# suppress-deprecations is false in order to be alerted to usages of
# deprecated code.
run_tests.phpunit:
types: 'PHPUnit-Unit'
testgroups: '--all'
suppress-deprecations: false
halt-on-fail: false
run_tests.kernel:
types: 'PHPUnit-Kernel'
testgroups: '--all'
suppress-deprecations: false
halt-on-fail: false
run_tests.build:
# Limit concurrency due to disk space concerns.
concurrency: 15
types: 'PHPUnit-Build'
testgroups: '--all'
suppress-deprecations: false
halt-on-fail: false
run_tests.functional:
types: 'PHPUnit-Functional'
testgroups: '--all'
suppress-deprecations: false
halt-on-fail: false
# Functional JavaScript tests require a concurrency of 1 because there is
# only one instance of PhantomJS on the testbot machine.
#run_tests.javascript:
# concurrency: 1
# types: 'PHPUnit-FunctionalJavascript'
# testgroups: '--all'
# suppress-deprecations: false
# halt-on-fail: false
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'
# This file exists so that \Drupal\package_manager\InstalledPackagesList knows
# which installed packages are considered part of Drupal core. There's no way
# to tell by package type alone, since these packages have varying types, but
# are all part of Drupal core's repository. This file is for internal use and
# may be changed or removed at any time. Code external to Package Manager
# should not use it in any way.
[
drupal/core,
drupal/core-composer-scaffold,
drupal/core-dev,
drupal/core-dev-pinned,
drupal/core-project-message,
drupal/core-recommended,
drupal/core-vendor-hardening
]
......@@ -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,23 +247,26 @@
* 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
* irrelevant to Composer or Package Manager. Examples include settings.php
* and related files, public and private files, SQLite databases, and git
* repositories. Custom code can subscribe to
* Drupal\package_manager\Event\CollectIgnoredPathsEvent to flag paths which
* Drupal\package_manager\Event\CollectPathsToExcludeEvent to flag paths which
* should never be copied into the stage directory from the active directory or
* vice versa.
*
* @see \Drupal\package_manager\Event\CollectIgnoredPathsEvent
* @see \Drupal\package_manager\Event\CollectPathsToExcludeEvent
*
* @section sec_services Useful services
* The following services are especially useful to validators:
......@@ -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.1
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,20 +44,49 @@ 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;
}
// BEGIN: DELETE FROM CORE MERGE REQUEST
/**
* Sets default value for new additional_trusted_composer_plugins configuration.
*
* @see https://www.drupal.org/project/automatic_updates/issues/3331168
* Implements hook_update_last_removed().
*/
function package_manager_update_9001(): void {
/** @var \Drupal\Core\Config\Config $config */
$config = \Drupal::service('config.factory')->getEditable('package_manager.settings');
$config->set('additional_trusted_composer_plugins', [])->save();
function package_manager_update_last_removed(): int {
return 9001;
}
// END: DELETE FROM CORE MERGE REQUEST
......@@ -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,66 +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\ComposerInspector: '@package_manager.composer_inspector'
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']]
# 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'
......@@ -77,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,12 +7,15 @@ 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\ProcessOutputCallback\ProcessOutputCallbackInterface;
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;
use Psr\Log\NullLogger;
/**
* Defines a class to get information from Composer.
......@@ -23,16 +26,19 @@ use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
* - read project & package configuration: getConfig() (`composer config`)
* - read root package info: getRootPackageInfo() (`composer show --self`)
*/
class ComposerInspector {
class ComposerInspector implements LoggerAwareInterface {
use LoggerAwareTrait {
setLogger as traitSetLogger;
}
use StringTranslationTrait;
/**
* The JSON process output callback.
* The process output callback.
*
* @var \Drupal\package_manager\JsonProcessOutputCallback
* @var \Drupal\package_manager\ProcessOutputCallback
*/
private JsonProcessOutputCallback $jsonCallback;
private ProcessOutputCallback $processCallback;
/**
* Statically cached installed package lists, keyed by directory.
......@@ -44,32 +50,37 @@ class ComposerInspector {
/**
* 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) {
$this->jsonCallback = new JsonProcessOutputCallback();
public function __construct(
private readonly ComposerProcessRunnerInterface $runner,
private readonly ComposerIsAvailableInterface $composerIsAvailable,
private readonly PathFactoryInterface $pathFactory
) {
$this->processCallback = new ProcessOutputCallback();
$this->setLogger(new NullLogger());
}
/**
* {@inheritdoc}
*/
public function setLogger(LoggerInterface $logger): void {
$this->traitSetLogger($logger);
$this->processCallback->setLogger($logger);
}
/**
......@@ -111,9 +122,7 @@ class ComposerInspector {
'--check-lock',
'--no-check-publish',
'--with-dependencies',
'--no-interaction',
'--ansi',
'--no-cache',
'--no-ansi',
"--working-dir=$working_dir",
]);
}
......@@ -158,7 +167,7 @@ class ComposerInspector {
// 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);
......@@ -222,6 +231,7 @@ class ComposerInspector {
* 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 {
......@@ -237,42 +247,13 @@ class ComposerInspector {
$this->validateProject($context);
$command[] = "--working-dir={$context}";
}
// For whatever reason, PHPCS thinks that $output is not used, even though
// it very clearly *is*. So, shut PHPCS up for the duration of this method.
// phpcs:disable DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable
$callback = new class () implements ProcessOutputCallbackInterface {
/**
* The command output.
*
* @var string
*/
public string $output = '';
/**
* {@inheritdoc}
*/
public function __invoke(string $type, string $buffer): void {
if ($type === ProcessOutputCallbackInterface::OUT) {
$this->output .= trim($buffer);
}
}
};
// phpcs:enable
try {
$this->runner->run($command, $callback);
$this->runner->run($command, $this->processCallback->reset());
}
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 once https://github.com/composer/composer/issues/11302 lands and ships in a composer release.
switch ($key) {
// @see https://getcomposer.org/doc/04-schema.md#minimum-stability
case 'minimum-stability':
return 'stable';
case 'extra':
return '{}';
......@@ -281,7 +262,8 @@ class ComposerInspector {
throw $e;
}
}
return $callback->output;
$output = $this->processCallback->getOutput();
return isset($output) ? trim($output) : $output;
}
/**
......@@ -291,11 +273,11 @@ class ComposerInspector {
* The Composer version.
*
* @throws \UnexpectedValueException
* Thrown if the expect data format is not found.
* Thrown if the Composer version cannot be determined.
*/
private function getVersion(): string {
$this->runner->run(['--format=json'], $this->jsonCallback);
$data = $this->jsonCallback->getOutputData();
public function getVersion(): string {
$this->runner->run(['--format=json'], $this->processCallback->reset());
$data = $this->processCallback->parseJsonOutput();
if (isset($data['application']['name'])
&& isset($data['application']['version'])
&& $data['application']['name'] === 'Composer'
......@@ -320,6 +302,7 @@ class ComposerInspector {
* working directory, and it is not of the `metapackage` type.
*/
public function getInstalledPackagesList(string $working_dir): InstalledPackagesList {
$working_dir = realpath($working_dir);
$this->validate($working_dir);
if (array_key_exists($working_dir, $this->packageLists)) {
......@@ -332,21 +315,31 @@ class ComposerInspector {
foreach ($packages_data as $name => $package) {
$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'");
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);
......@@ -376,7 +369,7 @@ class ComposerInspector {
*/
private function getPackageTypes(array $packages_data, string $working_dir): array {
$lock_content = file_get_contents($working_dir . DIRECTORY_SEPARATOR . 'composer.lock');
$lock_data = json_decode($lock_content, TRUE, 512, JSON_THROW_ON_ERROR);
$lock_data = json_decode($lock_content, TRUE, flags: JSON_THROW_ON_ERROR);
$lock_packages = array_merge($lock_data['packages'] ?? [], $lock_data['packages-dev'] ?? []);
foreach ($lock_packages as $lock_package) {
......@@ -400,8 +393,8 @@ class ComposerInspector {
public function getRootPackageInfo(string $working_dir): array {
$this->validate($working_dir);
$this->runner->run(['show', '--self', '--format=json', "--working-dir={$working_dir}"], $this->jsonCallback);
return $this->jsonCallback->getOutputData();
$this->runner->run(['show', '--self', '--format=json', "--working-dir={$working_dir}"], $this->processCallback->reset());
return $this->processCallback->parseJsonOutput();
}
/**
......@@ -423,8 +416,8 @@ class ComposerInspector {
// about the installed packages. So, to work around this maddening quirk, we
// call `composer show` once without the --path option, and once with it,
// then merge the results together.
$this->runner->run($options, $this->jsonCallback);
$output = $this->jsonCallback->getOutputData();
$this->runner->run($options, $this->processCallback->reset());
$output = $this->processCallback->parseJsonOutput();
// $output['installed'] will not be set if no packages are installed.
if (isset($output['installed'])) {
foreach ($output['installed'] as $installed_package) {
......@@ -432,8 +425,8 @@ class ComposerInspector {
}
$options[] = '--path';
$this->runner->run($options, $this->jsonCallback);
$output = $this->jsonCallback->getOutputData();
$this->runner->run($options, $this->processCallback->reset());
$output = $this->processCallback->parseJsonOutput();
foreach ($output['installed'] as $installed_package) {
$data[$installed_package['name']]['path'] = $installed_package['path'];
}
......@@ -477,4 +470,27 @@ class ComposerInspector {
return $invalidate;
}
/**
* Returns the value of `allow-plugins` config setting.
*
* @param string $dir
* The directory in which to run Composer.
*
* @return bool[]|bool
* An array of boolean flags to allow or disallow certain plugins, or TRUE
* if all plugins are allowed.
*
* @see https://getcomposer.org/doc/06-config.md#allow-plugins
*/
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 ?: [];
}
}
<?php
declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\package_manager\Stage;
use PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface;
use PhpTuf\ComposerStager\Infrastructure\Value\PathList\PathList;
/**
* Defines an event that collects ignored paths.
*
* Ignored paths are completely ignored by Composer Stager. They are never
* copied into the stage directory from the active directory, or vice-versa.
*/
class CollectIgnoredPathsEvent extends StageEvent implements PathListInterface {
/**
* The list of ignored paths.
*
* @var \PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface
*/
protected PathListInterface $pathList;
/**
* {@inheritdoc}
*/
public function __construct(Stage $stage) {
parent::__construct($stage);
$this->pathList = new PathList([]);
}
/**
* {@inheritdoc}
*/
public function add(array $paths): void {
$this->pathList->add($paths);
}
/**
* {@inheritdoc}
*/
public function getAll(): array {
return $this->pathList->getAll();
}
}
......@@ -2,35 +2,71 @@
declare(strict_types = 1);
namespace Drupal\package_manager\PathExcluder;
namespace Drupal\package_manager\Event;
use Drupal\package_manager\Event\CollectIgnoredPathsEvent;
use Drupal\package_manager\StageBase;
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;
/**
* Contains methods for excluding paths from stage operations.
* Defines an event that collects paths to exclude.
*
* These paths are excluded by Composer Stager and are never copied into the
* stage directory from the active directory, or vice-versa.
*/
trait PathExclusionsTrait {
final class CollectPathsToExcludeEvent extends StageEvent implements PathListInterface {
/**
* The path locator service.
* Constructs a CollectPathsToExcludeEvent object.
*
* @var \Drupal\package_manager\PathLocator
* @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 $pathLocator;
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 add(string ...$paths): void {
$this->pathList->add(...$paths);
}
/**
* Flags paths to be excluded, relative to the web root.
* {@inheritdoc}
*/
public function getAll(): array {
return array_unique($this->pathList->getAll());
}
/**
* 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 \Drupal\package_manager\Event\CollectIgnoredPathsEvent|\Drupal\package_manager\Event\StageEvent $event
* The event object.
* @param string[] $paths
* The paths to exclude. These should be relative to the web root, and will
* The paths to ignore. These should be relative to the web root, and will
* be made relative to the project root.
*/
protected function excludeInWebRoot(CollectIgnoredPathsEvent $event, array $paths): void {
public function addPathsRelativeToWebRoot(array $paths): void {
$web_root = $this->pathLocator->getWebRoot();
if ($web_root) {
$web_root .= '/';
......@@ -38,25 +74,26 @@ trait PathExclusionsTrait {
foreach ($paths as $path) {
// Make the path relative to the project root by prefixing the web root.
$event->add([$web_root . $path]);
$this->add($web_root . $path);
}
}
/**
* Flags paths to be excluded, relative to the project root.
* Flags paths to be ignored, relative to the project root.
*
* @param \Drupal\package_manager\Event\CollectIgnoredPathsEvent|\Drupal\package_manager\Event\StageEvent $event
* The event object.
* @param string[] $paths
* The paths to exclude. Absolute paths will be made relative to the project
* 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 excluded as given.
* project root, and ignored as given.
*
* @throws \LogicException
* If any of the given paths are absolute, but not inside the project root.
*/
protected function excludeInProjectRoot(CollectIgnoredPathsEvent $event, array $paths): void {
public function addPathsRelativeToProjectRoot(array $paths): void {
$project_root = $this->pathLocator->getProjectRoot();
foreach ($paths as $path) {
if (str_starts_with($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.");
}
......@@ -65,7 +102,7 @@ trait PathExclusionsTrait {
// Make absolute paths relative to the project root.
$path = str_replace($project_root, '', $path);
$path = ltrim($path, '/');
$event->add([$path]);
$this->add($path);
}
}
......@@ -78,7 +115,7 @@ trait PathExclusionsTrait {
* @return string[]
* All discovered absolute paths matching the given directory name.
*/
protected function scanForDirectoriesByName(string $directory_name): array {
public function scanForDirectoriesByName(string $directory_name): array {
$flags = \FilesystemIterator::UNIX_PATHS;
$flags |= \FilesystemIterator::CURRENT_AS_SELF;
$directories_tree = new \RecursiveDirectoryIterator($this->pathLocator->getProjectRoot(), $flags);
......
<?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\Stage::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\Stage;
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\Stage $stage
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param string[] $ignored_paths
* The list of ignored paths. These will not be copied from the stage
* @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(Stage $stage, array $ignored_paths) {
public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
parent::__construct($stage);
$this->excludedPaths = $ignored_paths;
$this->excludedPaths = new ImmutablePathList($excluded_paths);
}
}
......@@ -4,27 +4,34 @@ declare(strict_types = 1);
namespace Drupal\package_manager\Event;
use Drupal\package_manager\Stage;
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\Stage $stage
* @param \Drupal\package_manager\StageBase $stage
* The stage which fired this event.
* @param string[] $ignored_paths
* The list of ignored paths. These will not be copied into the stage
* @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(Stage $stage, array $ignored_paths) {
public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
parent::__construct($stage);
$this->excludedPaths = $ignored_paths;
$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\Stage::destroy()
*/
class PreDestroyEvent extends PreOperationStageEvent {
}