Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
178 commits
Select commit Hold shift + click to select a range
cfcf77d
ACP2E-4349: [B2B] Purchase orders not auto-converting to sales order …
dhorytskyi Jan 3, 2026
bd40864
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Feb 23, 2026
5d1d429
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Feb 23, 2026
52e12de
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Feb 27, 2026
4134ec5
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Mar 3, 2026
e25269e
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Mar 9, 2026
db6842d
ACP2E-4349: [B2B] Purchase orders not auto-converting to sales order …
dhorytskyi Mar 10, 2026
58d5f39
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Mar 13, 2026
7bcf945
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Mar 23, 2026
e6031c0
ACP2E-4765: In-store pickup order placement fails after Directory sub…
thiaramus Mar 26, 2026
578b097
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Mar 31, 2026
10bb41b
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Apr 3, 2026
4483a06
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 6, 2026
67e4291
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 6, 2026
0dbdd64
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 7, 2026
1bc6714
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 7, 2026
dac77c8
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 7, 2026
0b4d0c7
Merge branch 'develop' of github.com:magento-commerce/inventory into …
alzota Apr 7, 2026
2d3e0e8
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Apr 13, 2026
6f454a7
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Apr 14, 2026
a04c758
AC-16487::Fake Modules Require dev/ Directory in Extension Repositories
Apr 14, 2026
32f5a77
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Apr 14, 2026
8e73b1a
Merge branch 'develop' into ACP2E-4741
veloraven Apr 14, 2026
8035daf
ACP2E-4805: Configurable Product's performance impact on checkout (to…
alzota Apr 15, 2026
35d48e7
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Apr 16, 2026
c61a326
ACP2E-4741: [CLOUD] Gift card and related not sellable WF-1688
veloraven Apr 17, 2026
ccc6aa0
Merge remote-tracking branch 'origin/ACP2E-4805' into PR_2026_04_20_f…
thiaramus Apr 20, 2026
30fbdb0
Merge pull request #738 from adobe-commerce-tier-4/PR_2026_04_20_flowers
dhorytskyi Apr 21, 2026
5323ea3
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi Apr 24, 2026
4ce41b7
ACP2E-4880: Add PR Quality Gates github workflow
alzota Apr 29, 2026
854cf03
ACP2E-4880: Add PR Quality Gates github workflow
alzota Apr 29, 2026
595212d
ACP2E-4866 : Bundle Product stock status inconsistency
arnobsh Apr 29, 2026
29872fa
Merge branch 'pre-release' into 2.4.9-develop-sync
sandesh-as May 4, 2026
6d7fafd
ACP2E-4866 : Add ReindexCompositeProductsOnLegacyStockItemSavePlugin …
arnobsh May 4, 2026
ee77f97
ACQE-9572: OfflineDistanceBasedSelectionAlgorithmCustomStockTestTwoIt…
sandesh-shivakumar May 6, 2026
bc83fc3
AC-16488::Hardcoded Fixture Paths Fail in Composer Builds [test suite]
del72683 May 7, 2026
28309a1
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 7, 2026
d3525a0
ACP2E-4866: Bundle Product stock status inconsistency
bubasuma May 7, 2026
361481b
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 7, 2026
3ce68a4
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 8, 2026
cd03090
ACQE-9572: OfflineDistanceBasedSelectionAlgorithmCustomStockTestTwoIt…
sandesh-shivakumar May 8, 2026
6b5188e
ACQE-9501: [MFTF TESTS] StorefrontBundleProductChangeStockStatusCusto…
chandrakalahanchinal May 8, 2026
a28facb
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 8, 2026
bf8c344
ACP2E-4866: Bundle Product stock status inconsistency
bubasuma May 8, 2026
47dba75
ACQE-9845: Filter - Sales Order Grid
Karishmathakare19 May 11, 2026
4225889
ACQE-9557: Update test steps
chandrakalahanchinal May 11, 2026
14c6e29
Merge pull request #743 from adobe-commerce-tier-4/ACP2E-4880
dhorytskyi May 11, 2026
689b4e2
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 12, 2026
5e0535e
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 12, 2026
07510a2
ACQE-9836: Filter - Sales Order Grid
Karishmathakare19 May 12, 2026
1fc3453
Merge branch 'ACQE-9845' into ACQE-functional-deployment-v6-6
Karishmathakare19 May 12, 2026
d9671df
Merge branch 'magento-commerce:develop' into 2.4.9-develop-sync
djadobe16 May 12, 2026
2787ad4
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi May 13, 2026
f27c911
Merge pull request #741 from magento-gl/2.4.9-develop-sync
glo71317 May 13, 2026
8d4131b
Merge branch 'develop' into ACQE-functional-deployment-v6-6
Karishmathakare19 May 13, 2026
2d4f74b
Merge branch 'develop' of https://github.com/magento-commerce/invento…
dhorytskyi May 13, 2026
4db39da
Merge branch 'develop' into AC-16487
May 13, 2026
7bc848e
ACP2E-4933: PR Quality Gates bugfixes and improvements
alzota May 13, 2026
003c69e
Merge branch 'develop' of github.com:adobe-commerce-tier-4/inventory …
alzota May 13, 2026
6ac2d0a
ACP2E-4933: PR Quality Gates bugfixes and improvements
alzota May 13, 2026
012226b
ACP2E-4933: PR Quality Gates bugfixes and improvements
alzota May 13, 2026
2c8b39d
ACQE-9850: Filter - Sales Order Grid
Karishmathakare19 May 13, 2026
d1a84b3
Merge branch 'develop' into ACQE-functional-deployment-version40
chandrakalahanchinal May 14, 2026
8c705cc
Merge branch 'ACQE-9836' into ACQE-functional-deployment-version40
chandrakalahanchinal May 14, 2026
f240c7c
Merge branch 'ACQE-functional-deployment-version40' of github.com:mag…
chandrakalahanchinal May 14, 2026
814e2ce
Merge remote-tracking branch 'origin/ACP2E-4765' into PR_2026_05_07_f…
thiaramus May 14, 2026
0b8eb3d
Merge remote-tracking branch 'origin/ACP2E-4866' into PR_2026_05_07_f…
thiaramus May 14, 2026
10c710a
AC-16487::Fake Modules Require dev/ Directory in Extension Repositories
sandesh-as May 15, 2026
db712a4
Merge pull request #742 from adobe-commerce-tier-4/PR_2026_05_07_flowers
alzota May 15, 2026
6ae3a3e
Merge branch 'develop' into ACQE-functional-deployment-v6-6
magento-devops-queue-mgr-svc May 15, 2026
390a956
Merge branch 'develop' into PR_2026_05_14_zota
alzota May 15, 2026
979cd48
Merge branch 'develop' into ACQE-functional-deployment-version40
magento-devops-queue-mgr-svc May 16, 2026
908becb
Merge pull request #744 from magento-gl/ACQE-functional-deployment-v6-6
internal-magento-queue-manager[bot] May 18, 2026
971eb09
Merge branch 'develop' into PR_2026_05_14_zota
magento-devops-queue-mgr-svc May 18, 2026
a8c5334
Merge branch 'develop' into ACQE-functional-deployment-version40
magento-devops-queue-mgr-svc May 18, 2026
a4fe7ec
ACQE-9696: [ATLH] Filters - Round 3 - Inventory Source Grid
shweta-tripathi-1519 May 18, 2026
8c6548d
ACQE-9557: Update test steps
chandrakalahanchinal May 18, 2026
b64033c
ACQE-9696: [ATLH] Filters - Round 3 - Inventory Source Grid
shweta-tripathi-1519 May 18, 2026
978a699
Merge branch 'ACQE-9850' into ACQE-functional-deployment-v6-7
Karishmathakare19 May 19, 2026
4d0dccb
Merge branch 'develop' into AC-16488_V1
del72683 May 20, 2026
92dcd50
Merge pull request #747 from adobe-commerce-tier-4/PR_2026_05_14_zota
alzota May 20, 2026
aa91c2d
Merge branch 'develop' into ACQE-functional-deployment-version40
chandrakalahanchinal May 20, 2026
2fb9531
Merge branch 'ACQE-9696' into ACQE-functional-deployment-v6-7
Karishmathakare19 May 20, 2026
f13094c
Merge branch 'develop' into ACQE-functional-deployment-v6-7
Karishmathakare19 May 20, 2026
4ded737
Merge branch 'develop' into AC-16488_V1
magento-devops-queue-mgr-svc May 20, 2026
f2eb109
Merge pull request #749 from magento-gl/AC-16488_V1
glo71317 May 21, 2026
01ff512
Merge branch 'develop' into ACQE-functional-deployment-version40
magento-devops-queue-mgr-svc May 21, 2026
65cc35c
ACP2E-4947: PR Quality Gates bugfixes and improvements 2
alzota May 21, 2026
6204230
ACP2E-4947: PR Quality Gates bugfixes and improvements 2
alzota May 21, 2026
1395f6e
Merge branch 'develop' into ACQE-functional-deployment-v6-7
magento-devops-queue-mgr-svc May 22, 2026
a3b423b
Merge pull request #746 from magento-gl/ACQE-functional-deployment-ve…
Karishmathakare19 May 22, 2026
dd7835b
Merge branch 'develop' into ACQE-functional-deployment-v6-7
magento-devops-queue-mgr-svc May 22, 2026
a6ed2e6
Merge branch 'ACQE-9501' into ACQE-functional-deployment-version41
chandrakalahanchinal May 22, 2026
ccc134e
Merge branch 'develop' into ACQE-functional-deployment-version41
chandrakalahanchinal May 22, 2026
4caf36f
Merge remote-tracking branch 'origin/ACP2E-4947' into PR_2026_05_22_zota
alzota May 22, 2026
d76b77b
ACQE-9557: Update test steps
chandrakalahanchinal May 22, 2026
33e2011
ACP2E-4972: PR Quality Gates bugfixes and improvements 3
alzota May 22, 2026
bbeca47
Merge branch 'develop' into AC-16487
May 22, 2026
b6f5a7d
ACP2E-4972: PR Quality Gates bugfixes and improvements 3
alzota May 22, 2026
895908f
Merge pull request #748 from magento-gl/ACQE-functional-deployment-v6-7
Karishmathakare19 May 25, 2026
17679aa
Merge branch 'develop' into ACQE-functional-deployment-version41
magento-devops-queue-mgr-svc May 25, 2026
81bbd96
Merge branch 'develop' into PR_2026_05_22_zota
alzota May 25, 2026
8531133
Merge branch 'develop' into AC-16487
djadobe16 May 25, 2026
1e0dbdd
Merge pull request #750 from magento-gl/ACQE-functional-deployment-ve…
Karishmathakare19 May 26, 2026
b6a1ae2
Merge branch 'develop' into PR_2026_05_22_zota
magento-devops-queue-mgr-svc May 26, 2026
15f461e
Merge branch 'develop' into AC-16487
magento-devops-queue-mgr-svc May 26, 2026
bc2757f
Merge pull request #751 from adobe-commerce-tier-4/PR_2026_05_22_zota
alzota May 26, 2026
0a169a4
ACQE-9701: [ATLH] Filters - Round 3 - Inventory Stocks Grid
Karishmathakare19 May 27, 2026
c8b6620
Merge branch 'develop' into AC-16487
glo71317 May 28, 2026
4ca648d
ACQE-9701: [ATLH] Filters - Round 3 - Inventory Stocks Grid
Karishmathakare19 May 28, 2026
3b496cb
Merge branch 'ACQE-9557' into ACQE-functional-deployment-version42
chandrakalahanchinal May 28, 2026
ace2680
Merge branch 'develop' into ACQE-functional-deployment-version42
chandrakalahanchinal May 28, 2026
821fee9
Merge pull request #752 from magento-gl/AC-16487
glo71317 May 28, 2026
844038d
Merge branch 'develop' of github.com:adobe-commerce-tier-4/inventory …
alzota May 28, 2026
17c0b58
ACP2E-4972: PR Quality Gates bugfixes and improvements 3
alzota May 28, 2026
f2565e4
ACP2E-4972: PR Quality Gates bugfixes and improvements 3
alzota May 28, 2026
3c9acca
Merge branch 'develop' into ACQE-functional-deployment-version42
magento-devops-queue-mgr-svc May 29, 2026
1c6ac40
Merge pull request #756 from adobe-commerce-tier-4/PR_2026_05_28_zota
alzota May 29, 2026
a3d19d6
Merge branch 'develop' into ACQE-functional-deployment-version42
chandrakalahanchinal May 29, 2026
a8584d1
ACP2E-4995: PR Quality Gates bugfixes and improvements 4
alzota May 29, 2026
03a9943
Merge pull request #757 from adobe-commerce-tier-4/PR_2026_05_30_zota
alzota May 30, 2026
5c163da
Merge branch 'develop' into ACQE-functional-deployment-version42
Karishmathakare19 May 31, 2026
48df397
Merge pull request #755 from magento-gl/ACQE-functional-deployment-ve…
glo71317 Jun 1, 2026
e108c4d
Merge remote-tracking branch 'origin/ACP2E-4349' into PR_2026_05_26_f…
thiaramus Jun 2, 2026
148d708
ACQE-9694: [ATLH] Filters - Round 3 - Products Grid
Karishmathakare19 Jun 2, 2026
de56284
ACQE-9694: [ATLH] Filters - Round 3 - Products Grid
Karishmathakare19 Jun 2, 2026
226aa72
ACQE-9694: [ATLH] Filters - Round 3 - Products Grid
Karishmathakare19 Jun 3, 2026
ecac3f7
AC-17305::Addressing Integration Test Failures in 2.4.9-dev Composer …
del72683 Jun 3, 2026
3e607f3
ACQE-9694: [ATLH] Filters - Round 3 - Products Grid
Karishmathakare19 Jun 3, 2026
000e296
AC-17305::Addressing Integration Test Failures in 2.4.9-dev Composer …
del72683 Jun 4, 2026
c766a90
AC-17305::Addressing Integration Test Failures in 2.4.9-dev Composer …
del72683 Jun 4, 2026
239464d
Merge pull request #754 from adobe-commerce-tier-4/PR_2026_05_26_flowers
alzota Jun 4, 2026
01819f4
Merge pull request #257 from magento-gl/develop
del72683 Jun 5, 2026
812e126
Merge branch 'ACQE-9701' into ACQE-functional-deployment-v6-8
Karishmathakare19 Jun 5, 2026
0bdbba8
ACQE-9640: [MFTF TESTS] StockStatusChangedForConfigurableProductOnTes…
mohit-adobe Jun 5, 2026
c1c4cc3
ACP2E-5003: Handle invalid store code gracefully in GetStockIdForCurr…
alzota Jun 5, 2026
1a0509a
Merge pull request #758 from magento-gl/ACQE-functional-deployment-v6-8
Karishmathakare19 Jun 8, 2026
b91b519
Merge branch 'develop' into AC-17305
magento-devops-queue-mgr-svc Jun 8, 2026
7780610
Merge branch 'develop' of github.com:magento-commerce/inventory into …
alzota Jun 8, 2026
c02fe1c
Merge branch 'ACQE-9694' into ACQE-functional-deployment-version43
chandrakalahanchinal Jun 9, 2026
2192dd2
Merge pull request #759 from magento-gl/AC-17305
glo71317 Jun 10, 2026
6a25a24
Merge branch 'develop' into ACQE-functional-deployment-version43
magento-devops-queue-mgr-svc Jun 10, 2026
af9698a
ACQE-9629: [MFTF TESTS] AdminUserSetStatusForEachSourceItemTest
mohit-adobe Jun 11, 2026
06b6d6b
Merge pull request #761 from magento-gl/ACQE-functional-deployment-ve…
Karishmathakare19 Jun 11, 2026
33507c1
Merge branch 'develop' into PR_2026_06_08_moyseyenko
alzota Jun 15, 2026
1bf25e4
Merge pull request #760 from adobe-commerce-tier-4/PR_2026_06_08_moys…
alzota Jun 15, 2026
79b7d96
ACQE-10343: [MFTF TESTS] AdminBundleProductDisabledManageStockOnProdu…
mohit-adobe Jun 16, 2026
4be3ea9
Merge branch 'ACQE-9640' into ACQE-functional-deployment-v61
Karishmathakare19 Jun 19, 2026
3569236
Merge branch 'ACQE-9629' into ACQE-functional-deployment-v61
Karishmathakare19 Jun 19, 2026
1b0abb8
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 22, 2026
2764fb5
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 22, 2026
b57f28f
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 22, 2026
a812d26
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 22, 2026
f0a2993
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 22, 2026
230e26a
ACQE-10614: [Unskip] Analysis- Run mentioned tests on local and check…
Karishmathakare19 Jun 23, 2026
d14c159
Merge branch 'ACQE-10343' into ACQE-functional-deployment-version44
chandrakalahanchinal Jun 23, 2026
0f93760
Merge branch 'ACQE-10614' into ACQE-functional-deployment-version44
chandrakalahanchinal Jun 23, 2026
df2dc54
Merge pull request #763 from magento-gl/ACQE-functional-deployment-v61
Karishmathakare19 Jun 24, 2026
81d6396
ACP2E-5049: PR Quality Gates: delivery to PB and Security Repos
alzota Jun 24, 2026
fd032fa
Merge branch 'develop' into ACQE-functional-deployment-version44
chandrakalahanchinal Jun 25, 2026
84d43f7
ACP2E-5049: PR Quality Gates: delivery to PB and Security Repos
alzota Jun 25, 2026
bfa1c6f
ACP2E-5049: PR Quality Gates: delivery to PB and Security Repos
alzota Jun 25, 2026
4d4d2b5
Merge pull request #764 from magento-gl/ACQE-functional-deployment-ve…
glo71317 Jun 26, 2026
9a41469
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 3, 2026
22a62d8
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 4, 2026
0e03fd4
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 4, 2026
f2435f3
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 6, 2026
9176b81
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
6631de4
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
812a94e
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
28e850b
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
7d4352c
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
852240e
ACP2E-4973: [Feature Request] Clear redundant in store pickup data wh…
soklymeach Jul 7, 2026
57047d2
Merge pull request #772 from adobe-commerce-tier-4/PR_2026_07_16_moys…
alzota Jul 24, 2026
ef016c1
Merge remote-tracking branch 'origin/ACP2E-5049' into PR_2026_07_30_zota
alzota Jul 30, 2026
bc7899a
Merge pull request #776 from adobe-commerce-tier-4/PR_2026_07_30_zota
alzota Jul 30, 2026
7c03167
chore(dist): merge upstream develop (bc7899a) into the 2.4.9 line
jeanmarcos-dev Aug 3, 2026
50cffaa
ci(dist): exclude the .github tooling inherited from upstream from th…
jeanmarcos-dev Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__/
*.pyc
.DS_Store
325 changes: 325 additions & 0 deletions .github/scripts/check_copyright_pr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
<?php
/**
* Copyright 2026 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

/**
* PR copyright header checker.
*
* New files — must carry the current year in the correct format for the repo edition.
* Existing files — the normalized copyright header must be identical to the base branch;
* the year must not be changed.
*
* Usage:
* php check_copyright_pr.php \
* --files /tmp/copyright_changed.txt \
* --added /tmp/copyright_added.txt \
* --base-dir <dir> \
* --template .github/scripts/copyright-template.txt
*/

