Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c762792
fix(zip): stop registering a second Composer autoloader in the store …
BrocksiNet Sep 21, 2026
b50d77f
fix(zip): declare the bundled psr-4 prefixes as strings, not lists
BrocksiNet Sep 21, 2026
ccc7048
ci(zip-install): pair CI_SMOKE_SKIP_PLUGIN with CI_SMOKE_BOOTSTRAP_ONLY
BrocksiNet Sep 21, 2026
500c93a
build(zip): keep development tooling out of the archive
BrocksiNet Sep 21, 2026
55ccd51
Merge branch 'main' into fix/no-second-composer-autoloader
BrocksiNet Sep 21, 2026
5424b50
fix(zip): address review on the autoloader change
BrocksiNet Sep 21, 2026
5600ad5
fix(zip): clean the uploaded archive up whichever way the install goes
BrocksiNet Sep 21, 2026
eeb17ba
fix(zip): let Shopware install the SDK, and survive the window where …
BrocksiNet Sep 22, 2026
94cfab8
docs(changelog): deutsche Einträge in eigenem Ton statt als Übersetzung
BrocksiNet Sep 22, 2026
6e33b83
docs(changelog): break the English entries into sentences that end
BrocksiNet Sep 22, 2026
30ad5a3
docs(agents): write down how the extension installs and updates
BrocksiNet Sep 22, 2026
90af54c
docs(agents): point at the upstream rework of composer require for pl…
BrocksiNet Sep 22, 2026
206dd7e
test(zip): assert the shop survives its SDK going missing
BrocksiNet Sep 22, 2026
38d1a5e
test(zip): make the recovery check deterministic, and read curl's sta…
BrocksiNet Sep 22, 2026
1eb3cbd
docs: document the degraded state for merchants instead of testing it…
BrocksiNet Sep 22, 2026
beeb671
docs(changelog): drop the blank line between the two new entries
BrocksiNet Sep 22, 2026
f25b3f1
docs(readme): name both places the inactive-extension message lands
BrocksiNet Sep 22, 2026
278429a
test(zip): stop asserting recovery after the SDK is put back
BrocksiNet Sep 22, 2026
26a0a4d
docs: one markdown format for the repository, and a check that keeps it
BrocksiNet Sep 22, 2026
716f8a3
Merge remote-tracking branch 'origin/main' into fix/no-second-compose…
BrocksiNet Sep 22, 2026
f90cf2b
fix(sdk): refuse the install on a cluster setup instead of installing…
BrocksiNet Sep 22, 2026
ea7c69c
fix(discovery): allow root UCP profile route
BrocksiNet Sep 23, 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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ jobs:
- name: ShellCheck bin scripts
run: shellcheck -x bin/*.sh bin/lib/*.sh

docs-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: npm
- run: npm ci --no-audit --no-fund
# Prettier owns the shape -- 100 columns, wrapped prose, one style across every markdown
# file. The changelogs keep one line per entry (see .prettierrc.json).
- name: Markdown formatting
run: npm run lint:md
- name: Markdown links
run: bin/ci-assert-markdown-links.sh

verify-main-validation:
runs-on: ubuntu-latest
outputs:
Expand Down Expand Up @@ -137,6 +153,7 @@ jobs:

expected_checks=(
"shell-lint"
"docs-lint"
"admin-static"
"php-quality (php-8.1)"
"php-quality (php-8.2)"
Expand Down
138 changes: 77 additions & 61 deletions .github/workflows/package-zip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ on:
types: [opened, reopened, synchronize, labeled]
workflow_dispatch:
inputs:
sdk_ref:
description: 'ucp-php-sdk ref to vendor into the zip'
required: false
default: '0.0.7'
sdk_version:
description: 'Version to attribute to that ref (its packages are untagged)'
required: false
default: '0.0.7'
overwrite_version:
description: 'Plugin version to stamp (Shopware rejects semver pre-release suffixes; use e.g. 1.3.99)'
required: false
Expand All @@ -52,42 +44,6 @@ jobs:
# full history so shopware-cli can generate the changelog
fetch-depth: 0

# The SDK's release track is ahead of anything published, so a plain resolve gets
# whatever packagist last saw rather than the code this archive is meant to carry.
# Checked out and resolved from source instead, and placed *inside* the extension:
# shopware-cli copies the extension to a temp directory before running composer, so
# a path repository pointing at a sibling (`../ucp-php-sdk`) resolves during
# planning and then materialises nothing.
- name: Check out the SDK ref to vendor
uses: actions/checkout@v6
with:
repository: agentic-commerce-alliance/ucp-php-sdk
ref: ${{ github.event.inputs.sdk_ref || '0.0.7' }}
path: ucp-php-sdk

- name: Vendor that SDK ref into the extension
env:
SDK_VERSION: ${{ github.event.inputs.sdk_version || '0.0.7' }}
run: |
set -euo pipefail
mkdir -p .sdk
cp -a ucp-php-sdk/packages/core .sdk/core
cp -a ucp-php-sdk/packages/symfony-bundle .sdk/symfony-bundle
rm -rf .sdk/core/tests .sdk/symfony-bundle/tests ucp-php-sdk
# `options.versions` attributes a version without editing the SDK's own
# composer.json -- its packages carry none and derive it from tags, and the
# bundle already requires core >=0.0.7 at the point a tag is cut.
composer config repositories.ucp-sdk-core "{\"type\":\"path\",\"url\":\"./.sdk/core\",\"options\":{\"symlink\":false,\"versions\":{\"ucp-php-sdk/core\":\"${SDK_VERSION}\"}}}"
composer config repositories.ucp-sdk-symfony "{\"type\":\"path\",\"url\":\"./.sdk/symfony-bundle\",\"options\":{\"symlink\":false,\"versions\":{\"ucp-php-sdk/symfony-bundle\":\"${SDK_VERSION}\"}}}"
# The source checkout puts development tooling in .tools/vendor. The archive's
# bootstrap loads runtime dependencies from vendor/autoload.php.
composer config vendor-dir vendor
# Fresh checkouts have no lock file, so Composer must resolve the full set.
composer update --with "ucp-php-sdk/symfony-bundle:${SDK_VERSION}" --with "ucp-php-sdk/core:${SDK_VERSION}" --no-dev --no-scripts --no-interaction --prefer-dist

- name: Keep only SDK packages in the bundled runtime
run: bin/ci-bundle-sdk-only.sh

- name: Install shopware-cli
uses: shopware/shopware-cli-action@v3

Expand All @@ -98,8 +54,8 @@ jobs:
if [[ -n "${{ github.event.inputs.overwrite_version }}" ]]; then
version_flag=(--overwrite-version "${{ github.event.inputs.overwrite_version }}")
fi
# Use the prepared checkout: selecting a git commit discards the SDK and
# Composer changes made by the preceding step.
# --disable-git packs the checkout as it stands. Nothing in this job modifies it any
# more, and bin/ci-assert-zip-no-vendor.sh fails the build if anything strays in.
shopware-cli extension zip . --disable-git --release "${version_flag[@]}"
shopware-cli extension validate SwagAgenticCommerce.zip

Expand All @@ -108,11 +64,11 @@ jobs:
- name: Assert the zip ships a usable administration bundle
run: bin/ci-assert-zip-admin-bundle.sh SwagAgenticCommerce.zip

# Neither `extension validate` nor the admin-bundle guard looks at vendor/, so an
# archive whose autoloader points at an SDK that was never copied in passes both and
# then fatals on the first request with `Class "Ucp\\Sdk\\..." not found`.
- name: Assert the zip carries the SDK it autoloads
run: bin/ci-assert-zip-vendors-sdk.sh SwagAgenticCommerce.zip
# `extension validate` does not read the archive's dependency shape, so a build that
# reintroduced a bundled vendor/ -- and with it a second Composer autoloader in every
# shop -- would pass it.
- name: Assert the zip bundles no dependencies
run: bin/ci-assert-zip-no-vendor.sh SwagAgenticCommerce.zip

- name: Unpack so the downloaded artifact is directly installable
run: |
Expand All @@ -126,19 +82,15 @@ jobs:
path: dist
include-hidden-files: true

# Neither guard above can prove the archive installs. The SDK was once present, correctly
# pointed at, and still never loaded, because Shopware does not require a plugin's own
# vendor/autoload.php -- so both checks passed while `plugin:install` failed on every
# Shopware version. Only an install onto a shop that does not separately provide the SDK
# settles it, and that is a local step until this workflow boots a shop of its own.
- name: How to verify this archive actually installs
# The zip-install job below installs this archive on 6.5, 6.6 and trunk. This tells a
# developer how to repeat it against their own lane, against the downloaded artifact.
- name: How to verify this archive on a local lane
run: |
{
echo "### Before handing this archive to anyone"
echo "### Installing this archive on your own lane"
echo
echo 'The checks in this job prove the SDK is *in* the archive and that the autoloader'
echo '*points* at it. They cannot prove it loads. Run the install smoke against a lane'
echo 'whose shop does not already provide the SDK:'
echo 'The zip-install job already did this on 6.5.x, 6.6.x and trunk. To repeat it'
echo 'against a local lane, on a shop that does not already provide the SDK:'
echo
echo '```'
echo 'bin/test-zip-install.sh --zip <downloaded>.zip \'
Expand All @@ -150,3 +102,67 @@ jobs:
echo 'It strips the project-level SDK first and refuses to run if the shop can still'
echo 'resolve it, because that is how the last defect stayed invisible.'
} >> "$GITHUB_STEP_SUMMARY"

# Neither the admin-bundle guard nor the SDK guard can prove the archive installs. The SDK was
# once present, correctly pointed at, and still never loaded, because Shopware does not require
# a plugin's own vendor/autoload.php -- so both checks passed while `plugin:install` failed on
# every Shopware version. Only an install onto a shop that does not separately provide the SDK
# settles it, on each supported line.
zip-install:
name: Install the zip (${{ matrix.lane }})
needs: package-zip
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
lane: ['6.5.x', '6.6.x', 'trunk']
steps:
- name: Checkout
uses: actions/checkout@v6
with:
path: agentic-commerce
- name: Check out the lane
uses: actions/checkout@v6
with:
repository: shopware/shopware
ref: ${{ matrix.lane }}
path: shopware
- name: Download the built archive
uses: actions/download-artifact@v7
with:
name: SwagAgenticCommerce
path: archive
# The artifact is uploaded unpacked (see the note at the top of this file), so re-zip the
# plugin directory to get back the single-rooted archive the upload endpoint expects.
- name: Re-zip the archive
run: cd archive && zip -qr "$GITHUB_WORKSPACE/SwagAgenticCommerce.zip" SwagAgenticCommerce
- run: chmod +x agentic-commerce/bin/ci-smoke.sh agentic-commerce/bin/ci-write-compose.sh agentic-commerce/bin/test-zip-install.sh
- run: agentic-commerce/bin/ci-write-compose.sh "$GITHUB_WORKSPACE/shopware" "${{ matrix.lane }}"
# CI_SMOKE_SKIP_PLUGIN removes the plugin and both SDK packages from the project before
# installing it, which is the whole point: on a shop that can resolve the SDK by another
# route, a green install proves nothing about the archive.
- name: Boot a shop that has never seen the SDK
run: agentic-commerce/bin/ci-smoke.sh "$GITHUB_WORKSPACE/shopware"
env:
SHOPWARE_REF: ${{ matrix.lane }}
# SKIP_PLUGIN is only accepted together with BOOTSTRAP_ONLY: a run that installs
# nothing has no smoke assertions left to make.
CI_SMOKE_SKIP_PLUGIN: '1'
CI_SMOKE_BOOTSTRAP_ONLY: '1'
CI_SMOKE_MODE: cold
CI_SMOKE_KEEP_STACK: '1'
- name: Install the archive the way a merchant does
working-directory: shopware
run: |
set -euo pipefail
container="$(docker compose ps -a -q web)"
if [[ -z "${container}" ]]; then
echo "The lane has no web container; the bootstrap step did not leave the stack up." >&2
exit 1
fi
url="$(sed -nE 's/^[[:space:]]*APP_URL:[[:space:]]*(.+)$/\1/p' compose.yaml | head -n 1)"
"$GITHUB_WORKSPACE/agentic-commerce/bin/test-zip-install.sh" \
--zip "$GITHUB_WORKSPACE/SwagAgenticCommerce.zip" \
--container "${container}" \
--shop-url "${url:-http://localhost:8000}"
9 changes: 1 addition & 8 deletions .github/workflows/store-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,6 @@ jobs:

echo "Validated main HEAD ${GITHUB_SHA} with CI gate ${gate_id}."

- name: Prepare the released SDK runtime
run: |
set -euo pipefail
composer config vendor-dir vendor
composer update --with ucp-php-sdk/symfony-bundle:0.0.7 --with ucp-php-sdk/core:0.0.7 --no-dev --no-scripts --no-interaction --prefer-dist
bin/ci-bundle-sdk-only.sh

- name: Install shopware-cli
uses: shopware/shopware-cli-action@v3

Expand All @@ -92,7 +85,7 @@ jobs:
shopware-cli extension zip . --disable-git --release
shopware-cli extension validate SwagAgenticCommerce.zip
bin/ci-assert-zip-admin-bundle.sh SwagAgenticCommerce.zip
bin/ci-assert-zip-vendors-sdk.sh SwagAgenticCommerce.zip
bin/ci-assert-zip-no-vendor.sh SwagAgenticCommerce.zip
mkdir -p dist
unzip -q SwagAgenticCommerce.zip -d dist

Expand Down
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Not ours to format.
node_modules/
.tools/
vendor/
var/
dist/
src/Resources/app/**/node_modules/
12 changes: 12 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"printWidth": 100,
"proseWrap": "always",
"overrides": [
{
"files": ["CHANGELOG.md", "CHANGELOG_de-DE.md"],
"options": {
"proseWrap": "preserve"
}
}
]
}
10 changes: 6 additions & 4 deletions .shopware-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ build:
pack:
excludes:
paths:
# Build-only: the workflow copies the SDK packages here and composer resolves
# them from here into vendor/. Shipping this as well would put two copies of
# the SDK in the archive.
- .sdk
# Development tooling, never part of a release. The source manifest points Composer
# at .tools/vendor and npm fills node_modules; `extension zip --disable-git` copies
# the working tree verbatim, so without these a local build ships PHPUnit, PHPStan
# and the whole npm tree.
- .tools
- bin
- docs
- eslint.config.mjs
- node_modules
- package.json
- package-lock.json
- playwright.config.js
Loading