Skip to content

fix(install, reset, update)!: Preserve user-defined environment variables - #6806

Open
sebagallo wants to merge 5 commits into
ScoopInstaller:developfrom
sebagallo:fix/preserve-user-env-vars
Open

sebagallo wants to merge 5 commits into
ScoopInstaller:developfrom
sebagallo:fix/preserve-user-env-vars

Conversation

@sebagallo

@sebagallo sebagallo commented Nov 1, 2026 •

Copy link
Copy Markdown

Description

  • env_set (lib/install.ps1) now distinguishes genuine user settings from values Scoop itself wrote. A new Test-EnvVarUserDefined helper replays the expanded env_set value every installed app in the target scope would have written — explicitly bound install-scope variables (dir, original_dir, persist_dir, app, version, url, bucket), junction- and no_junction-aware. Only values that no installed manifest accounts for are preserved as user settings; Scoop-owned values are re-pointed to the app being installed/reset, so a manifest's env_set still takes effect after reinstalls, version-dir values (APP_HOME: $dir) do not go stale under NO_JUNCTION, and a shared variable follows the app you just installed or reset.
  • Upgrades (update_app) no longer remove manifest environment variables; the new manifest overwrites Scoop-owned values. Variables the new manifest stops declaring are removed only when their value still matches what the updated app's old manifest wrote; values belonging to the user or to another installed app are left untouched. PATH cleanup (env_rm_path) is kept: those entries point at the old version directory and must be replaced.
  • scoop uninstall gains -k/--keep-env, which skips removing the manifest's environment variables. scoop uninstall scoop -k/--keep-env is rejected up front, since it would be silently ignored by the self-removal path.
  • scoop reset no longer force-resets values: it re-applies the manifest value unless the user has edited it (per-app shared variables are re-pointed again, as before).
  • New unit tests cover: set-when-undefined, user-value preserved, value owned by another installed app re-pointed, empty-as-undefined, ownership replay true/false, and uninstall removal; CHANGELOG has entries under Unreleased.

Motivation and Context

Closes ScoopInstaller/Main#8116

Static env_set blocks clobber variables the user has deliberately configured (e.g. a cache directory on another volume), and the upgrade path additionally unsets them during the internal uninstall step. Because the layout is cross-volume, forcing it back to persist also breaks hard-linking and degrades performance. Fixing it in core (instead of per-manifest workarounds, cf. ScoopInstaller/Main#8709) protects every bucket. A plain uninstall without --keep-env still cleans the variables up, so fresh installs keep working.

How Has This Been Tested?

  • Full core suite via bin/test.ps1 on Pester 6.2, PSScriptAnalyzer 1.25: all tests passed (unit + lint + static-analysis + file-style; decompress matrix excluded locally per the harness's own changed-files gate, runs on CI).
  • 6 env-related regression tests in test/Scoop-Install.Tests.ps1.
  • Manual smoke against the real HKCU registry: a pre-defined (unaccounted) variable survived env_set, an undefined one was set to the manifest value, Test-EnvVarUserDefined classified both correctly, and uninstall-time removal still deletes it.
  • Option parsing through the real dispatcher (bin/scoop.ps1): uninstall -k, --keep-env, and combined -kp accepted; -z/--bogus rejected as before.
  • Windows 11 x64, PowerShell 7.

Checklist:

  • I have read the Contributing Guide.
  • I have ensured that I am targeting the develop branch.
  • I have updated the documentation accordingly. (docs live in ScoopInstaller/Docs; a matching PR there for --keep-env and the env_set behavior change can follow)
  • I have updated the tests accordingly.
  • I have added an entry in the CHANGELOG.

RetriggerView in GreptileConfidence Score: 5/5

No new blocking issue was identified.

Summary

Preserves user-defined environment values while allowing Scoop-owned values to follow the selected app.

  • Scoop leaves user-set environment values alone.
  • Updates keep environment values the old app no longer owns.
  • Uninstall can leave an app’s environment variables behind.

No new actionable findings were identified.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Apply manifest environment variables] --> B{Installed manifest accounts for value?}
    B -- Yes --> C[Write selected app value]
    B -- No, value exists --> D[Keep user value]
    C --> E[Add effective app paths]
    D --> E
    F[Remove app paths] --> G[Consider stored value and manifest default]
    G --> H[Remove paths inside app directories]
Loading

Reviews (11) · Last reviewed commit: "fix(install): resolve %VAR% env_add_path..." · Reviewed by Greptile

Comment thread lib/install.ps1 Resolved
Comment thread libexec/scoop-uninstall.ps1 Resolved
Comment thread lib/update.ps1 Resolved
Comment thread lib/update.ps1 Resolved
@sebagallo
sebagallo force-pushed the fix/preserve-user-env-vars branch 3 times, most recently from e84990f to 3cd7568 Compare November 3, 2026 03:32
…bles

The 'env_set' block no longer overwrites a variable that is already
defined in the scope the install writes to; upgrades no longer remove
the variables of the old version before installing the new one; and
'scoop reset' no longer removes them before re-adding. 'scoop uninstall'
gains '-k'/'--keep-env' to skip their removal there too.

BREAKING CHANGE: 'env_set' values changed by a manifest between versions
are no longer applied on upgrade or reset. A plain uninstall (without
'--keep-env') followed by an install still applies the new values.
Address review: 'env_set' now distinguishes genuine user settings from
values Scoop itself wrote. Test-EnvVarUserDefined replays the installed
manifests' expanded 'env_set' values and only protects those no installed
app accounts for; Scoop-owned values (including versioned ones under
NO_JUNCTION and values owned by another app sharing the variable) are
re-pointed to the installing/resetting app. Upgrades remove variables
the new manifest no longer declares, unless user-defined.
'scoop uninstall scoop --keep-env' is now rejected up front.
Address review: when the new manifest stops declaring an 'env_set'
variable, remove it only if its current value matches what this app's
old manifest wrote. Values claimed by another installed app (via the
new 'exclude_app' parameter of Test-EnvVarUserDefined) or edited by the
user are left untouched.
…set values

Address review: expand_env_set_value relied on PowerShell dynamic
scoping, so a dropped manifest value using the version variable expanded
with the replacement version (or nothing). The helper now takes an
explicit variable set mirroring the installer's scope (dir, original_dir,
persist_dir, app, version, url, bucket) and both the ownership replay and
the update cleanup pass the installed/old values.
@sebagallo
sebagallo force-pushed the fix/preserve-user-env-vars branch from 3cd7568 to 336c13f Compare November 6, 2026 12:58
Comment thread lib/install.ps1 Resolved
Comment thread lib/update.ps1 Resolved
@sebagallo
sebagallo force-pushed the fix/preserve-user-env-vars branch 2 times, most recently from e03eb59 to 401cb34 Compare November 6, 2026 13:21
Comment thread lib/update.ps1 Resolved
@greptile-apps

