From be66820b83afbc7e952d3103bd4fe759cdf75883 Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Sat, 25 Jul 2026 09:47:37 -0600 Subject: [PATCH] fix: don't configure the external-auth URL as a Composer GitHub token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `coder external-auth access-token github` prints the token to stdout and exits 0, but when the user has no linked GitHub account it prints the *authentication URL* to stdout and exits 1. The token capture discarded that exit code with `|| true`, so workspaces whose owner had never authorized the deployment's GitHub app configured this as their OAuth token: [github-oauth.github.com] https://staging-coder.ddev.com/external-auth/github Every github.com dist download then failed with "Could not authenticate against github.com", and because Composer disables source fallback once authentication fails, all three composer install attempts failed the same way — the retry loop can't help, nothing here is transient. This is strictly worse than configuring no token at all: with none, Composer downloads anonymously and succeeds, which is exactly what happens once the bogus value is unset. - Honor the exit code, so only a real token is used. - Validate the token against api.github.com/user before configuring it, so an expired or revoked GITHUB_TOKEN fails loudly here instead of opaquely during install. - Unset any token a previous run configured when we don't have a valid one. Composer's global config lives in a persistent DDEV volume, so a bad value keeps breaking every later start even after this code stops writing one. Also stop running the cache rebuild when SETUP_FAILED is set — it sat outside the guard and appended a misleading "drush is not available" error after the real failure. Applied to drupal-contrib too, which had an identical copy of the block. Co-Authored-By: Claude Opus 5 (1M context) --- drupal-contrib/template.tf | 23 ++++++++++++++++++++++- drupal-core/template.tf | 33 +++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/drupal-contrib/template.tf b/drupal-contrib/template.tf index 250e38e..847dec5 100644 --- a/drupal-contrib/template.tf +++ b/drupal-contrib/template.tf @@ -717,12 +717,33 @@ COMPOSE_EOF if [ -z "$${_COMPOSER_GITHUB_TOKEN}" ]; then _CODER_BIN=$(find /tmp -name "coder" -path "*/coder.*/*" -type f -executable 2>/dev/null | head -1) if [ -n "$${_CODER_BIN}" ]; then - _COMPOSER_GITHUB_TOKEN=$("$${_CODER_BIN}" external-auth access-token github 2>/dev/null || true) + # `coder external-auth access-token` prints the token to stdout and exits 0, + # but when the user has no linked GitHub account it prints the *authentication + # URL* to stdout and exits 1. The exit code must be honored: handing that URL + # to Composer as an OAuth token makes every github.com dist download fail with + # "Could not authenticate against github.com" and suppresses the anonymous + # fallback, which is strictly worse than configuring no token at all. + if _token=$("$${_CODER_BIN}" external-auth access-token github 2>/dev/null); then + _COMPOSER_GITHUB_TOKEN="$_token" + else + log_setup "No linked GitHub account — using anonymous Composer downloads" + fi fi fi + # An expired or revoked token fails the same opaque way, so verify before use. + if [ -n "$${_COMPOSER_GITHUB_TOKEN}" ] && \ + ! curl -sSf --max-time 10 -o /dev/null -H "Authorization: Bearer $${_COMPOSER_GITHUB_TOKEN}" https://api.github.com/user 2>/dev/null; then + log_setup "⚠ GitHub token rejected by api.github.com — using anonymous Composer downloads" + _COMPOSER_GITHUB_TOKEN="" + fi if [ -n "$${_COMPOSER_GITHUB_TOKEN}" ]; then log_setup "Configuring Composer GitHub OAuth..." ddev exec composer config --global github-oauth.github.com "$${_COMPOSER_GITHUB_TOKEN}" >> "$SETUP_LOG" 2>&1 || true + else + # Clear any token a previous run configured. Composer's global config lives in + # a persistent DDEV volume, so a bad value keeps breaking every later start + # even once this code stops writing one. + ddev exec composer config --global --unset github-oauth.github.com >> "$SETUP_LOG" 2>&1 || true fi # Run ddev poser: expands composer.json → composer.contrib.json (includes require-dev), diff --git a/drupal-core/template.tf b/drupal-core/template.tf index 1f051be..0611a62 100644 --- a/drupal-core/template.tf +++ b/drupal-core/template.tf @@ -887,12 +887,33 @@ WELCOME_STATIC if [ -z "$${_COMPOSER_GITHUB_TOKEN}" ]; then _CODER_BIN=$(find /tmp -name "coder" -path "*/coder.*/*" -type f -executable 2>/dev/null | head -1) if [ -n "$${_CODER_BIN}" ]; then - _COMPOSER_GITHUB_TOKEN=$("$${_CODER_BIN}" external-auth access-token github 2>/dev/null || true) + # `coder external-auth access-token` prints the token to stdout and exits 0, + # but when the user has no linked GitHub account it prints the *authentication + # URL* to stdout and exits 1. The exit code must be honored: handing that URL + # to Composer as an OAuth token makes every github.com dist download fail with + # "Could not authenticate against github.com" and suppresses the anonymous + # fallback, which is strictly worse than configuring no token at all. + if _token=$("$${_CODER_BIN}" external-auth access-token github 2>/dev/null); then + _COMPOSER_GITHUB_TOKEN="$_token" + else + log_setup "No linked GitHub account — using anonymous Composer downloads" + fi fi fi + # An expired or revoked token fails the same opaque way, so verify before use. + if [ -n "$${_COMPOSER_GITHUB_TOKEN}" ] && \ + ! curl -sSf --max-time 10 -o /dev/null -H "Authorization: Bearer $${_COMPOSER_GITHUB_TOKEN}" https://api.github.com/user 2>/dev/null; then + log_setup "⚠ GitHub token rejected by api.github.com — using anonymous Composer downloads" + _COMPOSER_GITHUB_TOKEN="" + fi if [ -n "$${_COMPOSER_GITHUB_TOKEN}" ]; then log_setup "Configuring Composer GitHub OAuth..." ddev exec composer config --global github-oauth.github.com "$${_COMPOSER_GITHUB_TOKEN}" >> "$SETUP_LOG" 2>&1 || true + else + # Clear any token a previous run configured. Composer's global config lives in + # a persistent DDEV volume, so a bad value keeps breaking every later start + # even once this code stops writing one. + ddev exec composer config --global --unset github-oauth.github.com >> "$SETUP_LOG" 2>&1 || true fi # Retry up to 3 times to handle transient codeload.github.com 400s. @@ -1006,9 +1027,13 @@ WELCOME_STATIC fi fi # end SETUP_FAILED guard - # 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 6.5: Cache rebuild — ensures a clean state after any successful setup path. + # Skipped when setup failed: there is no installed site to rebuild, and running it + # anyway just appends a confusing "drush is not available" error to the log. + if [ "$SETUP_FAILED" != "true" ]; then + log_setup "Running cache rebuild..." + ddev drush cr >> "$SETUP_LOG" 2>&1 || true + fi # Step 6.6: Set up phpunit.xml for running core tests if [ ! -f "phpunit.xml" ] && [ -f "phpunit-ddev.xml" ]; then