Skip to content

feat: serve external plugins from nginx via multi-stage Docker build - #511

Merged
stiliyan-v merged 28 commits into
mainfrom
copilot/make-plugins-available-from-nginx
Sep 4, 2026
Merged

feat: serve external plugins from nginx via multi-stage Docker build#511
stiliyan-v merged 28 commits into
mainfrom
copilot/make-plugins-available-from-nginx

Conversation

Copilot AI commented Jun 29, 2026

Copy link
Copy Markdown

External plugins were pulled from third-party URLs at browser runtime via git submodules. This replaces that approach with build-time download and integrity verification, so the CoMPAS nginx container serves all plugins itself with no outbound browser requests.

Changes

distribution/remote-plugins.json (new)

Declarative config listing all 9 external plugins (previously git submodules) with url, dest (path under /external-plugins/), and optional sha256:

{
  "plugins": [
    {
      "name": "Publisher",
      "url": "https://raw.githubusercontent.com/com-pas/oscd-publisher/deploy/oscd-publisher.js",
      "dest": "oscd-publisher/oscd-publisher.js",
      "sha256": ""
    }
  ]
}

distribution/scripts/download-plugins.sh (new)

POSIX shell script run during Docker build:

  • Fetches each plugin via curl --failbuild fails on download error
  • Verifies SHA-256 when provided — build fails on mismatch
  • Emits a stderr warning for any plugin missing a hash
  • Supports REQUIRE_SHA256=true env var to make the build fail for any plugin without a hash (strict mode for production)

distribution/Dockerfile (multi-stage)

  • New plugin-downloader Alpine stage: COPY distribution/remote-plugins.json → run download script. remote-plugins.json is copied before the RUN step so the download layer is only cache-invalidated when the plugin config changes, not on app code changes.
  • Final nginx stage gains COPY --from=plugin-downloader /build/external-plugins /usr/share/nginx/html/external-plugins
  • Exposes ARG REQUIRE_SHA256 for strict-mode builds
  • GitHub workflow files updated to reference file: distribution/Dockerfile (build context remains the repo root)

docs/how-to/remote-plugins.md (new)

Documents config schema, how to compute and add SHA-256 hashes, and the REQUIRE_SHA256 flag.

Notes

  • All container-related files (Dockerfile, remote-plugins.json, scripts/download-plugins.sh) are placed under the distribution/ directory.
  • sha256 fields in distribution/remote-plugins.json are intentionally empty; they must be populated before production use (see docs for curl | sha256sum workflow).
  • nginx already serves everything under /usr/share/nginx/html; no nginx config changes needed.

Copilot AI changed the title [WIP] Add configuration file for external remote plugins in CoMPAS nginx container feat: serve external plugins from nginx via multi-stage Docker build Jun 29, 2026
Copilot AI requested a review from david-monichi June 29, 2026 17:24
@david-monichi

Copy link
Copy Markdown

@copilot
revalidate if the ticket is fully implemented, specify differences and adapt the code base to match the issues requirements

Copilot AI commented Jul 14, 2026

Copy link
Copy Markdown
Author

@copilot
revalidate if the ticket is fully implemented, specify differences and adapt the code base to match the issues requirements

Revalidation complete. Here is the gap analysis against all 10 acceptance criteria and the fix applied.

All criteria were met except one critical bug:

# Acceptance criterion Status
1 Config file with remote URL remote-plugins.json
2 Hash code field per entry sha256 field
3 Hash verified at container build time scripts/download-plugins.sh
4 Build fails on download failure curl --fail + exit 1
5 Build fails on hash mismatch exit 1 on mismatch
6 Multi-stage Docker build plugin-downloader + nginx stage
7 Minimise layer changes / cache COPY remote-plugins.json placed before RUN
8 nginx serves downloaded plugins COPY --from=plugin-downloader
9 Editor loads plugins from nginx only Bug (see below)
10 Documentation docs/how-to/remote-plugins.md

Criterion 9 — bug fixed in commit e2a3fba

The IED Editor entry in remote-plugins.json tried to download IedEditor.js from the oscd-editor-ied deploy branch:

https://raw.githubusercontent.com/OMICRONEnergyOSS/oscd-editor-ied/deploy/IedEditor.js

IedEditor.js is a local re-export shim committed only in this repository — it does not exist on the deploy branch. curl --fail would receive a 404, fail with a non-zero exit, and abort the Docker build before any plugin was served.

Fixed by aligning with the consistent pattern used by all other plugins:

  • remote-plugins.json: URL points to the natural deploy-branch bundle oscd-editor-ied.js; dest is oscd-editor-ied/oscd-editor-ied.js
  • plugins.js: IED src updated to /external-plugins/oscd-editor-ied/oscd-editor-ied.js

This path also works in local development mode: when the submodule is initialised, vite copies packages/external-plugins/oscd-editor-ied/**dist/external-plugins/oscd-editor-ied/, so no separate handling is needed.

@david-monichi

Copy link
Copy Markdown

@copilot
Reconsider the change from issue #510 that all container related files will be placed to a directory "distribution"

Copilot AI commented Jul 14, 2026

Copy link
Copy Markdown
Author

@copilot
Reconsider the change from issue #510 that all container related files will be placed to a directory "distribution"

Done in commit refactor: move container-related files to distribution/ directory. All container-related files have been moved under distribution/ as required by issue #510:

  • Dockerfiledistribution/Dockerfile
  • remote-plugins.jsondistribution/remote-plugins.json
  • scripts/download-plugins.shdistribution/scripts/download-plugins.sh

The Dockerfile COPY paths were updated to reference the distribution/ prefix (build context remains the repo root). Both GitHub workflow files now include file: distribution/Dockerfile. Documentation updated accordingly.

Comment thread distribution/Dockerfile
Comment thread docs/how-to/remote-plugins.md Outdated
Comment thread distribution/README.md
Comment thread distribution/README.md
Comment thread docs/how-to/remote-plugins.md Outdated
Comment thread docs/how-to/remote-plugins.md Outdated
optional editor metadata: `--icon`, `--kind`, `--active-by-default`,
`--require-doc`. To compute the hash beforehand:

```sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this relevant for us, specially here? Eventually I would add a section troubleshooting with this information. not everybody will have curl installed ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not relevant, i deleted the line

Comment thread distribution/README.md Outdated
Comment thread distribution/scripts/download-plugins.sh Outdated
Comment thread distribution/scripts/download-plugins.sh
Comment thread distribution/scripts/manage-plugins.js Outdated
Copilot AI requested a review from david-monichi August 6, 2026 16:22
@david-monichi
david-monichi marked this pull request as ready for review August 11, 2026 19:32
@david-monichi
david-monichi requested a review from a team as a code owner August 11, 2026 19:32
@david-monichi

Copy link
Copy Markdown

@noraeb @clepski since its a bigger change we would love to if you can have a look at it and approve it as well. If you have any questions, just let me know, I will check the unsigned commits in the meantime

Copilot AI and others added 6 commits August 12, 2026 22:05
…r build

Add a JSON configuration file, a download/validation shell script, and a
multi-stage Dockerfile so that external plugins are fetched and integrity-
checked at container build time and then served directly by the CoMPAS nginx
service — no browser requests to external sources required.

- remote-plugins.json: lists each plugin URL, local destination path and
  optional SHA-256 digest
- scripts/download-plugins.sh: downloads every plugin with curl; fails the
  build if a download fails or a provided SHA-256 does not match
- Dockerfile: new plugin-downloader Alpine stage before the nginx stage;
  layer ordering ensures the download cache is only invalidated when
  remote-plugins.json actually changes
- docs/how-to/remote-plugins.md: explains the config format, how to add or
  update plugins, and security recommendations

Closes #510
- Print the "no SHA256" warning to stderr so it is visible in build logs
- Add REQUIRE_SHA256 env-var / Docker build-arg: when true, the build fails
  immediately for any plugin that has an empty sha256 field
- Expose ARG REQUIRE_SHA256 in the Dockerfile plugin-downloader stage
- Document the new flag in docs/how-to/remote-plugins.md
The IED Editor entry in remote-plugins.json referenced IedEditor.js,
a local re-export shim that only exists in the repository's
packages/external-plugins/ directory and is NOT present on the
oscd-editor-ied deploy branch. This caused curl --fail to receive a 404
and fail the Docker build.

Fix by aligning with the consistent pattern used by all other plugins:
download the natural bundle file (oscd-editor-ied.js) directly from the
deploy branch and serve it under its natural path, and update plugins.js
to reference /external-plugins/oscd-editor-ied/oscd-editor-ied.js.

This also matches local development: when the submodule is initialised,
vite copies packages/external-plugins/oscd-editor-ied/** → dist/external-
plugins/oscd-editor-ied/, so the same path works in both modes.
Per issue #510, all container-related files are now placed under
the distribution/ directory:

- Dockerfile → distribution/Dockerfile
- remote-plugins.json → distribution/remote-plugins.json
- scripts/download-plugins.sh → distribution/scripts/download-plugins.sh

Update Dockerfile COPY paths to reference distribution/ prefix (build
context remains the repo root). Add file: distribution/Dockerfile to
both workflow files. Update docs to reference new paths.
stiliyan-v and others added 12 commits August 12, 2026 22:05
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
…secure

Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
…nt flags

Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Updated sha256 values to empty strings for all plugins so that when updates are deployed always the version is taken without build issues.

Signed-off-by: David Monichi <david.monichi@bearingpoint.com>
Signed-off-by: David Monichi <david.monichi@bearingpoint.com>
Signed-off-by: David Monichi <david.monichi@bearingpoint.com>
@david-monichi
david-monichi force-pushed the copilot/make-plugins-available-from-nginx branch from 3b6acee to 2b9dade Compare August 12, 2026 20:07
Comment thread public/js/plugins.js Outdated
{
name: 'IED',
src: '/external-plugins/IedEditor.js',
src: '/external-plugins/oscd-editor-ied/oscd-editor-ied.js',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might break the IED plugin, because the oscd-editor-ied doesn't have a default export (which CoMPAS expects). So /external-plugins/IedEditor.js' was added as a workaround:

export { OscdEditorIED as default } from './oscd-editor-ied/oscd-editor-ied.js'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the old path has been restored.

Comment thread public/js/plugins.js
Comment on lines +405 to +459
{
name: 'Plugins Hub',
src: '/external-plugins/compas-bearingpoint-plugins/plugins/plugins-hub/index.js',
icon: 'details-icon',
activeByDefault: true,
kind: 'editor',
requireDoc: true,
},
{
name: 'Engineering Wizard',
src: '/external-plugins/compas-transnetbw-plugins/plugins/engineering-wizard/index.js',
icon: 'star',
activeByDefault: true,
kind: 'editor',
requireDoc: true,
},
{
name: 'Template Generator (TransnetBW)',
src: '/external-plugins/compas-transnetbw-plugins/plugins/template-generator/index.js',
icon: 'copy_all',
activeByDefault: true,
kind: 'editor',
requireDoc: true,
},
{
name: 'Location Manager',
src: '/external-plugins/compas-transnetbw-plugins/plugins/location-manager/index.js',
icon: 'edit_location_alt',
activeByDefault: false,
kind: 'editor',
requireDoc: true,
},
{
name: 'Location Viewer',
src: '/external-plugins/compas-transnetbw-plugins/plugins/location-viewer/index.js',
icon: 'location_on',
activeByDefault: false,
kind: 'editor',
requireDoc: true,
},
{
name: 'History Viewer',
src: '/external-plugins/compas-transnetbw-plugins/plugins/history-viewer/index.js',
icon: 'history',
activeByDefault: false,
kind: 'editor',
requireDoc: true,
},
{
name: 'Archive Explorer',
src: '/external-plugins/compas-transnetbw-plugins/plugins/archive-explorer/index.js',
icon: 'archive',
activeByDefault: false,
kind: 'editor',
requireDoc: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess these plugins only work within the container and not when running locally via pnpm start? Could we add a way to populate /external-plugins for local dev too?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, a few things have been added:

  • a new script npm run download:plugins , which executes /distribution/scripts/download-plugins.sh locally;
  • a new target in vite.config.ts to copy the downloaded files into /dist/external-plugins.

Note: to execute the shell script in a Windows environment, Git Bash or WSL is needed.

Comment on lines +40 to +43
"name": "plugins-hub",
"url": "https://ase-compas.github.io/compas-bearingpoint-plugins/compas/plugins/plugins-hub/index.js",
"dest": "compas-bearingpoint-plugins/plugins/plugins-hub/index.js",
"sha256": ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When building the new docker image locally and testing it, I noticed the Plugin store fails to load any registries:

GET /proxy/sprinteins.github.io/oscd-plugin-store/plugins.json 404 (Not Found)
GET /proxy/ase-compas.github.io/compas-transnetbw-plugins/plugins.json 404 (Not Found)
GET /proxy/ase-compas.github.io/compas-bearingpoint-plugins/plugins.json 404 (Not Found)

Not sure if that's just an issue in my environment or if there's something missing in the nginx config?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In local development, Plugins Hub requires a configured wiremock proxy running in background (more info here: DEVELOPMENT.md). For this, the nginx config must be also extended with more locations (only for local environment), however I'm not exactly sure if I should do it in this PR.

noraeb
noraeb previously approved these changes Aug 27, 2026
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
…c copy

Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
@stiliyan-v
stiliyan-v force-pushed the copilot/make-plugins-available-from-nginx branch from 7e7b352 to 9ce2793 Compare August 31, 2026 07:38
Signed-off-by: Stiliyan Valkanov <stiliyan.valkanov@bearingpoint.com>
@sonarqubecloud

sonarqubecloud Bot commented Sep 2, 2026

Copy link
Copy Markdown

@stiliyan-v
stiliyan-v merged commit b39159f into main Sep 4, 2026
7 checks passed
@stiliyan-v
stiliyan-v deleted the copilot/make-plugins-available-from-nginx branch September 4, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

4 participants