Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .github/workflows/installed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ jobs:
ref: ${{ steps.component.outputs.ref }}
path: component
persist-credentials: false
- name: Check out the independent webservices plugin
uses: actions/checkout@v7
with:
repository: joomengine/mcp_webservices
ref: 94fd2a2b4ef802e1deef1050563cca2c0d879e1f
path: webservices
persist-credentials: false
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
Expand All @@ -75,6 +82,7 @@ jobs:
MCP_TEST_ALLOW_DESTRUCTIVE: '1'
JOOMLA_ROOT: ${{ runner.temp }}/joomla
MCP_PLUGIN_SOURCE: ${{ github.workspace }}/plugin
MCP_WEBSERVICES_SOURCE: ${{ github.workspace }}/webservices
MCP_TEST_DB_TYPE: mysqli
MCP_TEST_DB_HOST: 127.0.0.1:3306
MCP_TEST_DB_USER: mcp_test
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The migration PR #1 has been merged. Branch new work from current `main`, or con

Use element `joomengine_mcp`, group `console`, namespace `VDM\Plugin\Console\JoomEngineMcp`; dependency `com_joomengine_mcp`, namespace `VDM\Component\JoomEngineMcp`. Joomla 6 native plugin/event/DI/console contracts are authoritative. Follow JCB's plugin-root manifest/installer/services/src/language/update layout. PHP style authority: https://github.com/extension-builder/joomla/blob/main/docs/development/php-code-style.md (tabs, LF, Allman braces, explicit typed properties/constructor injection, meaningful docblocks, no closing tags or isolated strict_types/promotion/readonly changes). Preserve inherited signatures.

External Composer client and remote stdio bridge belong only to `joomengine/mcp_client`, package `joomengine/mcp-client`. Do not implement or depend on them here. The component owns HTTP routing glue, catalogue/schema resolution, business handlers, permissions/plans, jobs/artifacts, verification and audit. The plugin supplies local entry and adaptation only.
External Composer client and remote stdio bridge belong only to `joomengine/mcp_client`, package `joomengine/mcp-client`. Do not implement or depend on them here. The independent `joomengine/mcp_webservices` plugin owns HTTP routing. The component owns catalogue/schema resolution, business handlers, permissions/plans, jobs/artifacts, verification and audit. This console plugin supplies local entry and adaptation only.

Only the real console application under CLI can establish server-owner authority. JSON, headers, tokens and database rows cannot do so. Local requests still validate inputs and bindings and retain audit/recovery; HTTP-originated jobs never acquire unrestricted authority just because a local worker executes them.

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## [[[NEXT_VERSION]]]

### Fix

- Check out the independent webservices plugin for the shared installed Joomla tests after its extraction from the component.
- Publish the native Joomla information URL and explicit site client in console update entries.
- Reject reused changelog versions, misplaced pending markers and update entries for unprepared future versions without changing release files.

### Addition

- Add a manual next-version release workflow that freezes both changelogs, creates an immutable tag, updates the native Joomla feed and waits for OctoShoom to commit its checksum.
Expand All @@ -22,6 +28,7 @@

- Install the compatible component before the console plugin. The plugin release does not invoke OctoJPack.
- Configure the six git-user secrets documented in docs/RELEASE.md before releasing.
- Complete both plugin release workflows before running the component workflow to publish the first combined package.

## 0.1.0 — development baseline

Expand Down
4 changes: 3 additions & 1 deletion docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Branch

