Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .vortex/docs/content/hosting/acquia.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ settings from [`web/sites/default/includes/providers/settings.acquia.php`](https
This includes Acquia-specific configurations for caching, file paths, and
environment variables.

#### Acquia settings file

By default, Vortex includes the Acquia-provided settings file from
`/var/www/site-php/{group}/{group}-settings.inc`. You can override this path
by setting the `DRUPAL_ACQUIA_SETTINGS_FILE` environment variable.

#### Temporary file path

Vortex configures the temporary file path (`file_temp_path`) with a three-tier
priority:

1. **Default**: `/tmp`
2. **Shared GFS mount**: If `DRUPAL_TMP_PATH_IS_SHARED` is set, uses
`/mnt/gfs/{group}.{env}/tmp` — a per-head mounted directory on Acquia's
shared filesystem. This is useful for operations like bulk uploads that
require a shared temporary directory across web heads. See
[Acquia temporary files documentation](https://docs.acquia.com/acquia-cloud-platform/manage-apps/files/temporary#section-important-considerations)
for details.
3. **Explicit override**: If `DRUPAL_TMP_PATH` is set, its value is used
regardless of other settings.

## Onboarding

Before you begin, ensure you have:
Expand All @@ -63,6 +84,9 @@ file:
| Variable | Description |
|----------|-------------|
| `VORTEX_ACQUIA_APP_NAME` | Your Acquia application name (machine name) |
| `DRUPAL_ACQUIA_SETTINGS_FILE` | Override the path to Acquia's settings `.inc` file (optional) |
| `DRUPAL_TMP_PATH` | Override the temporary file path (optional) |
| `DRUPAL_TMP_PATH_IS_SHARED` | Use Acquia's shared GFS mount for temporary files (optional) |

For CI environments, configure these secrets in your CI provider ([GitHub Actions](/docs/continuous-integration/github-actions) or [CircleCI](/docs/continuous-integration/circleci)):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
$vortex_localdev_url = getenv('VORTEX_LOCALDEV_URL');
if (!empty($vortex_localdev_url)) {
// Local development URL.
$patterns = str_replace(['.', 'https://', 'http://', ','], [
$container_patterns = str_replace(['.', 'https://', 'http://', ','], [
'\.', '', '', '|',
], $vortex_localdev_url);
$settings['trusted_host_patterns'][] = '^' . $patterns . '$';
$settings['trusted_host_patterns'][] = '^' . $container_patterns . '$';

// URL for internal container access (e.g., via drush, in tests etc.).
$settings['trusted_host_patterns'][] = '^nginx$';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@
declare(strict_types=1);

if (!empty(getenv('AH_SITE_ENVIRONMENT'))) {
$ah_site_group = getenv('AH_SITE_GROUP');
$ah_site_env = getenv('AH_SITE_ENVIRONMENT');

// Delay the initial database connection.
$config['acquia_hosting_settings_autoconnect'] = FALSE;

// Include Acquia environment settings.
// The path is built dynamically from the AH_SITE_GROUP environment variable
// provided by Acquia Cloud.
$ah_site_group = getenv('AH_SITE_GROUP');
// @codeCoverageIgnoreStart
if (!empty($ah_site_group)) {
$ah_settings_file = sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
$ah_settings_file = getenv('DRUPAL_ACQUIA_SETTINGS_FILE') ?: sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
// @codeCoverageIgnoreStart
if (!file_exists($ah_settings_file)) {
throw new \RuntimeException(sprintf('Acquia settings file "%s" not found. Check Acquia Cloud environment configuration.', $ah_settings_file));
}
require $ah_settings_file;
// @codeCoverageIgnoreEnd
}
// @codeCoverageIgnoreEnd

// Default all environments to 'dev', including ODE environments.
$settings['environment'] = ENVIRONMENT_DEV;

Expand All @@ -51,14 +52,28 @@
// Override the config sync directory with the DRUPAL_CONFIG_PATH environment
// variable if provided, or fall back to the config_vcs_directory setting
// provided by Acquia.
$drupal_config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($drupal_config_path)) {
$settings['config_sync_directory'] = $drupal_config_path;
$config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($config_path)) {
$settings['config_sync_directory'] = $config_path;
}
elseif (!empty($settings['config_vcs_directory'])) {
$settings['config_sync_directory'] = $settings['config_vcs_directory'];
}

// Automatically create an Apache HTTP .htaccess file in writable directories.
$settings['auto_create_htaccess'] = TRUE;

// Allow to override temporary path using per-head mounted directory or
// DRUPAL_TMP_PATH variable.
// @see https://docs.acquia.com/acquia-cloud-platform/manage-apps/files/temporary#section-important-considerations
$settings['file_temp_path'] = '/tmp';

if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED')) {
// @see https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
$settings['file_temp_path'] = sprintf('/mnt/gfs/%s.%s/tmp', $ah_site_group, $ah_site_env);
}

if (getenv('DRUPAL_TMP_PATH')) {
$settings['file_temp_path'] = getenv('DRUPAL_TMP_PATH');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
$vortex_localdev_url = getenv('VORTEX_LOCALDEV_URL');
if (!empty($vortex_localdev_url)) {
// Local development URL.
$patterns = str_replace(['.', 'https://', 'http://', ','], [
$container_patterns = str_replace(['.', 'https://', 'http://', ','], [
'\.', '', '', '|',
], $vortex_localdev_url);
$settings['trusted_host_patterns'][] = '^' . $patterns . '$';
$settings['trusted_host_patterns'][] = '^' . $container_patterns . '$';

// URL for internal container access (e.g., via drush, in tests etc.).
$settings['trusted_host_patterns'][] = '^nginx$';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@
declare(strict_types=1);

if (!empty(getenv('AH_SITE_ENVIRONMENT'))) {
$ah_site_group = getenv('AH_SITE_GROUP');
$ah_site_env = getenv('AH_SITE_ENVIRONMENT');

// Delay the initial database connection.
$config['acquia_hosting_settings_autoconnect'] = FALSE;

// Include Acquia environment settings.
// The path is built dynamically from the AH_SITE_GROUP environment variable
// provided by Acquia Cloud.
$ah_site_group = getenv('AH_SITE_GROUP');
// @codeCoverageIgnoreStart
if (!empty($ah_site_group)) {
$ah_settings_file = sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
$ah_settings_file = getenv('DRUPAL_ACQUIA_SETTINGS_FILE') ?: sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
// @codeCoverageIgnoreStart
if (!file_exists($ah_settings_file)) {
throw new \RuntimeException(sprintf('Acquia settings file "%s" not found. Check Acquia Cloud environment configuration.', $ah_settings_file));
}
require $ah_settings_file;
// @codeCoverageIgnoreEnd
}
// @codeCoverageIgnoreEnd

// Default all environments to 'dev', including ODE environments.
$settings['environment'] = ENVIRONMENT_DEV;

Expand All @@ -51,14 +52,28 @@
// Override the config sync directory with the DRUPAL_CONFIG_PATH environment
// variable if provided, or fall back to the config_vcs_directory setting
// provided by Acquia.
$drupal_config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($drupal_config_path)) {
$settings['config_sync_directory'] = $drupal_config_path;
$config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($config_path)) {
$settings['config_sync_directory'] = $config_path;
}
elseif (!empty($settings['config_vcs_directory'])) {
$settings['config_sync_directory'] = $settings['config_vcs_directory'];
}

// Automatically create an Apache HTTP .htaccess file in writable directories.
$settings['auto_create_htaccess'] = TRUE;

// Allow to override temporary path using per-head mounted directory or
// DRUPAL_TMP_PATH variable.
// @see https://docs.acquia.com/acquia-cloud-platform/manage-apps/files/temporary#section-important-considerations
$settings['file_temp_path'] = '/tmp';

if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED')) {
// @see https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
$settings['file_temp_path'] = sprintf('/mnt/gfs/%s.%s/tmp', $ah_site_group, $ah_site_env);
}

if (getenv('DRUPAL_TMP_PATH')) {
$settings['file_temp_path'] = getenv('DRUPAL_TMP_PATH');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
$vortex_localdev_url = getenv('VORTEX_LOCALDEV_URL');
if (!empty($vortex_localdev_url)) {
// Local development URL.
$patterns = str_replace(['.', 'https://', 'http://', ','], [
$container_patterns = str_replace(['.', 'https://', 'http://', ','], [
'\.', '', '', '|',
], $vortex_localdev_url);
$settings['trusted_host_patterns'][] = '^' . $patterns . '$';
$settings['trusted_host_patterns'][] = '^' . $container_patterns . '$';

// URL for internal container access (e.g., via drush, in tests etc.).
$settings['trusted_host_patterns'][] = '^nginx$';
Expand Down
6 changes: 3 additions & 3 deletions .vortex/tests/bats/unit/download-db-acquia.bats
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ load ../_helper.bash

# Mock file download curl call with its message and side effect to create zipped archive
"Downloading DB dump into file .data/testdb_backup_backup-id-789.sql.gz."
'@curl --progress-bar -L -H Accept: application/json, version=2 -H Authorization: Bearer test-token https://backup.example.com/db.sql.gz -o .data/testdb_backup_backup-id-789.sql.gz # 0 # # echo "CREATE TABLE test (id INT);" | gzip > .data/testdb_backup_backup-id-789.sql.gz'
'@curl --progress-bar -L https://backup.example.com/db.sql.gz -o .data/testdb_backup_backup-id-789.sql.gz # 0 # # echo "CREATE TABLE test (id INT);" | gzip > .data/testdb_backup_backup-id-789.sql.gz'

# Mock gunzip operations with their message
"Expanding DB file .data/testdb_backup_backup-id-789.sql.gz into .data/testdb_backup_backup-id-789.sql."
Expand Down Expand Up @@ -228,7 +228,7 @@ load ../_helper.bash

# Mock file download curl call with its message and side effect to create zipped archive
"Downloading DB dump into file ./.data/testdb_backup_backup-id-789.sql.gz."
'@curl --progress-bar -L -H Accept: application/json, version=2 -H Authorization: Bearer test-token https://backup.example.com/db.sql.gz -o ./.data/testdb_backup_backup-id-789.sql.gz # 0 # # mkdir -p ./.data && echo "database content" | gzip > ./.data/testdb_backup_backup-id-789.sql.gz'
'@curl --progress-bar -L https://backup.example.com/db.sql.gz -o ./.data/testdb_backup_backup-id-789.sql.gz # 0 # # mkdir -p ./.data && echo "database content" | gzip > ./.data/testdb_backup_backup-id-789.sql.gz'

# Mock gunzip operations with their message
"Expanding DB file ./.data/testdb_backup_backup-id-789.sql.gz into ./.data/testdb_backup_backup-id-789.sql."
Expand Down Expand Up @@ -494,7 +494,7 @@ load ../_helper.bash
'@curl --progress-bar -L -H Accept: application/json, version=2 -H Authorization: Bearer test-token https://cloud.acquia.com/api/environments/env-id-456/databases/testdb/backups/backup-id-new-123/actions/download # {"url":"https://backup.example.com/db-fresh.sql.gz"}'

"[TASK] Downloading DB dump into file .data/testdb_backup_backup-id-new-123.sql.gz."
'@curl --progress-bar -L -H Accept: application/json, version=2 -H Authorization: Bearer test-token https://backup.example.com/db-fresh.sql.gz -o .data/testdb_backup_backup-id-new-123.sql.gz # 0 # # echo "CREATE TABLE fresh (id INT);" | gzip > .data/testdb_backup_backup-id-new-123.sql.gz'
'@curl --progress-bar -L https://backup.example.com/db-fresh.sql.gz -o .data/testdb_backup_backup-id-new-123.sql.gz # 0 # # echo "CREATE TABLE fresh (id INT);" | gzip > .data/testdb_backup_backup-id-new-123.sql.gz'

"[TASK] Expanding DB file .data/testdb_backup_backup-id-new-123.sql.gz into .data/testdb_backup_backup-id-new-123.sql."
"@gunzip -t .data/testdb_backup_backup-id-new-123.sql.gz # 0"
Expand Down
2 changes: 1 addition & 1 deletion scripts/vortex/download-db-acquia.sh
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ else
[ -z "${backup_url}" ] && fail "Unable to discover backup URL for backup ID '${backup_id}'. API response: ${backup_json}" && exit 1

task "Downloading DB dump into file ${file_name_compressed}."
curl --progress-bar -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "${backup_url}" -o "${file_name_compressed}"
curl --progress-bar -L "${backup_url}" -o "${file_name_compressed}"
download_result=$?

# shellcheck disable=SC2181
Expand Down
33 changes: 24 additions & 9 deletions web/sites/default/includes/providers/settings.acquia.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@
declare(strict_types=1);

if (!empty(getenv('AH_SITE_ENVIRONMENT'))) {
$ah_site_group = getenv('AH_SITE_GROUP');
$ah_site_env = getenv('AH_SITE_ENVIRONMENT');

// Delay the initial database connection.
$config['acquia_hosting_settings_autoconnect'] = FALSE;

// Include Acquia environment settings.
// The path is built dynamically from the AH_SITE_GROUP environment variable
// provided by Acquia Cloud.
$ah_site_group = getenv('AH_SITE_GROUP');
// @codeCoverageIgnoreStart
if (!empty($ah_site_group)) {
$ah_settings_file = sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
$ah_settings_file = getenv('DRUPAL_ACQUIA_SETTINGS_FILE') ?: sprintf('/var/www/site-php/%s/%s-settings.inc', $ah_site_group, $ah_site_group);
// @codeCoverageIgnoreStart
if (!file_exists($ah_settings_file)) {
throw new \RuntimeException(sprintf('Acquia settings file "%s" not found. Check Acquia Cloud environment configuration.', $ah_settings_file));
}
require $ah_settings_file;
// @codeCoverageIgnoreEnd
}
// @codeCoverageIgnoreEnd

// Default all environments to 'dev', including ODE environments.
$settings['environment'] = ENVIRONMENT_DEV;

Expand All @@ -51,14 +52,28 @@
// Override the config sync directory with the DRUPAL_CONFIG_PATH environment
// variable if provided, or fall back to the config_vcs_directory setting
// provided by Acquia.
$drupal_config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($drupal_config_path)) {
$settings['config_sync_directory'] = $drupal_config_path;
$config_path = getenv('DRUPAL_CONFIG_PATH');
if (!empty($config_path)) {
$settings['config_sync_directory'] = $config_path;
}
elseif (!empty($settings['config_vcs_directory'])) {
$settings['config_sync_directory'] = $settings['config_vcs_directory'];
}

// Automatically create an Apache HTTP .htaccess file in writable directories.
$settings['auto_create_htaccess'] = TRUE;

// Allow to override temporary path using per-head mounted directory or
// DRUPAL_TMP_PATH variable.
// @see https://docs.acquia.com/acquia-cloud-platform/manage-apps/files/temporary#section-important-considerations
$settings['file_temp_path'] = '/tmp';

if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED')) {
// @see https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
$settings['file_temp_path'] = sprintf('/mnt/gfs/%s.%s/tmp', $ah_site_group, $ah_site_env);
}
Comment on lines +71 to +74
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In PHP, what does getenv()return for set and unset variables, and how are the strings"0"and"false" evaluated in boolean conditions?

💡 Result:

In PHP, getenv($name) returns the string value of the environment variable if it is set, or false if it is unset (does not exist). This behavior is unchanged in PHP 8+. In boolean conditions (e.g., if (getenv('VAR'))), PHP converts the result to bool: - If unset: false → false - If set to non-empty string: true (truthy) - If set to empty string "": false - If set to "0": false (specifically listed as falsy) The string "false" (or any other non-empty string except "0") evaluates to true in boolean contexts. Examples: $unset = getenv('UNSET_VAR'); // false → if($unset) is false $set_empty = getenv('EMPTY'); // "" (if VAR=) → if($set_empty) is false $set_zero = getenv('ZERO'); // "0" → if($set_zero) is false $set_false = getenv('FALSE'); // "false" → if($set_false) is true $set_one = getenv('ONE'); // "1" → if($set_one) is true

Citations:


Use explicit comparison for DRUPAL_TMP_PATH_IS_SHARED instead of relying on PHP truthiness.

getenv() returns a string when set and false when unset. In boolean context, only the strings "" and "0" are falsey; the string "false" is truthy. This means setting DRUPAL_TMP_PATH_IS_SHARED=false would unexpectedly enable the temporary path override. Explicitly compare to the intended value to prevent misconfiguration.

Suggested fix
-  if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED')) {
+  if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED') === '1') {
     // `@see` https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
     $settings['file_temp_path'] = "/mnt/gfs/{$ah_site_group}.{$ah_site_env}/tmp";
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED')) {
// @see https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
$settings['file_temp_path'] = "/mnt/gfs/{$ah_site_group}.{$ah_site_env}/tmp";
}
if (!empty($ah_site_group) && getenv('DRUPAL_TMP_PATH_IS_SHARED') === '1') {
// `@see` https://acquia.my.site.com/s/article/360054835954-Bulk-Upload-Not-Working-Correctly
$settings['file_temp_path'] = "/mnt/gfs/{$ah_site_group}.{$ah_site_env}/tmp";
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/sites/default/includes/providers/settings.acquia.php` around lines 71 -
74, The conditional uses getenv('DRUPAL_TMP_PATH_IS_SHARED') in a boolean
context which treats the string "false" as truthy; change the check to an
explicit comparison so the override only runs when the env is intentionally
enabled (e.g., compare getenv('DRUPAL_TMP_PATH_IS_SHARED') === 'true' or use
in_array(getenv('DRUPAL_TMP_PATH_IS_SHARED'), ['1','true'], true)). Update the
if condition that references $ah_site_group and
getenv('DRUPAL_TMP_PATH_IS_SHARED') so it explicitly matches the allowed truthy
values before setting $settings['file_temp_path'].

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is intentional. The convention in this project is to use truthiness checks for environment variable flags, consistent with how other env vars are handled in these settings files. Setting DRUPAL_TMP_PATH_IS_SHARED=false is not an expected usage pattern — the variable is either set (to any non-empty value) or unset.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@AlexSkrypnyk, understood — thanks for the clarification. I'll keep this convention in mind for future reviews of this project's settings files.


✏️ Learnings added
Learnt from: AlexSkrypnyk
Repo: drevops/vortex PR: 2445
File: web/sites/default/includes/providers/settings.acquia.php:71-74
Timestamp: 2026-04-02T07:27:42.181Z
Learning: In the `drevops/vortex` project, boolean environment variable flags (e.g., `DRUPAL_TMP_PATH_IS_SHARED`) are intentionally checked using PHP truthiness (e.g., `getenv('VAR')`) rather than explicit comparisons like `=== '1'`. The convention is that such variables are either set to any non-empty value or left unset; setting them to `"false"` is not an expected usage pattern. This applies to settings files under `web/sites/default/includes/providers/`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


if (getenv('DRUPAL_TMP_PATH')) {
$settings['file_temp_path'] = getenv('DRUPAL_TMP_PATH');
}
}
4 changes: 2 additions & 2 deletions web/sites/default/includes/providers/settings.container.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
$vortex_localdev_url = getenv('VORTEX_LOCALDEV_URL');
if (!empty($vortex_localdev_url)) {
// Local development URL.
$patterns = str_replace(['.', 'https://', 'http://', ','], [
$container_patterns = str_replace(['.', 'https://', 'http://', ','], [
'\.', '', '', '|',
], $vortex_localdev_url);
$settings['trusted_host_patterns'][] = '^' . $patterns . '$';
$settings['trusted_host_patterns'][] = '^' . $container_patterns . '$';

// URL for internal container access (e.g., via drush, in tests etc.).
$settings['trusted_host_patterns'][] = '^nginx$';
Expand Down
Loading