greptile-apps Bot commented Nov 6, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

@sebagallo
sebagallo force-pushed the fix/preserve-user-env-vars branch from 401cb34 to 7ecc6a5 Compare November 6, 2026 13:30
Comment thread lib/install.ps1 Resolved
@sebagallo
sebagallo force-pushed the fix/preserve-user-env-vars branch from 7ecc6a5 to c173e59 Compare November 6, 2026 13:40
…aken

Address review: 'env_paths' expanded '%VAR%' entries from the manifest's
env_set default even though 'env_set' preserves user-defined variables,
so PATH got the wrong directory, and re-expanding only one value either
way left stale entries on PATH. 'env_paths' now takes the consumer:
adding resolves the value in effect (the stored one, the default as
fallback), the update-time keep-list expands the new manifest as written
('$mode' = 'manifest' in 'remove_obsolete_wiring'), and removal expands
both, so directories Scoop added before the variable was edited or the
entry was dropped are cleaned up too. Containment against the app and
persist directories is unchanged.
@sebagallo

sebagallo commented Nov 6, 2026 •

Copy link
Copy Markdown
Author

Rebased on develop twice as it moved (#6808 env-path expansion, then #6810 update-failure restore). The Addressed in … replies above reference pre-rebase commits; they map to the current history as: 56fe2faa → e99adf4e, b786fbc5 → 056448aa, 398368ee → 336c13f4, plus this round's c173e598 (consumer-aware %VAR% expansion).

Heads-up for reviewers of the #6810 interaction: remove_obsolete_wiring deletes variables the new manifest no longer declares unconditionally; this branch anchors that removal to the updated app's old-manifest expansion and skips user-defined values and values claimed by other installed apps. env_paths now expands %VAR% per consumer (effective/manifest/remove) so PATH cleanup removes what was actually added.

Failed-update env handling (correcting an earlier version of this comment that overstated a gap): the failure paths are covered by the same replay as reset — restore_app calls link_app with the old manifest, whose guarded env_set rewrites variables that the rollback removed, and env_add_path re-adds their directories. An uninstaller failure leaves the environment untouched entirely. The one residue is a double failure where unlink_app's own cleanup throws (already warned): shared variables can be left pointing at the failed version's target.

@niheaven

niheaven commented Nov 6, 2026

Copy link
Copy Markdown
Member

I'm refactoring the env handle now, with new shiming system which could handle envs in shim file. And the best place to put the envs that scoop added is the app's install.json. Please wait for the entire jobs' done then continue again.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants