From 7187b902a02876644e3cd28004a3e6dee3e68e42 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:08:27 -0700 Subject: [PATCH 01/21] feat: add Drupal issue picker and issue fork support to drupal-core template Adds a web picker page at start.coder.ddev.com/drupal-issue that fetches issue metadata from drupal.org and branch lists from git.drupalcode.org, then constructs a Coder workspace URL with pre-filled template parameters. Adds issue_fork, issue_branch, and install_profile variables to the drupal-core template. The startup script checks out the issue fork branch before ddev composer install (so vendor reflects the branch), and bypasses the DB seed cache when an issue fork is active (always runs ddev drush si). Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 542 ++++++++++++++++++++++++++++++++++++++++ docs/index.html | 13 + drupal-core/template.tf | 105 +++++++- 3 files changed, 654 insertions(+), 6 deletions(-) create mode 100644 docs/drupal-issue.html diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html new file mode 100644 index 0000000..51a5180 --- /dev/null +++ b/docs/drupal-issue.html @@ -0,0 +1,542 @@ + + + + + + Drupal Issue Picker — DDEV Coder Workspaces + + + + +
+ DDEV Coder Workspaces + / + Drupal Issue Picker +
+ +
+

Drupal Issue Picker

+

Enter a Drupal.org issue URL or number to launch a pre-configured workspace.

+ +
+ + +
+ +
+ + +
+
+ Issue + +
+ +
+
+ + +
+ +
+ + +
demo_umami uses a pre-built database for fast startup. Other profiles require a full site install.
+
+ +
+ + +
Must be lowercase letters, numbers, and hyphens only.
+
+
+ + +
+ + +
+
+ Could not fetch issue data automatically. Enter values manually below. +
+ +
+
+ + +
Format: drupal-{issue-number}
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ +
+

Notes

+
    +
  • Requires a GitHub account — sign in at coder.ddev.com first
  • +
  • Issue fork workspaces always run a full ddev drush si (branch code may differ from the cached database)
  • +
  • The demo_umami fast path (~30s) is only used for standard Drupal core without an issue fork
  • +
  • Issue fork must exist on git.drupalcode.org — click "Get push access" on the issue page to create one
  • +
+
+
+ + + + + diff --git a/docs/index.html b/docs/index.html index 060bab9..dd090bc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -257,6 +257,19 @@

What you get

+
+

Drupal issue development

+
+
+
+

Drupal Issue Picker

+

Enter a Drupal.org issue number — auto-selects branch and launches a workspace with the issue fork checked out

+
+ Open Picker +
+
+
+

Other templates

diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 8e08330..a877151 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -64,6 +64,28 @@ variable "cache_path" { default = "/home/rfay/cache/drupal-core-seed" } +variable "issue_fork" { + description = "Drupal.org issue fork name (e.g., drupal-3568144). Leave empty for standard Drupal core development." + type = string + default = "" +} + +variable "issue_branch" { + description = "Issue branch to check out in the drupal core git repo (e.g., 3568144-editorfilterxss-11.x). Leave empty for HEAD." + type = string + default = "" +} + +variable "install_profile" { + description = "Drupal install profile. demo_umami uses the seed cache for fast startup (~30s). Other profiles require a full site install (~3-5 min)." + type = string + default = "demo_umami" + validation { + condition = contains(["demo_umami", "minimal", "standard"], var.install_profile) + error_message = "install_profile must be one of: demo_umami, minimal, standard" + } +} + # Workspace data source @@ -475,6 +497,7 @@ STATUS_HEADER CACHE_SEED="/home/coder-cache-seed" DRUPAL_SETUP_NEEDED=false + ISSUE_FORK_CHECKOUT_DONE=false SETUP_START=$SECONDS # Diagnostic: report what the cache mount contains @@ -495,6 +518,16 @@ STATUS_HEADER log_setup " .tarballs/db.sql.gz: MISSING" fi + # Issue fork / install profile parameters (baked in at template evaluation) + ISSUE_FORK="${var.issue_fork}" + ISSUE_BRANCH="${var.issue_branch}" + INSTALL_PROFILE="${var.install_profile}" + USING_ISSUE_FORK=false + if [ -n "$ISSUE_FORK" ] || [ -n "$ISSUE_BRANCH" ]; then + USING_ISSUE_FORK=true + log_setup "Issue fork mode: ISSUE_FORK=$ISSUE_FORK ISSUE_BRANCH=$ISSUE_BRANCH INSTALL_PROFILE=$INSTALL_PROFILE" + fi + # Step 4: Set up Drupal core project — use seed cache when available (fast path) if [ -f "composer.json" ] && [ -d "repos/drupal/.git" ]; then log_setup "✓ Drupal core project already present — skipping setup" @@ -510,7 +543,31 @@ STATUS_HEADER _t=$SECONDS git -C "$DRUPAL_DIR/repos/drupal" fetch --all --prune >> "$SETUP_LOG" 2>&1 || true log_setup " git fetch complete ($((SECONDS - _t))s)" - # Ensure vendor matches current composer.lock (no-op when lock is unchanged) + # Checkout issue fork branch (must happen before composer install so vendor matches branch) + if [ "$USING_ISSUE_FORK" = "true" ]; then + _t=$SECONDS + REPOS_DIR="$DRUPAL_DIR/repos/drupal" + CURRENT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") + if [ -n "$ISSUE_BRANCH" ] && [ "$CURRENT_BRANCH" = "$ISSUE_BRANCH" ]; then + log_setup " ✓ Already on issue branch: $ISSUE_BRANCH" + else + if [ -n "$ISSUE_FORK" ]; then + log_setup " Adding issue fork remote: $ISSUE_FORK" + git -C "$REPOS_DIR" remote remove issue 2>/dev/null || true + git -C "$REPOS_DIR" remote add issue "https://git.drupalcode.org/issue/$ISSUE_FORK.git" + git -C "$REPOS_DIR" fetch issue 2>&1 | tee -a "$SETUP_LOG" || true + fi + if [ -n "$ISSUE_BRANCH" ]; then + log_setup " Checking out issue branch: $ISSUE_BRANCH" + git -C "$REPOS_DIR" checkout -b "$ISSUE_BRANCH" "issue/$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ + git -C "$REPOS_DIR" checkout "$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ + log_setup " ✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" + fi + fi + log_setup " issue fork setup complete ($((SECONDS - _t))s)" + ISSUE_FORK_CHECKOUT_DONE=true + fi + # Ensure vendor matches current composer.lock (no-op when lock is unchanged; reflects issue branch if checked out) _t=$SECONDS ddev composer install >> "$SETUP_LOG" 2>&1 || true log_setup " composer install complete ($((SECONDS - _t))s)" @@ -548,6 +605,34 @@ STATUS_HEADER # Steps 5-7: run whenever project files are present — inner checks handle idempotency if [ -f "composer.json" ] && [ -d "repos/drupal" ]; then + # Step 4.5: Issue fork checkout — runs if not already done in the cache-seed fast path + # Must happen before Drush install so that vendor reflects the correct branch + if [ "$USING_ISSUE_FORK" = "true" ] && [ "$ISSUE_FORK_CHECKOUT_DONE" = "false" ]; then + REPOS_DIR="$DRUPAL_DIR/repos/drupal" + if [ -d "$REPOS_DIR/.git" ]; then + CURRENT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") + if [ -n "$ISSUE_BRANCH" ] && [ "$CURRENT_BRANCH" = "$ISSUE_BRANCH" ]; then + log_setup "✓ Already on issue branch: $ISSUE_BRANCH" + else + if [ -n "$ISSUE_FORK" ]; then + log_setup "Adding issue fork remote: $ISSUE_FORK" + git -C "$REPOS_DIR" remote remove issue 2>/dev/null || true + git -C "$REPOS_DIR" remote add issue "https://git.drupalcode.org/issue/$ISSUE_FORK.git" + git -C "$REPOS_DIR" fetch issue 2>&1 | tee -a "$SETUP_LOG" || true + fi + if [ -n "$ISSUE_BRANCH" ]; then + log_setup "Checking out issue branch: $ISSUE_BRANCH" + git -C "$REPOS_DIR" checkout -b "$ISSUE_BRANCH" "issue/$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ + git -C "$REPOS_DIR" checkout "$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ + log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" + fi + fi + # Re-run composer install so vendor reflects the checked-out branch + log_setup "Running composer install for issue branch..." + ddev composer install >> "$SETUP_LOG" 2>&1 || true + fi + fi + # Step 5: Ensure Drush is available (skip if already present from cache) if [ -f "vendor/bin/drush" ]; then log_setup "✓ Drush already present" @@ -567,10 +652,14 @@ STATUS_HEADER fi # Step 6: Install or import Drupal database + # Fast path (DB cache import) is only used when: + # - No issue fork (issue code may differ from cached DB) + # - Install profile is demo_umami (cache was built with that profile) + # - Cache tarball exists if ddev drush status 2>/dev/null | grep -q "Drupal bootstrap.*Successful"; then log_setup "✓ Drupal already installed" update_status "✓ Drupal install: Already present" - elif [ -f "$CACHE_SEED/.tarballs/db.sql.gz" ]; then + elif [ "$USING_ISSUE_FORK" = "false" ] && [ "$INSTALL_PROFILE" = "demo_umami" ] && [ -f "$CACHE_SEED/.tarballs/db.sql.gz" ]; then _t=$SECONDS log_setup "Importing database from cache (fast path)..." update_status "⏳ Drupal install: Importing cached database..." @@ -587,7 +676,7 @@ STATUS_HEADER log_setup "⚠ DB import failed ($((SECONDS - _t))s), falling back to full site install..." update_status "⚠ DB import failed, running full install..." _t=$SECONDS - if ddev drush si -y demo_umami --account-pass=admin >> "$SETUP_LOG" 2>&1; then + if ddev drush si -y "$INSTALL_PROFILE" --account-pass=admin >> "$SETUP_LOG" 2>&1; then log_setup "✓ Drupal installed successfully (fallback, $((SECONDS - _t))s)" update_status "✓ Drupal install: Success (fallback)" else @@ -597,10 +686,14 @@ STATUS_HEADER fi else _t=$SECONDS - log_setup "Installing Drupal with demo_umami profile (this will take 2-3 minutes)..." + if [ "$USING_ISSUE_FORK" = "true" ]; then + log_setup "Installing Drupal with $INSTALL_PROFILE profile (issue fork: full install required)..." + else + log_setup "Installing Drupal with $INSTALL_PROFILE profile (this will take 2-3 minutes)..." + fi update_status "⏳ Drupal install: In progress..." - if ddev drush si -y demo_umami --account-pass=admin >> "$SETUP_LOG" 2>&1; then + if ddev drush si -y "$INSTALL_PROFILE" --account-pass=admin >> "$SETUP_LOG" 2>&1; then log_setup "✓ Drupal installed ($((SECONDS - _t))s)" log_setup "" log_setup " Admin Credentials:" @@ -615,7 +708,7 @@ STATUS_HEADER update_status "" update_status "Manual recovery:" update_status " cd $DRUPAL_DIR" - update_status " ddev drush si -y demo_umami --account-pass=admin" + update_status " ddev drush si -y $INSTALL_PROFILE --account-pass=admin" fi fi From 66bbd8278a31c1553d7387c262d12de2d2286fc0 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:17:36 -0700 Subject: [PATCH 02/21] fix: derive issue fork name from issue number instead of missing API field The Drupal.org REST API does not return a field_issue_fork field. The fork name is always drupal-{nid} by convention on git.drupalcode.org. Verify fork existence via the GitLab branches API (404 = no fork yet) and fetch the issue title from drupal.org as a best-effort secondary call. Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 48 ++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index 51a5180..98b151f 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -418,42 +418,34 @@

Notes

setStatus('Loading issue ' + nid + '...', 'loading'); try { - // Step 1: fetch issue from Drupal.org API - const issueResp = await fetch('https://www.drupal.org/api-d7/node/' + nid + '.json'); - if (!issueResp.ok) throw new Error('Drupal.org API returned ' + issueResp.status); - const issue = await issueResp.json(); - - const title = issue.title || ('Issue #' + nid); - - // Extract issue fork name - // field_issue_fork may be a string, an object, or an array depending on API version - let issueFork = ''; - const f = issue.field_issue_fork; - if (typeof f === 'string') { - issueFork = f; - } else if (f && typeof f === 'object') { - // Try common shapes: {value: '...'}, {und: [{value: '...'}]}, or plain string nested - if (f.value) issueFork = f.value; - else if (Array.isArray(f.und) && f.und[0] && f.und[0].value) issueFork = f.und[0].value; - else if (Array.isArray(f) && f[0] && f[0].value) issueFork = f[0].value; - } + // Issue fork name is always drupal-{nid} by convention on git.drupalcode.org + const issueFork = 'drupal-' + nid; + const encodedProject = encodeURIComponent('issue/' + issueFork); - if (!issueFork) { + // Step 1: verify fork exists and fetch branches from git.drupalcode.org + setStatus('Loading issue fork ' + issueFork + '...', 'loading'); + const branchResp = await fetch( + 'https://git.drupalcode.org/api/v4/projects/' + encodedProject + '/repository/branches?per_page=100&order_by=updated_at&sort=desc' + ); + if (branchResp.status === 404) { setStatus('Issue #' + nid + ' does not have an issue fork yet. Click "Get push access" on the issue page to create one, then try again.', 'info'); document.getElementById('load-btn').disabled = false; return; } + if (!branchResp.ok) throw new Error('git.drupalcode.org API returned ' + branchResp.status); + const branches = await branchResp.json(); currentIssueFork = issueFork; - // Step 2: fetch branches from git.drupalcode.org - setStatus('Loading branches for ' + issueFork + '...', 'loading'); - const encodedProject = encodeURIComponent('issue/' + issueFork); - const branchResp = await fetch( - 'https://git.drupalcode.org/api/v4/projects/' + encodedProject + '/repository/branches?per_page=100&order_by=updated_at&sort=desc' - ); - if (!branchResp.ok) throw new Error('git.drupalcode.org API returned ' + branchResp.status); - const branches = await branchResp.json(); + // Step 2: fetch issue title from Drupal.org API (best-effort — don't block on failure) + let title = 'Issue #' + nid; + try { + const issueResp = await fetch('https://www.drupal.org/api-d7/node/' + nid + '.json'); + if (issueResp.ok) { + const issue = await issueResp.json(); + if (issue.title) title = issue.title; + } + } catch (_) { /* title stays as fallback */ } // Populate branch selector const branchSelect = document.getElementById('branch-select'); From e4c6406a9b3b37dbdb99a6dace2c305be9e40538 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:23:59 -0700 Subject: [PATCH 03/21] fix: remove unsupported order_by/sort params from GitLab branches API call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git.drupalcode.org returns 400 for order_by=updated_at&sort=desc — remove those query params (branch selection by issue number prefix still works). Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index 98b151f..167dc49 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -425,7 +425,7 @@

Notes

// Step 1: verify fork exists and fetch branches from git.drupalcode.org setStatus('Loading issue fork ' + issueFork + '...', 'loading'); const branchResp = await fetch( - 'https://git.drupalcode.org/api/v4/projects/' + encodedProject + '/repository/branches?per_page=100&order_by=updated_at&sort=desc' + 'https://git.drupalcode.org/api/v4/projects/' + encodedProject + '/repository/branches?per_page=100' ); if (branchResp.status === 404) { setStatus('Issue #' + nid + ' does not have an issue fork yet. Click "Get push access" on the issue page to create one, then try again.', 'info'); From d6b8bc8b04950ec938903cba2358f4dea87fdf87 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:46:57 -0700 Subject: [PATCH 04/21] Add file to image --- image/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/image/Dockerfile b/image/Dockerfile index e51cc4f..6a6ae76 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update && \ ca-certificates \ curl \ direnv \ + file \ git \ gnupg \ httpie \ From 7bdf502002628b0638eb60c626c3970283c04cfc Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:47:58 -0700 Subject: [PATCH 05/21] fix: use coder_parameter instead of variable for issue_fork/issue_branch/install_profile Terraform variable{} blocks are admin-level (set at template push time) and do not appear in the workspace creation UI. Per-workspace user inputs require data "coder_parameter" resources, which show up in the creation form and support ?param.name=value URL pre-filling from the picker page. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 57 ++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index a877151..9edc211 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -64,25 +64,46 @@ variable "cache_path" { default = "/home/rfay/cache/drupal-core-seed" } -variable "issue_fork" { - description = "Drupal.org issue fork name (e.g., drupal-3568144). Leave empty for standard Drupal core development." - type = string - default = "" +# Per-workspace user parameters (shown in workspace creation UI, pre-fillable via ?param.name=value URL) +data "coder_parameter" "issue_fork" { + name = "issue_fork" + display_name = "Issue Fork" + description = "Drupal.org issue fork name (e.g., drupal-3568144). Leave empty for standard Drupal core development." + type = "string" + default = "" + mutable = true + order = 1 } -variable "issue_branch" { - description = "Issue branch to check out in the drupal core git repo (e.g., 3568144-editorfilterxss-11.x). Leave empty for HEAD." - type = string - default = "" +data "coder_parameter" "issue_branch" { + name = "issue_branch" + display_name = "Issue Branch" + description = "Issue branch to check out (e.g., 3568144-editorfilterxss-11.x). Leave empty for HEAD." + type = "string" + default = "" + mutable = true + order = 2 } -variable "install_profile" { - description = "Drupal install profile. demo_umami uses the seed cache for fast startup (~30s). Other profiles require a full site install (~3-5 min)." - type = string - default = "demo_umami" - validation { - condition = contains(["demo_umami", "minimal", "standard"], var.install_profile) - error_message = "install_profile must be one of: demo_umami, minimal, standard" +data "coder_parameter" "install_profile" { + name = "install_profile" + display_name = "Install Profile" + description = "demo_umami uses the seed cache for fast startup (~30s). Other profiles require a full site install (~3-5 min)." + type = "string" + default = "demo_umami" + mutable = true + order = 3 + option { + name = "demo_umami (fast, ~30s — recommended)" + value = "demo_umami" + } + option { + name = "minimal (~3-5 min, full install)" + value = "minimal" + } + option { + name = "standard (~3-5 min, full install)" + value = "standard" } } @@ -519,9 +540,9 @@ STATUS_HEADER fi # Issue fork / install profile parameters (baked in at template evaluation) - ISSUE_FORK="${var.issue_fork}" - ISSUE_BRANCH="${var.issue_branch}" - INSTALL_PROFILE="${var.install_profile}" + ISSUE_FORK="${data.coder_parameter.issue_fork.value}" + ISSUE_BRANCH="${data.coder_parameter.issue_branch.value}" + INSTALL_PROFILE="${data.coder_parameter.install_profile.value}" USING_ISSUE_FORK=false if [ -n "$ISSUE_FORK" ] || [ -n "$ISSUE_BRANCH" ]; then USING_ISSUE_FORK=true From 0ed479aac3b8e2011fab912f005214dd978ea1b0 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:55:34 -0700 Subject: [PATCH 06/21] fix: improve no-issue-fork message to explain what to do Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index 167dc49..ff6f5c8 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -428,7 +428,7 @@

Notes

'https://git.drupalcode.org/api/v4/projects/' + encodedProject + '/repository/branches?per_page=100' ); if (branchResp.status === 404) { - setStatus('Issue #' + nid + ' does not have an issue fork yet. Click "Get push access" on the issue page to create one, then try again.', 'info'); + setStatus('No issue fork exists for #' + nid + ' yet. To work on this issue, visit the issue page on drupal.org and click "Get push access" to create a fork, then come back here.', 'info'); document.getElementById('load-btn').disabled = false; return; } From 9909b9257c14d9b793a3e6e0603c108184a36289 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 08:56:58 -0700 Subject: [PATCH 07/21] fix: add unconditional ddev drush cr after setup to ensure clean cache state Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 9edc211..19d193c 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -733,6 +733,10 @@ STATUS_HEADER fi fi + # Step 6.5: Cache rebuild — ensures a clean state after any setup path + log_setup "Running cache rebuild..." + ddev drush cr >> "$SETUP_LOG" 2>&1 || true + # Step 7: Install custom DDEV launch command mkdir -p ~/.ddev/commands/host cat > ~/.ddev/commands/host/launch << 'LAUNCH_EOF' From 2b3358a8a5d63e88ded4157091477865e21b5713 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:08:41 -0700 Subject: [PATCH 08/21] feat: add Drupal version selector to issue picker and template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add drupal_version coder_parameter (10/11/12) to drupal-core template - Set DDEV project type and PHP version based on selected version: drupal10→PHP 8.3, drupal11→PHP 8.3, drupal12→PHP 8.5 - Add Drupal Version dropdown to drupal-issue.html picker (auto-detected from field_issue_version in Drupal.org API, user-editable) - Include param.drupal_version in generated Coder workspace URL - Add version select to manual fallback form as well Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 44 +++++++++++++++++++++++++++++++++++------ drupal-core/template.tf | 38 +++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index ff6f5c8..2d76ce0 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -310,7 +310,17 @@

Drupal Issue Picker

-
demo_umami uses a pre-built database for fast startup. Other profiles require a full site install.
+
demo_umami uses a pre-built database for fast startup. Other profiles require a full site install. Issue fork workspaces always do a full install.
+
+ +
+ + +
Auto-detected from the issue. Sets the DDEV project type and PHP version.
@@ -350,6 +360,15 @@

Drupal Issue Picker

+
+ + +
+
@@ -376,6 +395,7 @@

Notes

let currentIssueFork = ''; let currentNid = ''; + let currentDrupalMajor = '12'; function setStatus(msg, type) { const el = document.getElementById('status'); @@ -437,15 +457,20 @@

Notes

currentIssueFork = issueFork; - // Step 2: fetch issue title from Drupal.org API (best-effort — don't block on failure) + // Step 2: fetch issue title and version from Drupal.org API (best-effort — don't block on failure) let title = 'Issue #' + nid; + let drupalMajor = '12'; try { const issueResp = await fetch('https://www.drupal.org/api-d7/node/' + nid + '.json'); if (issueResp.ok) { const issue = await issueResp.json(); if (issue.title) title = issue.title; + // field_issue_version e.g. "10.6.x-dev" or "11.x-dev" → extract major version + const ver = issue.field_issue_version || ''; + const m = ver.match(/^(\d+)\./); + if (m) drupalMajor = m[1]; } - } catch (_) { /* title stays as fallback */ } + } catch (_) { /* title/version stay as fallback */ } // Populate branch selector const branchSelect = document.getElementById('branch-select'); @@ -464,6 +489,10 @@

Notes

// Set issue title document.getElementById('issue-title-text').textContent = '#' + nid + ': ' + title; + // Store and display detected Drupal version + currentDrupalMajor = drupalMajor; + document.getElementById('drupal-version-select').value = drupalMajor; + // Pre-fill workspace name const wsName = document.getElementById('workspace-name'); wsName.value = 'issue-' + nid; @@ -483,7 +512,7 @@

Notes

document.getElementById('load-btn').disabled = false; } - function buildCoderUrl(issueFork, issueBranch, installProfile, workspaceName) { + function buildCoderUrl(issueFork, issueBranch, installProfile, drupalVersion, workspaceName) { const base = CODER_BASE + '/templates/' + CODER_TEMPLATE + '/workspace'; const params = new URLSearchParams({ mode: 'manual', @@ -491,6 +520,7 @@

Notes

'param.issue_fork': issueFork, 'param.issue_branch': issueBranch, 'param.install_profile': installProfile, + 'param.drupal_version': drupalVersion, }); return base + '?' + params.toString(); } @@ -498,6 +528,7 @@

Notes

function openWorkspace() { const branch = document.getElementById('branch-select').value; const profile = document.getElementById('profile-select').value; + const drupalVersion = document.getElementById('drupal-version-select').value; const wsName = document.getElementById('workspace-name').value || ('issue-' + currentNid); if (!currentIssueFork || !branch) { @@ -505,7 +536,7 @@

Notes

return; } - const url = buildCoderUrl(currentIssueFork, branch, profile, wsName); + const url = buildCoderUrl(currentIssueFork, branch, profile, drupalVersion, wsName); window.open(url, '_blank'); } @@ -513,6 +544,7 @@

Notes

const fork = document.getElementById('manual-fork').value.trim(); const branch = document.getElementById('manual-branch').value.trim(); const profile = document.getElementById('manual-profile').value; + const drupalVersion = document.getElementById('manual-drupal-version').value; const wsName = document.getElementById('manual-workspace-name').value || ('issue-' + currentNid); if (!fork || !branch) { @@ -520,7 +552,7 @@

Notes

return; } - const url = buildCoderUrl(fork, branch, profile, wsName); + const url = buildCoderUrl(fork, branch, profile, drupalVersion, wsName); window.open(url, '_blank'); } diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 19d193c..41ac475 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -85,6 +85,28 @@ data "coder_parameter" "issue_branch" { order = 2 } +data "coder_parameter" "drupal_version" { + name = "drupal_version" + display_name = "Drupal Version" + description = "Major Drupal version — sets DDEV project type and PHP version. Match the version of the issue you are working on." + type = "string" + default = "12" + mutable = true + order = 4 + option { + name = "12.x (HEAD / upcoming)" + value = "12" + } + option { + name = "11.x (stable)" + value = "11" + } + option { + name = "10.x (stable)" + value = "10" + } +} + data "coder_parameter" "install_profile" { name = "install_profile" display_name = "Install Profile" @@ -460,12 +482,20 @@ STATUS_HEADER cd "$DRUPAL_DIR" || exit 1 # Step 2: Configure DDEV (must be done before composer create) + # Derive project type and PHP version from the Drupal major version parameter + DRUPAL_VERSION="${data.coder_parameter.drupal_version.value}" + case "$DRUPAL_VERSION" in + 10) DDEV_PROJECT_TYPE="drupal10"; PHP_VERSION="8.3" ;; + 11) DDEV_PROJECT_TYPE="drupal11"; PHP_VERSION="8.3" ;; + *) DDEV_PROJECT_TYPE="drupal12"; PHP_VERSION="8.5" ;; + esac + if [ ! -f ".ddev/config.yaml" ]; then - log_setup "Configuring DDEV for Drupal HEAD with PHP 8.5 and docroot=web..." + log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE, PHP $PHP_VERSION)..." update_status "⏳ DDEV config: In progress..." - if ddev config --project-type=drupal12 --php-version=8.5 --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then - log_setup "✓ DDEV configured successfully with web/ as docroot" + if ddev config --project-type="$DDEV_PROJECT_TYPE" --php-version="$PHP_VERSION" --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then + log_setup "✓ DDEV configured (project-type=$DDEV_PROJECT_TYPE php=$PHP_VERSION docroot=web)" update_status "✓ DDEV config: Success" else log_setup "✗ Failed to configure DDEV" @@ -474,7 +504,7 @@ STATUS_HEADER update_status "" update_status "Manual recovery:" update_status " cd $DRUPAL_DIR" - update_status " ddev config --project-type=drupal12 --php-version=8.5 --docroot=web --host-webserver-port=80" + update_status " ddev config --project-type=$DDEV_PROJECT_TYPE --php-version=$PHP_VERSION --docroot=web --host-webserver-port=80" fi # Configure DDEV global settings (omit router) From 123d049437c7ee170ba76ab6a97439e0fb9e22a6 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:26:51 -0700 Subject: [PATCH 09/21] fix: remove --php-version from ddev config, let DDEV pick defaults DDEV sets the appropriate PHP version when given --project-type=drupalXX. Explicitly passing --php-version was causing incorrect PHP versions to be used (e.g. 8.5 for Drupal 10/11 issues). Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 2 +- drupal-core/template.tf | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index 2d76ce0..89f5c22 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -320,7 +320,7 @@

Drupal Issue Picker

-
Auto-detected from the issue. Sets the DDEV project type and PHP version.
+
Auto-detected from the issue. Sets the DDEV project type (drupal10/11/12).
diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 41ac475..f0c8636 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -88,7 +88,7 @@ data "coder_parameter" "issue_branch" { data "coder_parameter" "drupal_version" { name = "drupal_version" display_name = "Drupal Version" - description = "Major Drupal version — sets DDEV project type and PHP version. Match the version of the issue you are working on." + description = "Major Drupal version — sets DDEV project type. Match the version of the issue you are working on." type = "string" default = "12" mutable = true @@ -482,20 +482,20 @@ STATUS_HEADER cd "$DRUPAL_DIR" || exit 1 # Step 2: Configure DDEV (must be done before composer create) - # Derive project type and PHP version from the Drupal major version parameter + # Derive project type from the Drupal major version parameter (let DDEV pick default PHP version) DRUPAL_VERSION="${data.coder_parameter.drupal_version.value}" case "$DRUPAL_VERSION" in - 10) DDEV_PROJECT_TYPE="drupal10"; PHP_VERSION="8.3" ;; - 11) DDEV_PROJECT_TYPE="drupal11"; PHP_VERSION="8.3" ;; - *) DDEV_PROJECT_TYPE="drupal12"; PHP_VERSION="8.5" ;; + 10) DDEV_PROJECT_TYPE="drupal10" ;; + 11) DDEV_PROJECT_TYPE="drupal11" ;; + *) DDEV_PROJECT_TYPE="drupal12" ;; esac if [ ! -f ".ddev/config.yaml" ]; then - log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE, PHP $PHP_VERSION)..." + log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE)..." update_status "⏳ DDEV config: In progress..." - if ddev config --project-type="$DDEV_PROJECT_TYPE" --php-version="$PHP_VERSION" --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then - log_setup "✓ DDEV configured (project-type=$DDEV_PROJECT_TYPE php=$PHP_VERSION docroot=web)" + if ddev config --project-type="$DDEV_PROJECT_TYPE" --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then + log_setup "✓ DDEV configured (project-type=$DDEV_PROJECT_TYPE docroot=web)" update_status "✓ DDEV config: Success" else log_setup "✗ Failed to configure DDEV" @@ -504,7 +504,7 @@ STATUS_HEADER update_status "" update_status "Manual recovery:" update_status " cd $DRUPAL_DIR" - update_status " ddev config --project-type=$DDEV_PROJECT_TYPE --php-version=$PHP_VERSION --docroot=web --host-webserver-port=80" + update_status " ddev config --project-type=$DDEV_PROJECT_TYPE --docroot=web --host-webserver-port=80" fi # Configure DDEV global settings (omit router) From 100db77f09203cca90c1377984eb1be2aae03a9c Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:33:04 -0700 Subject: [PATCH 10/21] style: remove time estimates from install profile options Time estimates are unreliable and will go stale. Removed ~30s/~3-5min hints from all install profile option labels, hints, and descriptions. Co-Authored-By: Claude Sonnet 4.6 --- docs/drupal-issue.html | 14 +++++++------- drupal-core/template.tf | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/drupal-issue.html b/docs/drupal-issue.html index 89f5c22..e85fd45 100644 --- a/docs/drupal-issue.html +++ b/docs/drupal-issue.html @@ -306,11 +306,11 @@

Drupal Issue Picker

-
demo_umami uses a pre-built database for fast startup. Other profiles require a full site install. Issue fork workspaces always do a full install.
+
demo_umami uses a pre-built database snapshot. Issue fork workspaces always run a full site install regardless of profile.
@@ -354,9 +354,9 @@

Drupal Issue Picker

diff --git a/drupal-core/template.tf b/drupal-core/template.tf index f0c8636..17bb080 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -110,21 +110,21 @@ data "coder_parameter" "drupal_version" { data "coder_parameter" "install_profile" { name = "install_profile" display_name = "Install Profile" - description = "demo_umami uses the seed cache for fast startup (~30s). Other profiles require a full site install (~3-5 min)." + description = "Drupal install profile. demo_umami uses a pre-built database snapshot; other profiles run a full site install. Issue fork workspaces always run a full install." type = "string" default = "demo_umami" mutable = true order = 3 option { - name = "demo_umami (fast, ~30s — recommended)" + name = "demo_umami" value = "demo_umami" } option { - name = "minimal (~3-5 min, full install)" + name = "minimal" value = "minimal" } option { - name = "standard (~3-5 min, full install)" + name = "standard" value = "standard" } } From 7f39416c1f1f530ab36fd4da9978b58184370f60 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:37:02 -0700 Subject: [PATCH 11/21] fix: always run ddev config on startup to fix stale host dir state Coder does not clean the bind-mount host directory on workspace delete, so a recreated workspace inherits the old .ddev/config.yaml. The previous guard (if [ ! -f .ddev/config.yaml ]) caused ddev config to be skipped, preserving the wrong project-type/php-version from the prior run. Running ddev config unconditionally ensures the project type always matches the selected drupal_version parameter. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 50 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 17bb080..4bc11ec 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -490,37 +490,33 @@ STATUS_HEADER *) DDEV_PROJECT_TYPE="drupal12" ;; esac - if [ ! -f ".ddev/config.yaml" ]; then - log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE)..." - update_status "⏳ DDEV config: In progress..." + # Always run ddev config to ensure project type is correct (host dir may persist across workspace delete/recreate) + log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE)..." + update_status "⏳ DDEV config: In progress..." - if ddev config --project-type="$DDEV_PROJECT_TYPE" --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then - log_setup "✓ DDEV configured (project-type=$DDEV_PROJECT_TYPE docroot=web)" - update_status "✓ DDEV config: Success" - else - log_setup "✗ Failed to configure DDEV" - log_setup "Check $SETUP_LOG for details" - update_status "✗ DDEV config: Failed" - update_status "" - update_status "Manual recovery:" - update_status " cd $DRUPAL_DIR" - update_status " ddev config --project-type=$DDEV_PROJECT_TYPE --docroot=web --host-webserver-port=80" - fi + if ddev config --project-type="$DDEV_PROJECT_TYPE" --docroot=web --host-webserver-port=80 >> "$SETUP_LOG" 2>&1; then + log_setup "✓ DDEV configured (project-type=$DDEV_PROJECT_TYPE docroot=web)" + update_status "✓ DDEV config: Success" + else + log_setup "✗ Failed to configure DDEV" + log_setup "Check $SETUP_LOG for details" + update_status "✗ DDEV config: Failed" + update_status "" + update_status "Manual recovery:" + update_status " cd $DRUPAL_DIR" + update_status " ddev config --project-type=$DDEV_PROJECT_TYPE --docroot=web --host-webserver-port=80" + fi - # Configure DDEV global settings (omit router) - log_setup "Configuring DDEV global settings..." - update_status "⏳ DDEV global config: In progress..." + # Configure DDEV global settings (omit router) + log_setup "Configuring DDEV global settings..." + update_status "⏳ DDEV global config: In progress..." - if ddev config global --omit-containers=ddev-router >> "$SETUP_LOG" 2>&1; then - log_setup "✓ DDEV global config applied (router omitted)" - update_status "✓ DDEV global config: Success" - else - log_setup "⚠ Warning: Failed to set DDEV global config (non-critical)" - update_status "⚠ DDEV global config: Warning (non-critical)" - fi + if ddev config global --omit-containers=ddev-router >> "$SETUP_LOG" 2>&1; then + log_setup "✓ DDEV global config applied (router omitted)" + update_status "✓ DDEV global config: Success" else - log_setup "✓ DDEV already configured" - update_status "✓ DDEV config: Already present" + log_setup "⚠ Warning: Failed to set DDEV global config (non-critical)" + update_status "⚠ DDEV global config: Warning (non-critical)" fi # Step 3: Start DDEV From 15da239bd58da3183f715f4a406d1c6e390d62b2 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:39:16 -0700 Subject: [PATCH 12/21] fix: rm .ddev/config.yaml before ddev config to get clean defaults ddev config only updates fields explicitly passed as flags; stale fields (e.g. php_version from a previous run) remain unchanged. Removing the config first lets DDEV write a clean file with correct defaults for the selected project type. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 4bc11ec..130edd0 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -490,7 +490,10 @@ STATUS_HEADER *) DDEV_PROJECT_TYPE="drupal12" ;; esac - # Always run ddev config to ensure project type is correct (host dir may persist across workspace delete/recreate) + # Always regenerate .ddev/config.yaml from scratch so DDEV picks its own defaults + # for the project type (e.g. correct PHP version). Preserving an old config.yaml + # would leave stale fields like php_version untouched even when project-type changes. + rm -f .ddev/config.yaml log_setup "Configuring DDEV for Drupal $DRUPAL_VERSION ($DDEV_PROJECT_TYPE)..." update_status "⏳ DDEV config: In progress..." From 86b746f2cfa66827ffcf47533ad2983eb3345bc8 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:52:46 -0700 Subject: [PATCH 13/21] fix: skip seed cache for issue forks; regenerate vendor after branch checkout The seed composer.json requires "drupal/core: dev-main" and vendor is resolved for drupal12/PHP8.5. Both are incompatible with non-main issue branches (e.g. 10.6.x). Changes: - Seed cache path now gated on USING_ISSUE_FORK=false - Issue fork workspaces always do a fresh ddev composer create - After branch checkout: relax "drupal/core: dev-main" constraint to "*", delete composer.lock and vendor/, run ddev composer install to get a clean vendor resolved for the branch's actual PHP requirements Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 48 ++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 130edd0..24dd637 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -579,10 +579,12 @@ STATUS_HEADER fi # Step 4: Set up Drupal core project — use seed cache when available (fast path) + # Issue forks skip the cache: the seed composer.json requires "drupal/core: dev-main" and + # vendor is resolved for PHP 8.5/drupal12, both incompatible with non-main issue branches. if [ -f "composer.json" ] && [ -d "repos/drupal/.git" ]; then log_setup "✓ Drupal core project already present — skipping setup" update_status "✓ Setup: Already present" - elif [ -f "$CACHE_SEED/composer.json" ] && [ -d "$CACHE_SEED/repos/drupal/.git" ]; then + elif [ "$USING_ISSUE_FORK" = "false" ] && [ -f "$CACHE_SEED/composer.json" ] && [ -d "$CACHE_SEED/repos/drupal/.git" ]; then _t=$SECONDS log_setup "Cache hit: seeding project from host cache (fast path)..." update_status "⏳ DDEV setup: Seeding from cache..." @@ -593,31 +595,7 @@ STATUS_HEADER _t=$SECONDS git -C "$DRUPAL_DIR/repos/drupal" fetch --all --prune >> "$SETUP_LOG" 2>&1 || true log_setup " git fetch complete ($((SECONDS - _t))s)" - # Checkout issue fork branch (must happen before composer install so vendor matches branch) - if [ "$USING_ISSUE_FORK" = "true" ]; then - _t=$SECONDS - REPOS_DIR="$DRUPAL_DIR/repos/drupal" - CURRENT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") - if [ -n "$ISSUE_BRANCH" ] && [ "$CURRENT_BRANCH" = "$ISSUE_BRANCH" ]; then - log_setup " ✓ Already on issue branch: $ISSUE_BRANCH" - else - if [ -n "$ISSUE_FORK" ]; then - log_setup " Adding issue fork remote: $ISSUE_FORK" - git -C "$REPOS_DIR" remote remove issue 2>/dev/null || true - git -C "$REPOS_DIR" remote add issue "https://git.drupalcode.org/issue/$ISSUE_FORK.git" - git -C "$REPOS_DIR" fetch issue 2>&1 | tee -a "$SETUP_LOG" || true - fi - if [ -n "$ISSUE_BRANCH" ]; then - log_setup " Checking out issue branch: $ISSUE_BRANCH" - git -C "$REPOS_DIR" checkout -b "$ISSUE_BRANCH" "issue/$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ - git -C "$REPOS_DIR" checkout "$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ - log_setup " ✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" - fi - fi - log_setup " issue fork setup complete ($((SECONDS - _t))s)" - ISSUE_FORK_CHECKOUT_DONE=true - fi - # Ensure vendor matches current composer.lock (no-op when lock is unchanged; reflects issue branch if checked out) + # Sync vendor with the (unchanged main-branch) lock file _t=$SECONDS ddev composer install >> "$SETUP_LOG" 2>&1 || true log_setup " composer install complete ($((SECONDS - _t))s)" @@ -655,8 +633,12 @@ STATUS_HEADER # Steps 5-7: run whenever project files are present — inner checks handle idempotency if [ -f "composer.json" ] && [ -d "repos/drupal" ]; then - # Step 4.5: Issue fork checkout — runs if not already done in the cache-seed fast path - # Must happen before Drush install so that vendor reflects the correct branch + # Step 4.5: Issue fork checkout + composer reinstall + # Runs after fresh composer create (ISSUE_FORK_CHECKOUT_DONE=false). + # The seed cache is skipped for issue forks, so we start from a freshly-created + # drupal12/main project. After checking out the issue branch we must regenerate + # vendor: the project composer.json hardcodes "drupal/core: dev-main" and vendor + # is resolved for drupal12/PHP8.5 — both wrong for a non-main issue branch. if [ "$USING_ISSUE_FORK" = "true" ] && [ "$ISSUE_FORK_CHECKOUT_DONE" = "false" ]; then REPOS_DIR="$DRUPAL_DIR/repos/drupal" if [ -d "$REPOS_DIR/.git" ]; then @@ -677,9 +659,15 @@ STATUS_HEADER log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" fi fi - # Re-run composer install so vendor reflects the checked-out branch - log_setup "Running composer install for issue branch..." + # Update drupal/core constraint from "dev-main" to "*" so Composer accepts + # any branch version from the path repository, then do a clean vendor install. + log_setup "Updating drupal/core constraint and regenerating vendor for issue branch..." + sed -i 's|"drupal/core": "dev-main"|"drupal/core": "*"|' composer.json 2>/dev/null || true + rm -f composer.lock + rm -rf vendor/ + _t=$SECONDS ddev composer install >> "$SETUP_LOG" 2>&1 || true + log_setup " composer install complete ($((SECONDS - _t))s)" fi fi From 5dc93c2c55065eb18973cf80f859714ab313ac47 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 09:54:34 -0700 Subject: [PATCH 14/21] perf: supplement git objects from seed cache for issue fork workspaces After ddev composer create clones repos/drupal fresh, copy git objects from the seed cache into the new clone. This means git fetch for the issue branch only downloads the delta rather than all objects from scratch. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 24dd637..e374271 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -621,6 +621,12 @@ STATUS_HEADER log_setup "✓ Drupal core development project created ($((SECONDS - _t))s)" update_status "✓ DDEV composer create: Success" DRUPAL_SETUP_NEEDED=true + # Supplement git objects from seed cache so issue-branch fetch only downloads the delta + if [ -d "$CACHE_SEED/repos/drupal/.git/objects" ]; then + log_setup "Supplementing git objects from seed cache..." + rsync -a "$CACHE_SEED/repos/drupal/.git/objects/" "$DRUPAL_DIR/repos/drupal/.git/objects/" >> "$SETUP_LOG" 2>&1 || true + log_setup " git objects supplement complete" + fi else log_setup "✗ Failed to create Drupal core development project ($((SECONDS - _t))s)" log_setup "Check $SETUP_LOG for details" From 4af8555af9a9717da905ca9795bf976f950395a7 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 10:03:46 -0700 Subject: [PATCH 15/21] fix: include drush in initial composer install for issue fork workspaces A separate "ddev composer require drush/drush" after the initial install triggers a new Composer resolution that fails because it can't reconcile the issue-branch version (e.g. dev-3525391-foo) reported by the path repos with the constraints in other path packages (drupal/core-recommended requires drupal/core 11.x-dev). Fix: add drush/drush to composer.json alongside the drupal/core constraint relaxation so the single ddev composer install resolves everything in one pass where the ComposerCoreVersionsLeniency plugin handles the aliasing. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index e374271..0679ac3 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -665,14 +665,18 @@ STATUS_HEADER log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" fi fi - # Update drupal/core constraint from "dev-main" to "*" so Composer accepts - # any branch version from the path repository, then do a clean vendor install. - log_setup "Updating drupal/core constraint and regenerating vendor for issue branch..." - sed -i 's|"drupal/core": "dev-main"|"drupal/core": "*"|' composer.json 2>/dev/null || true + # Update composer.json for issue branch compatibility: + # - Loosen "drupal/core: dev-main" to "*" — accepts any path-repo branch version + # - Add drush/drush upfront so it resolves in the same composer install pass + # (a separate "composer require drush/drush" triggers a new resolution that + # conflicts with the issue-branch version reported by the path repos) + log_setup "Updating composer.json and regenerating vendor for issue branch..." + python3 -c "import json; d=json.load(open('composer.json')); d['require'].update({'drupal/core':'*','drush/drush':'*'}); json.dump(d,open('composer.json','w'),indent=4)" 2>/dev/null || \ + sed -i 's|"drupal/core": "dev-main"|"drupal/core": "*"|' composer.json rm -f composer.lock rm -rf vendor/ _t=$SECONDS - ddev composer install >> "$SETUP_LOG" 2>&1 || true + ddev composer install 2>&1 | tee -a "$SETUP_LOG" || true log_setup " composer install complete ($((SECONDS - _t))s)" fi fi From 867d21e86937e5ff9e3168a4ecf238d02d093cd0 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 11:06:02 -0700 Subject: [PATCH 16/21] =?UTF-8?q?fix:=20clean=20up=20issue=20fork=20setup?= =?UTF-8?q?=20=E2=80=94=20remove=20wrong=20workarounds,=20add=20phpunit=20?= =?UTF-8?q?support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove --ignore-platform-reqs from ddev composer create and drush require - Remove git-checkout-main trick from Step 5 drush install - Remove pre-checkout drush install (drush added via Step 5 after checkout) - Remove || true from ddev composer install (cache seed path) to surface errors - Add Step 4.9: restore repos/drupal/vendor symlink if missing (created by joachim-n post-install scripts, can be absent after failed previous attempt) - Add Step 6.6: copy phpunit-ddev.xml → phpunit.xml and set BROWSERTEST_OUTPUT_BASE_URL so users can run core tests with ddev exec vendor/bin/phpunit immediately - Use jq instead of python3 for composer.json modifications - Disable platform-check in composer.json after issue branch checkout (jq) Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 0679ac3..8589565 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -597,7 +597,7 @@ STATUS_HEADER log_setup " git fetch complete ($((SECONDS - _t))s)" # Sync vendor with the (unchanged main-branch) lock file _t=$SECONDS - ddev composer install >> "$SETUP_LOG" 2>&1 || true + ddev composer install >> "$SETUP_LOG" 2>&1 log_setup " composer install complete ($((SECONDS - _t))s)" log_setup "✓ Cache seed complete ($((SECONDS - SETUP_START))s total so far)" update_status "✓ DDEV composer create: Seeded from cache" @@ -605,7 +605,7 @@ STATUS_HEADER else log_setup "✗ Failed to seed from cache ($((SECONDS - _t))s), falling back to full setup..." update_status "⚠ Cache seed failed, running full setup..." - ddev composer create joachim-n/drupal-core-development-project --no-interaction >> "$SETUP_LOG" 2>&1 || true + ddev composer create joachim-n/drupal-core-development-project --no-interaction >> "$SETUP_LOG" 2>&1 DRUPAL_SETUP_NEEDED=true fi else @@ -639,12 +639,11 @@ STATUS_HEADER # Steps 5-7: run whenever project files are present — inner checks handle idempotency if [ -f "composer.json" ] && [ -d "repos/drupal" ]; then - # Step 4.5: Issue fork checkout + composer reinstall + # Step 4.5: Issue fork branch checkout + autoload regeneration # Runs after fresh composer create (ISSUE_FORK_CHECKOUT_DONE=false). - # The seed cache is skipped for issue forks, so we start from a freshly-created - # drupal12/main project. After checking out the issue branch we must regenerate - # vendor: the project composer.json hardcodes "drupal/core: dev-main" and vendor - # is resolved for drupal12/PHP8.5 — both wrong for a non-main issue branch. + # Drush was already added above (before this step, while on main branch). + # After checkout, vendor/drupal/core path-repo symlink auto-reflects the new branch. + # We disable platform-check and regenerate the autoload classmap — no vendor rebuild. if [ "$USING_ISSUE_FORK" = "true" ] && [ "$ISSUE_FORK_CHECKOUT_DONE" = "false" ]; then REPOS_DIR="$DRUPAL_DIR/repos/drupal" if [ -d "$REPOS_DIR/.git" ]; then @@ -665,29 +664,35 @@ STATUS_HEADER log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" fi fi - # Update composer.json for issue branch compatibility: - # - Loosen "drupal/core: dev-main" to "*" — accepts any path-repo branch version - # - Add drush/drush upfront so it resolves in the same composer install pass - # (a separate "composer require drush/drush" triggers a new resolution that - # conflicts with the issue-branch version reported by the path repos) - log_setup "Updating composer.json and regenerating vendor for issue branch..." - python3 -c "import json; d=json.load(open('composer.json')); d['require'].update({'drupal/core':'*','drush/drush':'*'}); json.dump(d,open('composer.json','w'),indent=4)" 2>/dev/null || \ - sed -i 's|"drupal/core": "dev-main"|"drupal/core": "*"|' composer.json - rm -f composer.lock - rm -rf vendor/ + # After branch checkout, vendor/drupal/core is a path-repo symlink pointing to + # repos/drupal/core — it already reflects the checked-out branch automatically. + # No vendor rebuild is needed. We just: + # 1. Disable platform-check (lock was resolved for a different PHP/Drupal version) + # 2. Regenerate the autoload classmap so new branch classes are found + log_setup "Regenerating autoload for issue branch (path-repo symlinks already reflect branch)..." + jq '.config["platform-check"] = false' composer.json > composer.json.tmp && mv composer.json.tmp composer.json _t=$SECONDS - ddev composer install 2>&1 | tee -a "$SETUP_LOG" || true - log_setup " composer install complete ($((SECONDS - _t))s)" + ddev composer dump-autoload --no-scripts 2>&1 | tee -a "$SETUP_LOG" + log_setup " autoload regenerated ($((SECONDS - _t))s)" fi fi - # Step 5: Ensure Drush is available (skip if already present from cache) + # Step 4.9: Restore repos/drupal/vendor symlink if missing. + # This symlink (repos/drupal/vendor -> ../../vendor) is created by joachim-n's + # post-install scripts. It can be absent when a previous workspace attempt failed + # before composer install completed. + if [ -d "repos/drupal/.git" ] && [ ! -e "repos/drupal/vendor" ]; then + log_setup "Restoring missing repos/drupal/vendor symlink..." + ln -s ../../vendor repos/drupal/vendor && log_setup " symlink restored" || log_setup " symlink restore failed (non-critical)" + fi + + # Step 5: Ensure Drush is available (skip if already present from cache or pre-checkout install) if [ -f "vendor/bin/drush" ]; then log_setup "✓ Drush already present" update_status "✓ Drush install: Already present" else _t=$SECONDS - log_setup "Adding Drush to composer require..." + log_setup "Adding Drush..." update_status "⏳ Drush install: In progress..." if ddev composer require drush/drush >> "$SETUP_LOG" 2>&1; then @@ -764,6 +769,18 @@ STATUS_HEADER log_setup "Running cache rebuild..." ddev drush cr >> "$SETUP_LOG" 2>&1 || true + # Step 6.6: Set up phpunit.xml for running core tests + if [ ! -f "phpunit.xml" ] && [ -f "phpunit-ddev.xml" ]; then + cp phpunit-ddev.xml phpunit.xml + # Replace PROJECT_NAME.ddev.site placeholder with actual workspace URL + if [ -n "$VSCODE_PROXY_URI" ] && [ -n "$CODER_WORKSPACE_OWNER_NAME" ]; then + CODER_DOMAIN=$(echo "$VSCODE_PROXY_URI" | sed -E 's|https?://[^.]+\.(.+?)(/.*)?$|\1|') + SITE_URL="https://80--$${CODER_WORKSPACE_NAME}--$${CODER_WORKSPACE_OWNER_NAME}.$${CODER_DOMAIN}" + sed -i "s|PROJECT_NAME\.ddev\.site|$${SITE_URL#https://}|" phpunit.xml + fi + log_setup "✓ phpunit.xml configured (run tests with: ddev exec vendor/bin/phpunit web/core/tests/...)" + fi + # Step 7: Install custom DDEV launch command mkdir -p ~/.ddev/commands/host cat > ~/.ddev/commands/host/launch << 'LAUNCH_EOF' From 805e0d9c6bb00c6179cd5913aa54e8d79fa712cb Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 12:41:52 -0700 Subject: [PATCH 17/21] fix: correct composer install for non-main issue branches (drupal 10/11/12) Issue forks based on non-main branches (10.5.x, 11.3.x, etc.) require three categories of fixes to ddev composer install: 1. Root composer.json has "drupal/core: dev-main" which conflicts with issue branch path repos. Change to "*" for all issue forks. 2. Drupal 10: drupal/core-dev 10.x has an irreconcilable conflict between composer/composer 2.9.x and justinrainbow/json-schema versions. Fix by pinning composer/composer to ~2.8.1 and setting audit.block-insecure=false (per drupal.org/project/drupal/issues/3557585). 3. Drupal 11/12: drupal/core-recommended requires a specific drupal/core version (e.g. 11.x-dev) but the canonical path repo offers dev- without that alias. Fix by adding a branch-alias derived from core-recommended. Also add RecipeUnpack as a path repo (required by drupal/drupal self.version but missing from joachim-n's list). The fresh create path for issue forks now uses --no-install so that the issue branch is checked out BEFORE ddev composer install runs, ensuring vendor is resolved for the correct Drupal/PHP version. See https://github.com/joachim-n/drupal-core-development-project/issues/41 Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 129 +++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 36 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 8589565..0e4c1ee 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -610,40 +610,54 @@ STATUS_HEADER fi else _t=$SECONDS - log_setup "No cache available, running full composer create (this takes 5-10 minutes)..." - log_setup "This creates a proper dev environment with:" - log_setup " - Drupal core git clone at repos/drupal/" - log_setup " - Web root at web/" - log_setup " - Composer dependency management" - update_status "⏳ DDEV composer create: In progress (this takes 5-10 minutes)..." - - if ddev composer create joachim-n/drupal-core-development-project --no-interaction >> "$SETUP_LOG" 2>&1; then - log_setup "✓ Drupal core development project created ($((SECONDS - _t))s)" - update_status "✓ DDEV composer create: Success" - DRUPAL_SETUP_NEEDED=true - # Supplement git objects from seed cache so issue-branch fetch only downloads the delta - if [ -d "$CACHE_SEED/repos/drupal/.git/objects" ]; then - log_setup "Supplementing git objects from seed cache..." - rsync -a "$CACHE_SEED/repos/drupal/.git/objects/" "$DRUPAL_DIR/repos/drupal/.git/objects/" >> "$SETUP_LOG" 2>&1 || true - log_setup " git objects supplement complete" + if [ "$USING_ISSUE_FORK" = "true" ]; then + # Issue fork: create project structure WITHOUT installing dependencies. + # We must checkout the issue branch before composer install so that vendor + # is resolved for the correct branch, not for main/drupal12. + log_setup "Issue fork: creating project structure (dependencies installed after branch checkout)..." + update_status "⏳ DDEV composer create-project: In progress..." + if ddev composer create-project --no-install --no-interaction joachim-n/drupal-core-development-project . >> "$SETUP_LOG" 2>&1; then + log_setup "✓ Project structure created ($((SECONDS - _t))s)" + update_status "✓ DDEV composer create-project: Success" + DRUPAL_SETUP_NEEDED=true + # Supplement git objects from seed cache so issue-branch fetch only downloads the delta + if [ -d "$CACHE_SEED/repos/drupal/.git/objects" ]; then + log_setup "Supplementing git objects from seed cache..." + rsync -a "$CACHE_SEED/repos/drupal/.git/objects/" "$DRUPAL_DIR/repos/drupal/.git/objects/" >> "$SETUP_LOG" 2>&1 || true + log_setup " git objects supplement complete" + fi + else + log_setup "✗ Failed to create project structure ($((SECONDS - _t))s)" + log_setup "Check $SETUP_LOG for details" + update_status "✗ DDEV composer create-project: Failed" + update_status "" + update_status "Manual recovery:" + update_status " cd $DRUPAL_DIR && ddev composer create-project --no-install joachim-n/drupal-core-development-project ." fi else - log_setup "✗ Failed to create Drupal core development project ($((SECONDS - _t))s)" - log_setup "Check $SETUP_LOG for details" - update_status "✗ DDEV composer create: Failed" - update_status "" - update_status "Manual recovery:" - update_status " cd $DRUPAL_DIR && ddev composer create joachim-n/drupal-core-development-project" + log_setup "No cache available, running full composer create (this takes 5-10 minutes)..." + update_status "⏳ DDEV composer create: In progress (this takes 5-10 minutes)..." + if ddev composer create joachim-n/drupal-core-development-project --no-interaction >> "$SETUP_LOG" 2>&1; then + log_setup "✓ Drupal core development project created ($((SECONDS - _t))s)" + update_status "✓ DDEV composer create: Success" + DRUPAL_SETUP_NEEDED=true + else + log_setup "✗ Failed to create Drupal core development project ($((SECONDS - _t))s)" + log_setup "Check $SETUP_LOG for details" + update_status "✗ DDEV composer create: Failed" + update_status "" + update_status "Manual recovery:" + update_status " cd $DRUPAL_DIR && ddev composer create joachim-n/drupal-core-development-project" + fi fi fi # Steps 5-7: run whenever project files are present — inner checks handle idempotency if [ -f "composer.json" ] && [ -d "repos/drupal" ]; then - # Step 4.5: Issue fork branch checkout + autoload regeneration - # Runs after fresh composer create (ISSUE_FORK_CHECKOUT_DONE=false). - # Drush was already added above (before this step, while on main branch). - # After checkout, vendor/drupal/core path-repo symlink auto-reflects the new branch. - # We disable platform-check and regenerate the autoload classmap — no vendor rebuild. + # Step 4.5: Issue fork — checkout branch, fix composer.json, run composer install. + # For issue forks the project was created with --no-install so no vendor exists yet. + # We must checkout the issue branch BEFORE composer install so that vendor is + # resolved for the correct Drupal version, not for main/drupal12. if [ "$USING_ISSUE_FORK" = "true" ] && [ "$ISSUE_FORK_CHECKOUT_DONE" = "false" ]; then REPOS_DIR="$DRUPAL_DIR/repos/drupal" if [ -d "$REPOS_DIR/.git" ]; then @@ -664,16 +678,59 @@ STATUS_HEADER log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" fi fi - # After branch checkout, vendor/drupal/core is a path-repo symlink pointing to - # repos/drupal/core — it already reflects the checked-out branch automatically. - # No vendor rebuild is needed. We just: - # 1. Disable platform-check (lock was resolved for a different PHP/Drupal version) - # 2. Regenerate the autoload classmap so new branch classes are found - log_setup "Regenerating autoload for issue branch (path-repo symlinks already reflect branch)..." - jq '.config["platform-check"] = false' composer.json > composer.json.tmp && mv composer.json.tmp composer.json + + # Apply composer.json fixes so ddev composer install resolves correctly. + # Root composer.json hardcodes "drupal/core: dev-main" which conflicts with + # non-main issue branches in the canonical path repos. + log_setup "Applying composer.json fixes for Drupal $DRUPAL_VERSION issue branch..." + # Fix 1 (ALL versions): relax drupal/core constraint so path repo version is accepted. + jq '.require["drupal/core"] = "*"' composer.json > composer.json.tmp && mv composer.json.tmp composer.json + + if [ "$DRUPAL_VERSION" = "10" ]; then + # Fix 2 (drupal 10): drupal/core-dev 10.x requires composer/composer ^2.8.1 AND + # justinrainbow/json-schema ^5.2, but 2.9.x requires ^6.5.1 — conflict. + # Pin to 2.8.x and disable the security advisory block (it's a dev tool, not a + # web vulnerability — see https://www.drupal.org/project/drupal/issues/3557585). + jq '.require["composer/composer"] = "~2.8.1" | .config.audit["block-insecure"] = false' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Applied drupal10 fix: pinned composer/composer to ~2.8.1" + else + # Fix 3 (drupal 11/12): drupal/core-recommended requires a specific drupal/core + # version (e.g. 11.x-dev) but the path repo offers dev- without that alias. + # Read the required version from core-recommended and add it as a branch-alias. + CHECKED_OUT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") + TARGET_ALIAS=$(jq -r '.require["drupal/core"]' \ + "$REPOS_DIR/composer/Metapackage/CoreRecommended/composer.json" 2>/dev/null || echo "") + if [ -n "$TARGET_ALIAS" ] && [ -n "$CHECKED_OUT_BRANCH" ]; then + jq --arg branch "dev-$CHECKED_OUT_BRANCH" --arg alias "$TARGET_ALIAS" \ + '.extra["branch-alias"] = {($branch): $alias}' \ + "$REPOS_DIR/core/composer.json" > /tmp/core-composer.tmp \ + && mv /tmp/core-composer.tmp "$REPOS_DIR/core/composer.json" + log_setup " Added branch-alias: dev-$CHECKED_OUT_BRANCH → $TARGET_ALIAS" + fi + # drupal/drupal on 11.x+ requires drupal/core-recipe-unpack self.version but + # it is not in the joachim-n path repos list. Add it if the directory exists. + if [ -d "$REPOS_DIR/composer/Plugin/RecipeUnpack" ]; then + jq '.repositories += [{"type":"path","url":"repos/drupal/composer/Plugin/RecipeUnpack"}]' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Added RecipeUnpack path repo" + fi + fi + + # Now install dependencies for the checked-out issue branch. + log_setup "Running composer install for issue branch..." + update_status "⏳ Composer install for issue branch: In progress..." _t=$SECONDS - ddev composer dump-autoload --no-scripts 2>&1 | tee -a "$SETUP_LOG" - log_setup " autoload regenerated ($((SECONDS - _t))s)" + if ddev composer install 2>&1 | tee -a "$SETUP_LOG"; then + log_setup "✓ Composer install complete ($((SECONDS - _t))s)" + update_status "✓ Composer install for issue branch: Success" + else + log_setup "✗ Composer install failed ($((SECONDS - _t))s)" + update_status "✗ Composer install for issue branch: Failed" + update_status "" + update_status "Manual recovery:" + update_status " cd $DRUPAL_DIR && ddev composer install" + fi fi fi From 0ab1c08e352e6c8fc664bfcc1671c0ab29a8f438 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 14:21:07 -0700 Subject: [PATCH 18/21] fix: resolve composer deps for issue fork branches without dirtying repos/drupal - Use composer inline aliases in root composer.json instead of writing branch-alias into repos/drupal/core/composer.json (which dirtied the git working tree). Declares "drupal/core: dev- as " so the path repo satisfies CoreRecommended's version constraint. - Pin drupal/drupal to "dev-" to prevent Packagist fallback pulling remote packages instead of path repos. - Detect actual Drupal major from CoreRecommended on disk (not user-selected DRUPAL_VERSION) so 10.x-labeled issues with 11.x code are handled correctly. - Fix issue fork git fetch URL: use drupal- format which is publicly accessible without credentials; plain .git requires auth. - Fix pipe exit code masking: use PIPESTATUS[0] after composer update pipe so failures are correctly detected even when tee succeeds. - Fail fast and skip remaining steps on branch checkout or composer failure: set SETUP_FAILED=true and gate Steps 5/6 (drush install, site install) on it. - Add make push-template-drupal-core to CLAUDE.md; prefer jq over python. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 7 ++ drupal-core/template.tf | 140 +++++++++++++++++++++++++++------------- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2ad4c55..6758596 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,10 +15,17 @@ This project provides a Coder v2+ template for DDEV-based development environmen **Note:** User-facing documentation has moved to `/docs/`. This file focuses on developer/contributor guidance. +## Tool Preferences + +- Use `jq` (not `python3 -m json.tool`) for JSON pretty-printing and querying + ## Essential Commands ### Template Management ```bash +# Deploy or update the drupal-core template (no image build needed) +make push-template-drupal-core + # Deploy or update template (example: user-defined-web) coder templates push --directory user-defined-web user-defined-web --yes diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 0e4c1ee..29d488e 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -573,6 +573,7 @@ STATUS_HEADER ISSUE_BRANCH="${data.coder_parameter.issue_branch.value}" INSTALL_PROFILE="${data.coder_parameter.install_profile.value}" USING_ISSUE_FORK=false + SETUP_FAILED=false if [ -n "$ISSUE_FORK" ] || [ -n "$ISSUE_BRANCH" ]; then USING_ISSUE_FORK=true log_setup "Issue fork mode: ISSUE_FORK=$ISSUE_FORK ISSUE_BRANCH=$ISSUE_BRANCH INSTALL_PROFILE=$INSTALL_PROFILE" @@ -666,71 +667,111 @@ STATUS_HEADER log_setup "✓ Already on issue branch: $ISSUE_BRANCH" else if [ -n "$ISSUE_FORK" ]; then - log_setup "Adding issue fork remote: $ISSUE_FORK" + log_setup "Adding issue fork remote and fetching: $ISSUE_FORK" git -C "$REPOS_DIR" remote remove issue 2>/dev/null || true - git -C "$REPOS_DIR" remote add issue "https://git.drupalcode.org/issue/$ISSUE_FORK.git" - git -C "$REPOS_DIR" fetch issue 2>&1 | tee -a "$SETUP_LOG" || true + git -C "$REPOS_DIR" remote add issue "https://git.drupalcode.org/issue/drupal-$ISSUE_FORK.git" + if git -C "$REPOS_DIR" fetch issue >> "$SETUP_LOG" 2>&1; then + log_setup " ✓ Fetched from issue remote" + else + log_setup "✗ Failed to fetch from issue remote $ISSUE_FORK — aborting setup" + SETUP_FAILED=true + fi fi - if [ -n "$ISSUE_BRANCH" ]; then + if [ "$SETUP_FAILED" != "true" ] && [ -n "$ISSUE_BRANCH" ]; then log_setup "Checking out issue branch: $ISSUE_BRANCH" - git -C "$REPOS_DIR" checkout -b "$ISSUE_BRANCH" "issue/$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ - git -C "$REPOS_DIR" checkout "$ISSUE_BRANCH" 2>&1 | tee -a "$SETUP_LOG" || \ - log_setup "✗ Failed to check out branch $ISSUE_BRANCH (continuing anyway)" + if git -C "$REPOS_DIR" checkout -b "$ISSUE_BRANCH" "issue/$ISSUE_BRANCH" >> "$SETUP_LOG" 2>&1 || \ + git -C "$REPOS_DIR" checkout "$ISSUE_BRANCH" >> "$SETUP_LOG" 2>&1; then + log_setup " ✓ Checked out branch: $ISSUE_BRANCH" + else + log_setup "✗ Failed to check out branch $ISSUE_BRANCH — aborting setup" + SETUP_FAILED=true + fi fi fi # Apply composer.json fixes so ddev composer install resolves correctly. # Root composer.json hardcodes "drupal/core: dev-main" which conflicts with # non-main issue branches in the canonical path repos. + if [ "$SETUP_FAILED" = "true" ]; then + log_setup "✗ Skipping composer.json fixes due to branch checkout failure" + else log_setup "Applying composer.json fixes for Drupal $DRUPAL_VERSION issue branch..." - # Fix 1 (ALL versions): relax drupal/core constraint so path repo version is accepted. - jq '.require["drupal/core"] = "*"' composer.json > composer.json.tmp && mv composer.json.tmp composer.json - - if [ "$DRUPAL_VERSION" = "10" ]; then - # Fix 2 (drupal 10): drupal/core-dev 10.x requires composer/composer ^2.8.1 AND - # justinrainbow/json-schema ^5.2, but 2.9.x requires ^6.5.1 — conflict. - # Pin to 2.8.x and disable the security advisory block (it's a dev tool, not a - # web vulnerability — see https://www.drupal.org/project/drupal/issues/3557585). + # Detect actual Drupal major version from CoreRecommended's constraint on disk + # (e.g. "10.5.x-dev" → "10", "11.x-dev" → "11") rather than trusting the + # user-selected DRUPAL_VERSION — users sometimes select the wrong version. + CHECKED_OUT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") + TARGET_ALIAS=$(jq -r '.require["drupal/core"]' \ + "$REPOS_DIR/composer/Metapackage/CoreRecommended/composer.json" 2>/dev/null || echo "") + ACTUAL_DRUPAL_MAJOR=$(echo "$TARGET_ALIAS" | grep -oE '^[0-9]+' || echo "$DRUPAL_VERSION") + if [ -n "$TARGET_ALIAS" ] && [ -n "$CHECKED_OUT_BRANCH" ]; then + log_setup " Detected Drupal $ACTUAL_DRUPAL_MAJOR.x (CoreRecommended requires $TARGET_ALIAS)" + if [ "$ACTUAL_DRUPAL_MAJOR" != "$DRUPAL_VERSION" ]; then + log_setup " ⚠ Drupal version mismatch: user selected $DRUPAL_VERSION but branch is actually $ACTUAL_DRUPAL_MAJOR.x" + fi + else + log_setup " ⚠ Could not detect Drupal version (CHECKED_OUT_BRANCH='$CHECKED_OUT_BRANCH' TARGET_ALIAS='$TARGET_ALIAS')" + ACTUAL_DRUPAL_MAJOR="$DRUPAL_VERSION" + fi + + # Fix 1 (ALL versions): set drupal/core inline alias in root composer.json so the + # path repo branch (dev-) satisfies CoreRecommended's version constraint. + # Inline aliases are declared in the *requiring* package — no files in repos/drupal touched. + jq --arg val "dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" \ + '.require["drupal/core"] = $val' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Set drupal/core inline alias: dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" + + # Fix 2 (ALL versions): pin drupal/drupal to the path repo version so Composer + # cannot fall back to Packagist's drupal/drupal (whose self.version requirements + # would pull in remote packages instead of path repos). + jq --arg branch "dev-$CHECKED_OUT_BRANCH" \ + '.require["drupal/drupal"] = $branch' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Pinned drupal/drupal to path repo: dev-$CHECKED_OUT_BRANCH" + + # Fix 3 (actual drupal 10 only): drupal/core-dev 10.x requires composer/composer ^2.8.1 + # AND justinrainbow/json-schema ^5.2, but composer 2.9.x requires ^6.5.1 — conflict. + # Pin to 2.8.x and disable the security advisory block. + # We use ACTUAL_DRUPAL_MAJOR (not DRUPAL_VERSION) so a mis-selected version doesn't + # apply the pin to a 11/12 branch where it adds an unnecessary constraint. + # See https://www.drupal.org/project/drupal/issues/3557585 + if [ "$ACTUAL_DRUPAL_MAJOR" = "10" ]; then jq '.require["composer/composer"] = "~2.8.1" | .config.audit["block-insecure"] = false' \ composer.json > composer.json.tmp && mv composer.json.tmp composer.json log_setup " Applied drupal10 fix: pinned composer/composer to ~2.8.1" - else - # Fix 3 (drupal 11/12): drupal/core-recommended requires a specific drupal/core - # version (e.g. 11.x-dev) but the path repo offers dev- without that alias. - # Read the required version from core-recommended and add it as a branch-alias. - CHECKED_OUT_BRANCH=$(git -C "$REPOS_DIR" branch --show-current 2>/dev/null || echo "") - TARGET_ALIAS=$(jq -r '.require["drupal/core"]' \ - "$REPOS_DIR/composer/Metapackage/CoreRecommended/composer.json" 2>/dev/null || echo "") - if [ -n "$TARGET_ALIAS" ] && [ -n "$CHECKED_OUT_BRANCH" ]; then - jq --arg branch "dev-$CHECKED_OUT_BRANCH" --arg alias "$TARGET_ALIAS" \ - '.extra["branch-alias"] = {($branch): $alias}' \ - "$REPOS_DIR/core/composer.json" > /tmp/core-composer.tmp \ - && mv /tmp/core-composer.tmp "$REPOS_DIR/core/composer.json" - log_setup " Added branch-alias: dev-$CHECKED_OUT_BRANCH → $TARGET_ALIAS" - fi - # drupal/drupal on 11.x+ requires drupal/core-recipe-unpack self.version but - # it is not in the joachim-n path repos list. Add it if the directory exists. - if [ -d "$REPOS_DIR/composer/Plugin/RecipeUnpack" ]; then - jq '.repositories += [{"type":"path","url":"repos/drupal/composer/Plugin/RecipeUnpack"}]' \ - composer.json > composer.json.tmp && mv composer.json.tmp composer.json - log_setup " Added RecipeUnpack path repo" - fi fi - # Now install dependencies for the checked-out issue branch. - log_setup "Running composer install for issue branch..." - update_status "⏳ Composer install for issue branch: In progress..." + # Fix 4 (ALL versions if directory exists): drupal/drupal on 11.x+ requires + # drupal/core-recipe-unpack at self.version. It is not in the joachim-n path repos + # list, so we add it. Gated on directory existence so it is safe on 10.x branches + # that don't have it. MUST be universal — not gated on DRUPAL_VERSION — because a + # user may select "10" while the actual issue branch is 11.x code. + if [ -d "$REPOS_DIR/composer/Plugin/RecipeUnpack" ]; then + jq '.repositories += [{"type":"path","url":"repos/drupal/composer/Plugin/RecipeUnpack"}]' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Added RecipeUnpack path repo" + fi + + # Now resolve dependencies for the checked-out issue branch. + # Use 'update -W' (not 'install') so composer re-solves the full dependency graph + # with the new composer.json constraints rather than trying to honour a stale lock file. + log_setup "Running composer update -W for issue branch..." + update_status "⏳ Composer update for issue branch: In progress..." _t=$SECONDS - if ddev composer install 2>&1 | tee -a "$SETUP_LOG"; then - log_setup "✓ Composer install complete ($((SECONDS - _t))s)" - update_status "✓ Composer install for issue branch: Success" + ddev composer update -W 2>&1 | tee -a "$SETUP_LOG" + _composer_exit=$${PIPESTATUS[0]} + if [ "$_composer_exit" = "0" ]; then + log_setup "✓ Composer update complete ($((SECONDS - _t))s)" + update_status "✓ Composer update for issue branch: Success" else - log_setup "✗ Composer install failed ($((SECONDS - _t))s)" - update_status "✗ Composer install for issue branch: Failed" + log_setup "✗ Composer update failed (exit $_composer_exit, $((SECONDS - _t))s) — skipping remaining setup" + update_status "✗ Composer update for issue branch: Failed" update_status "" update_status "Manual recovery:" - update_status " cd $DRUPAL_DIR && ddev composer install" + update_status " cd $DRUPAL_DIR && ddev composer update -W" + SETUP_FAILED=true fi + fi # end SETUP_FAILED (branch checkout) guard fi fi @@ -743,6 +784,12 @@ STATUS_HEADER ln -s ../../vendor repos/drupal/vendor && log_setup " symlink restored" || log_setup " symlink restore failed (non-critical)" fi + # Steps 5 and 6 are skipped if an earlier step (e.g. composer update) failed. + if [ "$SETUP_FAILED" = "true" ]; then + log_setup "⚠ Skipping Drush and Drupal install due to earlier failure" + update_status "⚠ Setup incomplete — see drupal-setup.log for details" + else + # Step 5: Ensure Drush is available (skip if already present from cache or pre-checkout install) if [ -f "vendor/bin/drush" ]; then log_setup "✓ Drush already present" @@ -752,7 +799,7 @@ STATUS_HEADER log_setup "Adding Drush..." update_status "⏳ Drush install: In progress..." - if ddev composer require drush/drush >> "$SETUP_LOG" 2>&1; then + if ddev composer require drush/drush -W >> "$SETUP_LOG" 2>&1; then log_setup "✓ Drush configured ($((SECONDS - _t))s)" update_status "✓ Drush install: Success" else @@ -821,6 +868,7 @@ STATUS_HEADER update_status " ddev drush si -y $INSTALL_PROFILE --account-pass=admin" fi fi + fi # end SETUP_FAILED guard # Step 6.5: Cache rebuild — ensures a clean state after any setup path log_setup "Running cache rebuild..." From b3480c99ebca27d4b21b2b23e52c0706ddb420e1 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 15:55:23 -0700 Subject: [PATCH 19/21] fix: correct 12.x and 11.2.x issue fork composer resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 12.x/main: inline alias approach fails because Packagist defines 12.x-dev=dev-main for all core sub-packages, and drupal/core's self.version cascades 12.x-dev to sub-packages. Fix: temporarily add branch-alias to repos/drupal/composer.json and repos/drupal/core/composer.json, change root drupal/core to "12.x-dev", run composer update, then restore repos/drupal files with git checkout (stays clean). - 11.2.x: Fix 3 (json-schema conflict detection) was reading the wrong path CoreDev/composer.json — correct path is DevDependencies/composer.json. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 83 +++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 29d488e..aa13e2a 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -713,32 +713,62 @@ STATUS_HEADER ACTUAL_DRUPAL_MAJOR="$DRUPAL_VERSION" fi - # Fix 1 (ALL versions): set drupal/core inline alias in root composer.json so the - # path repo branch (dev-) satisfies CoreRecommended's version constraint. - # Inline aliases are declared in the *requiring* package — no files in repos/drupal touched. - jq --arg val "dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" \ - '.require["drupal/core"] = $val' \ - composer.json > composer.json.tmp && mv composer.json.tmp composer.json - log_setup " Set drupal/core inline alias: dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" - - # Fix 2 (ALL versions): pin drupal/drupal to the path repo version so Composer - # cannot fall back to Packagist's drupal/drupal (whose self.version requirements - # would pull in remote packages instead of path repos). - jq --arg branch "dev-$CHECKED_OUT_BRANCH" \ - '.require["drupal/drupal"] = $branch' \ - composer.json > composer.json.tmp && mv composer.json.tmp composer.json - log_setup " Pinned drupal/drupal to path repo: dev-$CHECKED_OUT_BRANCH" - - # Fix 3 (actual drupal 10 only): drupal/core-dev 10.x requires composer/composer ^2.8.1 - # AND justinrainbow/json-schema ^5.2, but composer 2.9.x requires ^6.5.1 — conflict. - # Pin to 2.8.x and disable the security advisory block. - # We use ACTUAL_DRUPAL_MAJOR (not DRUPAL_VERSION) so a mis-selected version doesn't - # apply the pin to a 11/12 branch where it adds an unnecessary constraint. + # Fix 1 + Fix 2: set version constraints to use path repos for the checked-out branch. + # + # For 10.x / 11.x: use inline alias "dev-$BRANCH as N.x-dev" on drupal/core and all + # drupal/* sub-packages (except drupal/drupal). drupal/core uses self.version for + # sub-packages; with the alias self.version = N.x-dev. Sub-packages must also be aliased + # so path repos satisfy that constraint. Packagist has no N.x-dev alias for sub-packages + # on these versions, so there is no conflict. + # Pin drupal/drupal to dev-$BRANCH so Packagist's version cannot pull in remote packages. + # + # For 12.x/main: inline alias CANNOT be used. With "dev-$BRANCH as 12.x-dev", Composer + # puts both dev-$BRANCH AND 12.x-dev in the resolution pool; both emit self.version + # requirements (dev-$BRANCH and 12.x-dev) for sub-packages, which conflict. Packagist + # defines 12.x-dev = dev-main for every sub-package, blocking a second inline alias. + # Solution: temporarily add "dev-$BRANCH": "12.x-dev" to the branch-alias in + # repos/drupal/composer.json (drupal/drupal) and repos/drupal/core/composer.json + # (drupal/core). Path repos then satisfy 12.x-dev natively. Root drupal/core is changed + # from "dev-main" to "12.x-dev" so path repo wins over Packagist. drupal/drupal's + # self.version becomes 12.x-dev, consistent with drupal/core — sub-packages come from + # Packagist at 12.x-dev = dev-main (same code). After composer update the repos/drupal + # files are restored with git checkout, keeping the git checkout clean. + if [ "$ACTUAL_DRUPAL_MAJOR" != "12" ]; then + # 10.x / 11.x: inline alias for drupal/core and all drupal/* sub-packages + jq --arg val "dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" \ + '.require |= with_entries(if (.key | startswith("drupal/")) and .key != "drupal/drupal" then .value = $val else . end)' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Set inline alias for all drupal/* packages: dev-$CHECKED_OUT_BRANCH as $TARGET_ALIAS" + # Pin drupal/drupal to path repo + jq --arg branch "dev-$CHECKED_OUT_BRANCH" \ + '.require["drupal/drupal"] = $branch' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Pinned drupal/drupal to path repo: dev-$CHECKED_OUT_BRANCH" + else + # 12.x: add temporary branch-alias to path repo files, set root to 12.x-dev + for _repo_file in composer.json core/composer.json; do + jq --arg b "dev-$CHECKED_OUT_BRANCH" '.extra["branch-alias"][$b] = "12.x-dev"' \ + "$REPOS_DIR/$_repo_file" > "$REPOS_DIR/$_repo_file.tmp" \ + && mv "$REPOS_DIR/$_repo_file.tmp" "$REPOS_DIR/$_repo_file" + done + log_setup " Added temporary branch-alias dev-$CHECKED_OUT_BRANCH=12.x-dev to path repos" + # Change root drupal/core from "dev-main" to "12.x-dev" so path repo (with alias) wins + jq --arg alias "$TARGET_ALIAS" \ + '.require["drupal/core"] = $alias' \ + composer.json > composer.json.tmp && mv composer.json.tmp composer.json + log_setup " Set root drupal/core to: $TARGET_ALIAS (path repo with branch-alias will satisfy this)" + fi + + # Fix 3: drupal/core-dev on some branches (10.x, 11.2.x, ...) requires + # justinrainbow/json-schema ^5.2, but composer 2.9.x requires ^6.5.1 — conflict. + # Detect from the actual path repo rather than assuming by major version. # See https://www.drupal.org/project/drupal/issues/3557585 - if [ "$ACTUAL_DRUPAL_MAJOR" = "10" ]; then + _core_dev_json_schema=$(jq -r '.require["justinrainbow/json-schema"] // ""' \ + "$REPOS_DIR/composer/Metapackage/DevDependencies/composer.json" 2>/dev/null || echo "") + if echo "$_core_dev_json_schema" | grep -q '^\^5'; then jq '.require["composer/composer"] = "~2.8.1" | .config.audit["block-insecure"] = false' \ composer.json > composer.json.tmp && mv composer.json.tmp composer.json - log_setup " Applied drupal10 fix: pinned composer/composer to ~2.8.1" + log_setup " Applied composer/composer pin to ~2.8.1 (json-schema conflict detected)" fi # Fix 4 (ALL versions if directory exists): drupal/drupal on 11.x+ requires @@ -771,6 +801,13 @@ STATUS_HEADER update_status " cd $DRUPAL_DIR && ddev composer update -W" SETUP_FAILED=true fi + + # For 12.x, restore the temporarily modified path repo files so git checkout stays clean. + if [ "$ACTUAL_DRUPAL_MAJOR" = "12" ]; then + git -C "$REPOS_DIR" checkout -- composer.json core/composer.json >> "$SETUP_LOG" 2>&1 \ + && log_setup " Restored repos/drupal path repo files (12.x branch-alias cleanup)" \ + || log_setup " ⚠ Could not restore repos/drupal files — check git status in repos/drupal" + fi fi # end SETUP_FAILED (branch checkout) guard fi fi From ae81394e2f2b2bf942a6b683178e2b4121265503 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 16:19:05 -0700 Subject: [PATCH 20/21] fix: strip drupal- prefix from issue_fork param; fix broken vendor symlink Users naturally enter "drupal-2555609" matching the picker example, but the URL construction already prepends "drupal-", resulting in the invalid URL drupal-drupal-2555609.git. Strip the prefix if provided so both forms work. Also fix vendor symlink restore: -e follows symlinks so returns false for broken symlinks, causing ln to fail with "File exists". Add -L check for the broken case and use ln -sf to repair it. Co-Authored-By: Claude Sonnet 4.6 --- drupal-core/template.tf | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drupal-core/template.tf b/drupal-core/template.tf index aa13e2a..c9ef8a2 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -68,7 +68,7 @@ variable "cache_path" { data "coder_parameter" "issue_fork" { name = "issue_fork" display_name = "Issue Fork" - description = "Drupal.org issue fork name (e.g., drupal-3568144). Leave empty for standard Drupal core development." + description = "Drupal.org issue number or fork name (e.g., 3568144 or drupal-3568144). Leave empty for standard Drupal core development." type = "string" default = "" mutable = true @@ -570,6 +570,7 @@ STATUS_HEADER # Issue fork / install profile parameters (baked in at template evaluation) ISSUE_FORK="${data.coder_parameter.issue_fork.value}" + ISSUE_FORK="$${ISSUE_FORK#drupal-}" # strip leading "drupal-" if user provided it ISSUE_BRANCH="${data.coder_parameter.issue_branch.value}" INSTALL_PROFILE="${data.coder_parameter.install_profile.value}" USING_ISSUE_FORK=false @@ -816,9 +817,12 @@ STATUS_HEADER # This symlink (repos/drupal/vendor -> ../../vendor) is created by joachim-n's # post-install scripts. It can be absent when a previous workspace attempt failed # before composer install completed. - if [ -d "repos/drupal/.git" ] && [ ! -e "repos/drupal/vendor" ]; then + if [ -d "repos/drupal/.git" ] && [ ! -e "repos/drupal/vendor" ] && [ ! -L "repos/drupal/vendor" ]; then log_setup "Restoring missing repos/drupal/vendor symlink..." ln -s ../../vendor repos/drupal/vendor && log_setup " symlink restored" || log_setup " symlink restore failed (non-critical)" + elif [ -d "repos/drupal/.git" ] && [ -L "repos/drupal/vendor" ] && [ ! -e "repos/drupal/vendor" ]; then + log_setup "Fixing broken repos/drupal/vendor symlink..." + ln -sf ../../vendor repos/drupal/vendor && log_setup " symlink fixed" || log_setup " symlink fix failed (non-critical)" fi # Steps 5 and 6 are skipped if an earlier step (e.g. composer update) failed. From 620dccc11eca456ed34a959530d1d8b2bec3734a Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Fri, 6 Mar 2026 18:01:04 -0700 Subject: [PATCH 21/21] docs: update README and quickstart for issue fork support - drupal-core/README.md: add issue fork feature to Features list, add picker/CLI examples to Quick Start, update Customization to use install_profile parameter instead of editing template.tf - docs/user/quickstart.md: add issue picker section and note that issue remote is pre-added, keep manual workflow as secondary option - README.md: update drupal-core description to mention issue picker and issue fork support with Composer resolution for 10/11/12 - docs/README.md: add quickstart.md to For Users section Co-Authored-By: Claude Sonnet 4.6 --- README.md | 14 ++++++++------ docs/README.md | 4 +++- docs/user/quickstart.md | 21 ++++++++++++++++++++- drupal-core/README.md | 33 ++++++++++++++++++++------------- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 86bf508..4a1658f 100644 --- a/README.md +++ b/README.md @@ -118,15 +118,17 @@ coder create --template user-defined-web my-workspace ``` ### drupal-core (Drupal Core Development) -Fully automated Drupal core development environment. +Fully automated Drupal core development environment, including issue fork support. -- **Setup**: Automatic (Drupal core cloned and installed) -- **Use Case**: Drupal core development, contribution, testing +- **Setup**: Automatic (Drupal core cloned and installed, ~30s with seed cache) +- **Use Case**: Drupal core development, contribution, patch testing - **Template Directory**: `drupal-core/` +- **Issue Picker**: [start.coder.ddev.com/drupal-issue](https://start.coder.ddev.com/drupal-issue) — paste any drupal.org issue URL to launch a workspace with the issue branch pre-checked-out - **Includes**: - - Pre-cloned Drupal core main branch (shallow clone, 50 commits depth) - - Configured DDEV (PHP 8.5, Drupal HEAD/main config, port 80) - - Installed demo_umami site + - Pre-cloned Drupal core (main branch by default) + - Issue fork checkout with automatic Composer dependency resolution (Drupal 10, 11, and 12/main) + - Configured DDEV with automatic PHP version selection + - Installed demo_umami site (or standard/minimal via parameter) - Admin account (admin/admin) **Create workspace:** diff --git a/docs/README.md b/docs/README.md index cdc2472..8018f8e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,10 +26,12 @@ System administrators and DevOps teams managing Coder infrastructure and the DDE Developers and users creating and working with DDEV workspaces. +- **[Drupal Core Quickstart](./user/quickstart.md)** - Get a Drupal core workspace running in minutes; issue fork support - **[Getting Started Guide](./user/getting-started.md)** - First-time setup, creating your first workspace, basic verification - **[Using Workspaces](./user/using-workspaces.md)** - Daily workflows, VS Code for Web, DDEV projects, Git, port forwarding -**Start here:** [Getting Started Guide](./user/getting-started.md) +**Drupal core contributors:** [Quickstart](./user/quickstart.md) +**New to Coder:** [Getting Started Guide](./user/getting-started.md) ### For DDEV Experts diff --git a/docs/user/quickstart.md b/docs/user/quickstart.md index 46c9d4b..c47fcf9 100644 --- a/docs/user/quickstart.md +++ b/docs/user/quickstart.md @@ -64,7 +64,26 @@ ddev ssh --- -## First contribution workflow +## Working on a Drupal issue + +The fastest way: use the **[Drupal Issue Picker](https://start.coder.ddev.com/drupal-issue)**. Paste a drupal.org issue URL or bare issue number — it fetches the available branches, lets you pick one, and opens a pre-configured workspace with the issue branch already checked out and all Composer dependencies resolved for that branch. + +To push your changes back: + +```bash +cd ~/drupal-core/repos/drupal + +# ... make changes ... + +# Push to the issue fork (remote is already added by the setup) +git push issue HEAD +``` + +Then create or update the merge request on [drupal.org](https://www.drupal.org/project/drupal). + +## First contribution workflow (manual) + +If you prefer to set up manually: ```bash # In the workspace terminal: diff --git a/drupal-core/README.md b/drupal-core/README.md index c4b9da3..04c2e81 100644 --- a/drupal-core/README.md +++ b/drupal-core/README.md @@ -11,8 +11,9 @@ Automated Coder workspace for Drupal core development using [joachim-n/drupal-co - **Professional Setup**: Uses the drupal-core-development-project template - **Clean Git Clone**: Drupal core in `repos/drupal/` directory - **Proper Structure**: Web root at `web/` with Composer management -- **Demo Site**: Umami demo profile pre-installed -- **Full DDEV**: Complete DDEV environment with PHP 8.5, Drupal 12 config +- **Demo Site**: Umami demo profile pre-installed (configurable) +- **Full DDEV**: Complete DDEV environment with automatic PHP version selection +- **Issue Fork Support**: Check out any Drupal.org issue branch, with automatic Composer dependency resolution - **VS Code**: Opens directly to Drupal core project root - **Port Forwarding**: HTTP (80) - **Custom Launch Command**: `ddev launch` shows Coder-specific instructions @@ -32,14 +33,24 @@ Subsequent starts are fast (< 1 minute) as everything is already present. ## Quick Start +**Standard Drupal core workspace:** ```bash -# Create workspace coder create --template drupal-core my-drupal-dev - -# Wait for setup to complete (monitor in Coder UI logs) # Then access via Coder dashboard "DDEV Web" app ``` +**Working on a specific issue:** + +Use the **[Drupal Issue Picker](https://start.coder.ddev.com/drupal-issue)** — enter an issue URL or number and it opens a pre-configured workspace with the issue branch already checked out and composer dependencies resolved. + +Or manually via CLI: +```bash +coder create --template drupal-core my-issue-3568144 \ + --parameter issue_fork=3568144 \ + --parameter issue_branch=3568144-editorfilterxss-11.x \ + --parameter drupal_version=11 +``` + ## Access - **Website**: Click "DDEV Web" in Coder dashboard @@ -135,17 +146,13 @@ ddev drush si -y demo_umami --account-pass=admin ## Customization ### Change Drupal Profile -Edit the startup script in `template.tf` and change: +Set the `install_profile` parameter when creating the workspace: ```bash -ddev drush si -y demo_umami --account-pass=admin +coder create --template drupal-core my-workspace --parameter install_profile=standard ``` -To use `minimal`, `standard`, or other profiles. +Options: `demo_umami` (default), `minimal`, `standard`. -### Change PHP Version -Edit DDEV config command in `template.tf`: -```bash -ddev config --project-type=drupal12 --docroot=web --php-version=8.4 -``` +Note: issue fork workspaces always run a full site install regardless of profile; `demo_umami` only uses the cached DB snapshot for standard workspaces. ### Add Custom Commands Create scripts in `~/.ddev/commands/host/` or `.ddev/commands/web/`