The migration [PR #1](https://github.com/joomengine/mcp_plugin/pull/1) is merged. Source-installation and release realignment is on `fix/octo-release-workflow`; the [component acceptance checklist](https://github.com/joomengine/mcp_component/pull/1#issuecomment-5732685349) tracks coordinated Joomla/JCB execution evidence.
The migration [PR #1](https://github.com/joomengine/mcp_plugin/pull/1) is merged. First-package release readiness is on `fix/first-package-release`; the [component acceptance checklist](https://github.com/joomengine/mcp_component/pull/1#issuecomment-5732685349) tracks coordinated Joomla/JCB execution evidence.

Plugin version 0.1.0 requires component version 0.1.1 or later within the same major version, because the explicit JCB synchronization operation is part of that runtime contract.

Expand Down Expand Up @@ -42,4 +42,6 @@ Installed CI uses the component's main branch by default; configuration and reus

The simplified release support passes actionlint, PHP 8.3 syntax checks, 13 isolated metadata checks, and the source manifest/language/installation-completeness checks. These checks do not exercise publication credentials or replace the installed runtime evidence above.

The first-package readiness changes pass 18 isolated release metadata checks and the source manifest/language/installation-completeness checks on PHP 8.3.6. These include native feed identity/information, rejection without file changes for reused or unprepared versions and misplaced pending markers, and checksum preservation across retries and successive releases. Runtime code is unchanged. Release instructions now cover both independent plugin workflows before the component's package release; actual publication still requires running those workflows with configured credentials.

The component golden-image suite exercises shared JCB operations, native options/dependencies, persisted read-back, generated/install artifacts, state isolation, long jobs/cancellation/recovery and cleanup. Each result belongs to its recorded component/JCB/plugin revisions. The linked PR and acceptance checklist are authoritative for current completion; historical runs do not certify later runtime changes. External-client interoperability is tracked in `mcp_client` and the coordinated component suite. Review/merge and deliberate release publication remain separate actions.
10 changes: 8 additions & 2 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ The repository source ZIP installs directly into Joomla after the compatible com

## Release a version

Open Actions → **Release console plugin with OctoShoom**, select `main`, and enter the next version, such as `1.2.3`. A `v` prefix is optional.
Open Actions → **Release console plugin with OctoShoom** → **Run workflow**, select `main`, and enter an unused next version. A `v` prefix is optional. For the first package, `0.1.2` is an example that is newer than the development baselines across all three extension repositories; the workflow input chooses the actual release version.

The workflow uses [git-user](https://github.com/octoleo/git-user#workflows) to configure Git authentication and signing. Its small metadata step freezes `[[[NEXT_VERSION]]]` in both changelogs, updates the manifest version/date, commits and tags the source, then adds the tagged ZIP to `joomengine_mcp_update_server.xml`. It calls the [OctoShoom action](https://github.com/octoleo/octoshoom#quick-start) directly to hash the downloads and commit the update feed.

Repository `joomengine/mcp_plugin`, branch `main` and the update-feed path are fixed in the workflow. OctoShoom inherits the Git identity and authentication from git-user. Existing tags and feed entries are left unchanged when rerunning the same version.

The plugin workflow stops after OctoShoom. Release the plugin before the component; OctoJPack reads the component's standalone `.octojpack` configuration and selects the latest plugin tag for the combined package.
The plugin workflow stops after OctoShoom. To create the first combined package:

1. Run this console plugin's release workflow and wait for it to succeed.
2. In `joomengine/mcp_webservices`, use **Actions → Run workflow** to release the webservices plugin and wait for it to succeed. The two plugin releases may run independently.
3. In `joomengine/mcp_component`, run the component release workflow. OctoJPack reads its standalone `.octojpack`, selects the latest plugin tags, and publishes the package at the component version. The component workflow then updates the package feed and runs OctoShoom.

These workflows start through **Run workflow**; pushing a tag alone does not trigger them. Both plugins must have a published tag before the component release can assemble the package.

## GitHub secrets

Expand Down
6 changes: 6 additions & 0 deletions joomengine_mcp_changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<type>plugin</type>
<folder>console</folder>
<version>[[[NEXT_VERSION]]]</version>
<fix>
<item>Check out the independent webservices plugin for the shared installed Joomla tests after its extraction from the component.</item>
<item>Publish the native Joomla information URL and explicit site client in console update entries.</item>
<item>Reject reused changelog versions, misplaced pending markers and update entries for unprepared future versions without changing release files.</item>
</fix>
<addition>
<item>Add manual version releases with changelog freezing, immutable tags, Joomla update entries and synchronous OctoShoom checksums.</item>
<item>Add a categorized Joomla changelog and document GitHub secrets and agent responsibilities.</item>
Expand All @@ -21,6 +26,7 @@
<note>
<item>Install the compatible component first. The plugin release never invokes OctoJPack.</item>
<item>Configure the six git-user secrets documented in docs/RELEASE.md before releasing.</item>
<item>Complete both plugin release workflows before running the component workflow to publish the first combined package.</item>
</note>
</changelog>
<changelog>
Expand Down
31 changes: 30 additions & 1 deletion tests/release.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,36 @@
. '<version>[[[NEXT_VERSION]]]</version><fix><item>Release metadata.</item></fix></changelog></changelogs>';
file_put_contents($root . '/joomengine_mcp.xml', '<extension type="plugin" group="console">'
. '<version>1.0.0</version><creationDate>January 2026</creationDate></extension>');
file_put_contents($root . '/joomengine_mcp_changelog.xml', $pending);
$initialChangelog = str_replace('</changelogs>', '<changelog><element>joomengine_mcp</element>'
. '<type>plugin</type><folder>console</folder><version>1.0.0</version>'
. '<note><item>Development baseline.</item></note></changelog></changelogs>', $pending);
file_put_contents($root . '/joomengine_mcp_changelog.xml', $initialChangelog);
file_put_contents($root . '/CHANGELOG.md', "# Changelog\n\n## [[[NEXT_VERSION]]]\n\n### Fix\n\n- Release metadata.\n");
file_put_contents($root . '/joomengine_mcp_update_server.xml', '<updates/>');

$rejectUnchanged = static function (array $arguments, string $message) use ($root, $check): void
{
$before = array_map('file_get_contents', glob($root . '/*'));
$rejected = false;

try
{
mcpRelease($arguments, $root);
}
catch (RuntimeException)
{
$rejected = true;
}

$check($rejected && $before === array_map('file_get_contents', glob($root . '/*')), $message);
};
$rejectUnchanged(['prepare', '1.0.0'], 'A baseline already in the XML changelog cannot be released again.');
$rejectUnchanged(['feed', '1.1.0'], 'An unprepared future version cannot change the feed.');
file_put_contents($root . '/joomengine_mcp_changelog.xml', str_replace('<version>[[[NEXT_VERSION]]]</version>',
'<version>1.0.1</version><note><item>[[[NEXT_VERSION]]]</item></note>', $initialChangelog));
$rejectUnchanged(['prepare', '1.1.0'], 'A marker outside the XML version cannot freeze a release.');
file_put_contents($root . '/joomengine_mcp_changelog.xml', $initialChangelog);

$originalFeed = file_get_contents($root . '/joomengine_mcp_update_server.xml');
mcpRelease(['prepare', 'v1.1.0'], $root);
$manifest = mcpReleaseXml($root . '/joomengine_mcp.xml');
Expand All @@ -45,6 +71,9 @@
$check($query->evaluate('string(/updates/update[version="1.1.0"]/downloads/downloadurl)')
=== 'https://github.com/joomengine/mcp_plugin/archive/refs/tags/v1.1.0.zip', 'Feed downloads the immutable source tag.');
$check($query->evaluate('string(/updates/update[version="1.1.0"]/folder)') === 'console', 'Feed identifies the console plugin.');
$check($query->evaluate('string(/updates/update[version="1.1.0"]/client)') === 'site', 'Feed matches the installed plugin client.');
$check($query->evaluate('string(/updates/update[version="1.1.0"]/infourl)')
=== 'https://github.com/joomengine/mcp_plugin/tree/v1.1.0', 'Feed exposes release information through the native Joomla field.');
$check($query->query('/updates/update[version="1.1.0"]/sha512')->length === 0, 'Checksum generation belongs to OctoShoom.');
mcpReleaseAppend($feed->documentElement->firstChild, 'sha512', str_repeat('a', 128));
$feed->save($root . '/joomengine_mcp_update_server.xml');
Expand Down
18 changes: 16 additions & 2 deletions tools/release.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ function mcpRelease(array $arguments, string $root): void

if ($command === 'prepare')
{
$changelog = new DOMXPath(mcpReleaseXml($root . '/joomengine_mcp_changelog.xml'));

if ($changelog->query('/changelogs/changelog[version="' . $version . '"]')->length !== 0
|| $changelog->query('/changelogs/changelog[version="[[[NEXT_VERSION]]]"]')->length !== 1)
{
throw new RuntimeException('Choose an unreleased version with exactly one pending XML changelog version.');
}

foreach (['CHANGELOG.md', 'joomengine_mcp_changelog.xml'] as $path)
{
$contents = file_get_contents($root . '/' . $path);
Expand All @@ -68,6 +76,11 @@ function mcpRelease(array $arguments, string $root): void
}
else
{
if (version_compare($version, $manifestVersion->textContent, '>'))
{
throw new RuntimeException('Prepare the plugin version before adding its update entry.');
}

$feed = mcpReleaseXml($root . '/joomengine_mcp_update_server.xml');

if ((new DOMXPath($feed))->query('/updates/update[version="' . $version . '"]')->length > 0)
Expand All @@ -79,7 +92,8 @@ function mcpRelease(array $arguments, string $root): void
$feed->documentElement->insertBefore($entry, $feed->documentElement->firstChild);

foreach (['name' => 'JoomEngine MCP Console', 'description' => 'JoomEngine MCP console plugin.',
'element' => 'joomengine_mcp', 'type' => 'plugin', 'version' => $version, 'folder' => 'console'] as $name => $value)
'element' => 'joomengine_mcp', 'type' => 'plugin', 'version' => $version, 'folder' => 'console',
'client' => 'site'] as $name => $value)
{
mcpReleaseAppend($entry, $name, $value);
}
Expand All @@ -93,7 +107,7 @@ function mcpRelease(array $arguments, string $root): void
$platform->setAttribute('name', 'joomla');
$platform->setAttribute('version', '6\\.[1-9][0-9]*');
mcpReleaseAppend($entry, 'php_minimum', '8.3.0');
mcpReleaseAppend($entry, 'detailsurl', 'https://github.com/joomengine/mcp_plugin/tree/v' . $version);
mcpReleaseAppend($entry, 'infourl', 'https://github.com/joomengine/mcp_plugin/tree/v' . $version);
mcpReleaseAppend($entry, 'changelogurl', 'https://raw.githubusercontent.com/joomengine/mcp_plugin/main/joomengine_mcp_changelog.xml');
$writes['joomengine_mcp_update_server.xml'] = $feed->saveXML();
}
Expand Down
Loading