// ── Argument parsing ──────────────────────────────────────────────────────────

$opts = getopt('', ['files:', 'added:', 'base-dir:', 'template:']);

$missing = [];
foreach (['files', 'base-dir', 'template'] as $req) {
if (empty($opts[$req])) $missing[] = "--$req";
}
if ($missing) {
fwrite(STDERR, "Error: missing required arguments: " . implode(', ', $missing) . "\n");
fwrite(STDERR, "Usage: php check_copyright_pr.php --files <list> --added <list> --base-dir <dir> --template <path>\n");
exit(1);
}

$filesPath = $opts['files'];
$addedPath = $opts['added'] ?? '';
$baseDir = rtrim((string) $opts['base-dir'], '/');
$templatePath = $opts['template'];

if (!file_exists($filesPath)) {
echo "No copyright-eligible changed files found — skipping.\n";
exit(0);
}

$template = trim((string) file_get_contents($templatePath));
$changedFiles = array_filter(array_map('trim', file($filesPath)));
$addedFiles = ($addedPath && file_exists($addedPath))
? array_flip(array_filter(array_map('trim', file($addedPath))))
: [];

$currentYear = (int) date('Y');
$issues = [];
$checked = 0;
$skipped = 0;

// ── Helpers ───────────────────────────────────────────────────────────────────

