Skip to content

Centralize Docker Hub image publishing for all Drupal CMS site templates - #6

Draft
darrenoh with Copilot wants to merge 5 commits into
mainfrom
copilot/generate-docker-hub-images-for-drupal-cms
Draft

Centralize Docker Hub image publishing for all Drupal CMS site templates#6
darrenoh with Copilot wants to merge 5 commits into
mainfrom
copilot/generate-docker-hub-images-for-drupal-cms

Conversation

Copilot AI commented Jul 31, 2026

Copy link
Copy Markdown

Per-template Docker images were built from separate per-template repositories, creating fragmented publishing and extra overhead. This moves image generation entirely into drupalforge/drupal_cms.

Workflow (docker-publish-template.yml)

  • get-templates job – fetches the canonical template list from the Drupal CMS GitLab API at runtime:

    https://git.drupalcode.org/api/v4/projects/204857/repository/files/site-templates.yml/raw?ref=HEAD
    

    Falls back to a hardcoded list of all 16 known templates when unreachable. Emits a JSON array used by the matrix.

  • build-template-images matrix job – calls the drupalforge/docker_publish_action reusable workflow once per template, publishing drupalforge/<template-name>:main. fail-fast: false ensures one failure doesn't abort other templates.

  • Existing build-application job retained for backward-compatible drupalforge/drupal_cms image.

Template selection in init.sh

Resolution priority for which template to install:

Source Condition Install
DRUPAL_CMS_SITE_TEMPLATE explicitly set drush si drupal_cms_installer installer_site_template_form.add_ons=<value>
DP_APP_ID set by action to drupalforge/<template> derives haven from drupalforge/haven, same drush command
fallback DP_APP_ID basename is drupal_cms existing recipe-based install (unchanged)

This matches exactly how the per-template repos install their templates.

Dockerfile

Added ARG DRUPAL_CMS_SITE_TEMPLATE / ENV DRUPAL_CMS_SITE_TEMPLATE so the template name can be baked into an image as self-describing metadata when --build-arg is passed by the caller.

Documentation (README.md)

Added a "Centralized Docker Hub image publishing" section covering the pipeline, the drupalforge/<template-name>:main naming convention, the DRUPAL_CMS_SITE_TEMPLATE priority chain, and guidance for disabling per-template repository workflows.

- Update docker-publish-template.yml:
  - Add get-templates job: fetches canonical list from Drupal CMS GitLab API
    (with hardcoded fallback for all 16 known templates when unreachable)
  - Add build-template-images matrix job: builds drupalforge/<template> for
    every template in the list via docker_publish_action reusable workflow
  - Keep existing build-application job for backward-compatible drupal_cms image

- Update .devpanel/init.sh:
  - Derive site template from DRUPAL_CMS_SITE_TEMPLATE env var; fall back to
    basename of DP_APP_ID (set by docker_publish_action to the image repo)
  - Use 'drush si drupal_cms_installer installer_site_template_form.add_ons=...'
    when a template is identified (matches how per-template repos install)
  - Preserve existing recipe-based path for the generic drupal_cms image
    (when DP_APP_ID basename is 'drupal_cms' and no explicit template is set)

- Update .devpanel/Dockerfile:
  - Add ARG DRUPAL_CMS_SITE_TEMPLATE / ENV DRUPAL_CMS_SITE_TEMPLATE so callers
    can bake the template name into the image as self-describing metadata

- Update .devpanel/README.md:
  - Document centralized image publishing, naming convention, env var usage,
    and guidance for deprecating per-template repository workflows

Closes #5
Copilot AI changed the title [WIP] Generate Docker Hub images per Drupal CMS site template Centralize Docker Hub image publishing for all Drupal CMS site templates Jul 31, 2026
Copilot AI requested a review from darrenoh July 31, 2026 23:33
…MPLATE

DP_APP_ID is an internal DevPanel variable and must not be used to
select the Drupal CMS site template. Only DRUPAL_CMS_SITE_TEMPLATE
should drive template selection in init.sh.

- init.sh: drop the DP_APP_ID fallback; update comment
- README.md: remove mention of DP_APP_ID as a fallback source
Comment thread .devpanel/Dockerfile

# Allow callers to bake the site template name into the image so init scripts
# can identify which template was installed without needing DP_APP_ID at runtime.
ARG DRUPAL_CMS_SITE_TEMPLATE

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.

How does the workflow populate this argument?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The ARG is populated via the drupal_cms_site_template input added to drupalforge/docker_publish_action (see the companion PR description below). That input appends DRUPAL_CMS_SITE_TEMPLATE=${{ inputs.drupal_cms_site_template }} to the build-args of both docker/build-push-action steps in action.yml, which satisfies the ARG in this Dockerfile and bakes it into the image as the ENV. The Dockerfile comment has been updated to reflect this (commit in this PR).

Comment thread .devpanel/init.sh Outdated
# Determine the site template to install.
# Set DRUPAL_CMS_SITE_TEMPLATE to the template name to select it; leave it
# unset to fall back to the recipe-based base install.
SITE_TEMPLATE="${DRUPAL_CMS_SITE_TEMPLATE:-}"

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.

Why copy this to a new variable?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed. DRUPAL_CMS_SITE_TEMPLATE is now used directly without copying to an intermediate variable.

Comment on lines +29 to +30
# Fallback list derived from the known site-template repositories.
FALLBACK_TEMPLATES='["archimedes","byte","caresphere","convene","convivial_gov","drupal_cms_site_template_base","drupal_cms_starter","everbright","forma","haven","healthcare","local","lupus_decoupled_starter","mercury","provus_edu","pulse"]'

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.

We should not have a fallback list. I gave you the list of existing templates only so you would know which templates need to have their docker-publish-template workflows disabled.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed. The get-templates job now calls exit 1 if the curl fetch fails, so the entire workflow fails rather than silently using a hardcoded list.

TEMPLATES=""
if [ -n "$YAML_CONTENT" ]; then
pip install PyYAML -q 2>/dev/null || true
TEMPLATES=$(echo "$YAML_CONTENT" | python3 -c "import sys,json,yaml; d=yaml.safe_load(sys.stdin.read()); print(json.dumps(list(d.keys()) if isinstance(d,dict) else [str(x) for x in d] if isinstance(d,list) else []))" 2>/dev/null || echo "")

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.

Templates that have a purchase price need to be saved to a private container registry. See https://github.com/drupalforge/meridian_charter_school for how to get the COMPOSER_AUTH secret for these templates.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added a build-paid-template-images matrix job. It omits Docker Hub credentials so the action falls back to GHCR, sets private: true (new input in the companion action PR) to prevent the GHCR package from being made public, and passes composer_auth: ${{ secrets.COMPOSER_AUTH }} for private Composer packages. The get-templates Python parser identifies paid templates by a paid: true or non-zero price field in site-templates.yml.

Copilot AI requested a review from darrenoh August 1, 2026 00:16
Comment on lines +40 to +41
free = [k for k, v in d.items() if not (isinstance(v, dict) and (v.get('paid') or v.get('price')))]
paid = [k for k, v in d.items() if isinstance(v, dict) and (v.get('paid') or v.get('price'))]

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.

Did you actually parse the list? I think paid site templates have a purchase key.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. The parser now checks v.get('purchase') on lines 40–41 instead of v.get('paid') or v.get('price').

Copilot AI requested a review from darrenoh August 1, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generate Docker Hub images per Drupal CMS site template from drupalforge/drupal_cms

2 participants