/**
* Normalize a copyright block for comparison: strip XML declarations, PHP
* declare(), comment markers, whitespace and punctuation — mirrors the Python
* _normalize_copyright() function.
*/
function normalizeHeader(string $text): string
{
$text = preg_replace('/<\?xml[^?]*\?>/', '', $text); // strip XML PI
$text = preg_replace('/\bdeclare\s*\([^)]*\)\s*;?/', '', $text); // strip declare()
return strtolower((string) preg_replace('/[\s#\/\*\-<>!?\[\]]+/', '', $text));
}

/**
* Returns true when the file should be skipped entirely.
*/
function shouldExclude(string $filePath): bool
{
static $allowedExts = ['php','phtml','html','js','xml','xsd','less','css','scss','graphqls'];

$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExts, true)) return true;

// .github/ and vendor/ are always excluded
if (preg_match('#^(\.github|vendor)/#', $filePath)) return true;

// lib/web/ is excluded except lib/web/mage/ (Adobe-authored JS)
if (preg_match('#^lib/web/#', $filePath) && !preg_match('#^lib/web/mage/#', $filePath)) return true;

return false;
}

/**
* Extract the first copyright block from file content.
* Handles C-style (/** ... *\/), HTML (<!-- ... -->), and hash (# ...) blocks.
* Returns the raw header text, or '' if no copyright found.
*/
function extractCopyrightBlock(string $content): string
{
$lines = explode("\n", $content);
$blockType = null; // 'c', 'html', 'hash'
$collected = [];
$foundCopyright = false;

foreach ($lines as $i => $line) {
$s = trim($line);

// Line 0: skip PHP/XML opening tag but collect it
if ($i === 0 && (str_starts_with($s, '<?php') || str_starts_with($s, '<?xml'))) {
$collected[] = $line;
continue;
}

if ($blockType === null) {
if (str_starts_with($s, '/**') || str_starts_with($s, '/*')) {
$blockType = 'c';
$collected[] = $line;
if (strlen($s) > 2 && str_ends_with($s, '*/')) {
$foundCopyright = stripos($s, 'copyright') !== false;
break;
}
continue;
}
if (str_starts_with($s, '<!--')) {
$blockType = 'html';
$collected[] = $line;
if (strlen($s) > 4 && str_ends_with($s, '-->')) {
$foundCopyright = stripos($s, 'copyright') !== false;
break;
}
continue;
}
if (str_starts_with($s, '#')) {
$blockType = 'hash';
$collected[] = $line;
if (stripos($s, 'copyright') !== false) $foundCopyright = true;
continue;
}
// Empty lines and declare() before the copyright block are allowed
if ($s === '') continue;
if (preg_match('/^declare\s*\([^)]*\)\s*;?$/', $s)) continue;
break;
}

if ($blockType === 'c') {
$collected[] = $line;
if (stripos($s, 'copyright') !== false) $foundCopyright = true;
if (str_ends_with($s, '*/')) {
if ($foundCopyright) break;
$collected = []; $foundCopyright = false; $blockType = null; // non-copyright block, reset
}
continue;
}

if ($blockType === 'html') {
$collected[] = $line;
if (stripos($s, 'copyright') !== false) $foundCopyright = true;
if (str_ends_with($s, '-->')) {
if ($foundCopyright) break;
$collected = []; $foundCopyright = false; $blockType = null;
}
continue;
}

if ($blockType === 'hash') {
if (!str_starts_with($s, '#')) break; // end of hash block
if (stripos($s, 'copyright') !== false) $foundCopyright = true;
$collected[] = $line;
continue;
}
}

return $foundCopyright ? implode("\n", $collected) : '';
}

/**
* Fetch the base-branch content of a file from the pre-fetched directory.
* Returns null when the file did not exist in the base branch.
*/
function getBaseContent(string $filePath, string $baseDir): ?string
{
$path = $baseDir . '/' . $filePath;
return file_exists($path) ? (string) file_get_contents($path) : null;
}

/**
* Build the expected copyright header for a given file type using the template.
* The template is in /** ... *\/ block format.
*/
function buildExpectedHeader(string $template, string $filePath, int $year): string
{
$tpl = str_replace('{{YEAR}}', (string) $year, $template);
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));

// Template is already in /** */ form; adapt wrapping for the file type.
// Strip outer /** and */ to get the inner lines.
$inner = trim((string) preg_replace('/^\/\*\*\s*|\s*\*\/$/', '', $tpl));
$innerLines = explode("\n", $inner);
$bodyLines = array_map(static fn(string $l): string => ltrim($l, ' *'), $innerLines);

switch ($ext) {
case 'php':
return "<?php\n" . $tpl . "\n";
case 'phtml':
return "<?php\n" . $tpl . "\n?>\n";
case 'xml':
case 'xsd':
$out = ['<?xml version="1.0"?>', '<!--'];
foreach ($bodyLines as $l) $out[] = $l !== '' ? " * {$l}" : ' *';
$out[] = ' -->';
return implode("\n", $out) . "\n";
case 'graphqls':
$out = [];
foreach ($bodyLines as $l) $out[] = $l !== '' ? "# {$l}" : '#';
return implode("\n", $out) . "\n";
case 'html':
$out = ['<!--'];
foreach ($bodyLines as $l) $out[] = $l !== '' ? " * {$l}" : ' *';
$out[] = ' -->';
return implode("\n", $out) . "\n";
default:
return $tpl . "\n";
}
}

// ── Main loop ─────────────────────────────────────────────────────────────────

foreach ($changedFiles as $filePath) {
if (shouldExclude($filePath)) {
$skipped++;
continue;
}

if (!is_file($filePath)) {
$skipped++;
continue;
}

$content = (string) file_get_contents($filePath);

// Skip third-party files that contain no Magento/Adobe reference
if (strpos($content, 'Magento') === false && strpos($content, 'Adobe') === false
&& strpos($filePath, 'Magento') === false && strpos($filePath, 'Adobe') === false
) {
$skipped++;
continue;
}

$checked++;
$isNew = isset($addedFiles[$filePath]);
$currentHeader = extractCopyrightBlock($content);


if ($isNew) {
// ── New file: must have a copyright header in current-year format ─────
if ($currentHeader === '') {
$issues[] = [
'file' => $filePath,
'reason' => 'Missing copyright header',
'new' => true,
];
continue;
}
$currentNorm = normalizeHeader($currentHeader);
$expected = buildExpectedHeader($template, $filePath, $currentYear);
$expectedNorm = normalizeHeader($expected);
if ($expectedNorm !== '' && strpos($currentNorm, $expectedNorm) === false) {
$issues[] = [
'file' => $filePath,
'reason' => "New file: copyright header must use year {$currentYear} in the correct format",
'new' => true,
];
}
} else {
// ── Existing file: compare against base; skip if base had no copyright ─
$baseContent = getBaseContent($filePath, $baseDir);

if ($baseContent === null) {
// Modified file but base blob unavailable (partial clone fetch failure).
// Skip rather than emit a false positive.
echo " [SKIP] {$filePath} — base content unavailable (partial clone fetch failure)\n";
$skipped++;
continue;
}

$baseHeader = extractCopyrightBlock($baseContent);
if ($baseHeader === '') {
continue;
}

if ($currentHeader === '') {
$issues[] = [
'file' => $filePath,
'reason' => 'Copyright header was removed in this PR',
'new' => false,
];
continue;
}

$baseNorm = normalizeHeader($baseHeader);
$currentNorm = normalizeHeader($currentHeader);
if ($baseNorm !== $currentNorm) {
$issues[] = [
'file' => $filePath,
'reason' => 'Copyright header was modified in this PR (year or content changed)',
'new' => false,
];
}
}
}

// ── Report ────────────────────────────────────────────────────────────────────

echo "Copyright check: {$checked} file(s) checked, {$skipped} skipped\n";

if (empty($issues)) {
echo "All files have correct copyright headers\n";
exit(0);
}

echo count($issues) . " file(s) have copyright issues:\n";
foreach ($issues as $issue) {
$tag = $issue['new'] ? '[NEW] ' : '[EXISTING]';
echo " {$tag} {$issue['file']}\n";
echo " {$issue['reason']}\n";
}
exit(1);
4 changes: 4 additions & 0 deletions .github/scripts/copyright-template.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Copyright {{YEAR}} Adobe
* All Rights Reserved.
*/
11 changes: 7 additions & 4 deletions .github/scripts/dist-qa-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ fi

echo "Base ref: $BASE"

# .github/ is excluded: it carries CI tooling inherited from upstream (CLI scripts that
# legitimately use echo/exit), which the Magento2 standard rejects and which is not part
# of the distributed code.
CHANGED=$(
{
git diff --name-only --diff-filter=ACMR "$BASE"...HEAD -- '*.php'
git diff --name-only --diff-filter=ACMR -- '*.php'
git diff --name-only --diff-filter=ACMR --cached -- '*.php'
git ls-files --others --exclude-standard -- '*.php'
git diff --name-only --diff-filter=ACMR "$BASE"...HEAD -- '*.php' ':(exclude).github/'
git diff --name-only --diff-filter=ACMR -- '*.php' ':(exclude).github/'
git diff --name-only --diff-filter=ACMR --cached -- '*.php' ':(exclude).github/'
git ls-files --others --exclude-standard -- '*.php' ':(exclude).github/'
} 2>/dev/null | sort -u | grep -v '^$' || true
)

Expand Down
20 changes: 20 additions & 0 deletions .github/scripts/read-pkg-constraint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
#
# Copyright 2026 Adobe
# All Rights Reserved.
#
# Usage: read-pkg-constraint.sh <composer.json> <package/name>
# Prints the version constraint for the package from require or require-dev.
set -euo pipefail

FILE="$1"
PKG="$2"

[ -f "$FILE" ] || exit 1

php -r '
$j = json_decode(file_get_contents($argv[1]), true) ?: [];
$p = $argv[2];
$v = $j["require"][$p] ?? $j["require-dev"][$p] ?? "";
echo is_string($v) ? $v : "";
' "$FILE" "$PKG"
Loading
Loading