mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-04-03 11:34:50 +08:00
[New] nvm install --offline: install from cache without network access
Add `--offline` flag to `nvm install` that resolves versions using only locally installed versions and cached downloads. No network calls are made. New helper functions `nvm_ls_cached` and `nvm_offline_version` scan `$NVM_DIR/.cache/bin/` for previously downloaded tarballs. In offline mode, `nvm_download_artifact` returns cached tarballs directly without checksum verification or download attempts. The curl/wget requirement is skipped when `--offline` is set. Supports `--lts` via locally stored LTS alias files.
This commit is contained in:
16
README.md
16
README.md
@@ -482,6 +482,22 @@ nvm install-latest-npm
|
|||||||
If you've already gotten an error to the effect of "npm does not support Node.js", you'll need to (1) revert to a previous node version (`nvm ls` & `nvm use <your latest _working_ version from the ls>`), (2) delete the newly created node version (`nvm uninstall <your _broken_ version of node from the ls>`), then (3) rerun your `nvm install` with the `--latest-npm` flag.
|
If you've already gotten an error to the effect of "npm does not support Node.js", you'll need to (1) revert to a previous node version (`nvm ls` & `nvm use <your latest _working_ version from the ls>`), (2) delete the newly created node version (`nvm uninstall <your _broken_ version of node from the ls>`), then (3) rerun your `nvm install` with the `--latest-npm` flag.
|
||||||
|
|
||||||
|
|
||||||
|
### Offline Install
|
||||||
|
|
||||||
|
If you've previously downloaded a node version (or it's still in the cache), you can install it without any network access using the `--offline` flag:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nvm install --offline 14.7.0
|
||||||
|
```
|
||||||
|
|
||||||
|
This resolves versions using only locally installed versions and cached downloads. It will not attempt to download anything. This is useful in air-gapped environments, on planes, or when you want to avoid network latency.
|
||||||
|
|
||||||
|
You can combine `--offline` with `--lts` to install the latest cached LTS version (as long as LTS aliases have been populated by a prior `nvm ls-remote --lts`):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nvm install --offline --lts
|
||||||
|
```
|
||||||
|
|
||||||
### Default Global Packages From File While Installing
|
### Default Global Packages From File While Installing
|
||||||
|
|
||||||
If you have a list of default packages you want installed every time you install a new version, we support that too -- just add the package names, one per line, to the file `$NVM_DIR/default-packages`. You can add anything npm would accept as a package argument on the command line.
|
If you have a list of default packages you want installed every time you install a new version, we support that too -- just add the package names, one per line, to the file `$NVM_DIR/default-packages`. You can add anything npm would accept as a package argument on the command line.
|
||||||
|
|||||||
132
nvm.sh
132
nvm.sh
@@ -2490,22 +2490,35 @@ nvm_download_artifact() {
|
|||||||
local COMPRESSION
|
local COMPRESSION
|
||||||
COMPRESSION="$(nvm_get_artifact_compression "${VERSION}")"
|
COMPRESSION="$(nvm_get_artifact_compression "${VERSION}")"
|
||||||
|
|
||||||
local CHECKSUM
|
|
||||||
CHECKSUM="$(nvm_get_checksum "${FLAVOR}" "${TYPE}" "${VERSION}" "${SLUG}" "${COMPRESSION}")"
|
|
||||||
|
|
||||||
local tmpdir
|
local tmpdir
|
||||||
if [ "${KIND}" = 'binary' ]; then
|
if [ "${KIND}" = 'binary' ]; then
|
||||||
tmpdir="$(nvm_cache_dir)/bin/${SLUG}"
|
tmpdir="$(nvm_cache_dir)/bin/${SLUG}"
|
||||||
else
|
else
|
||||||
tmpdir="$(nvm_cache_dir)/src/${SLUG}"
|
tmpdir="$(nvm_cache_dir)/src/${SLUG}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local TARBALL
|
||||||
|
TARBALL="${tmpdir}/${SLUG}.${COMPRESSION}"
|
||||||
|
|
||||||
|
if [ "${NVM_OFFLINE-}" = 1 ]; then
|
||||||
|
# In offline mode, use cached tarball without checksum or download
|
||||||
|
if [ -r "${TARBALL}" ]; then
|
||||||
|
nvm_err "Offline: using cached archive $(nvm_sanitize_path "${TARBALL}")"
|
||||||
|
nvm_echo "${TARBALL}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
nvm_err "Offline: no cached archive found for ${SLUG}"
|
||||||
|
return 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
local CHECKSUM
|
||||||
|
CHECKSUM="$(nvm_get_checksum "${FLAVOR}" "${TYPE}" "${VERSION}" "${SLUG}" "${COMPRESSION}")"
|
||||||
|
|
||||||
command mkdir -p "${tmpdir}/files" || (
|
command mkdir -p "${tmpdir}/files" || (
|
||||||
nvm_err "creating directory ${tmpdir}/files failed"
|
nvm_err "creating directory ${tmpdir}/files failed"
|
||||||
return 3
|
return 3
|
||||||
)
|
)
|
||||||
|
|
||||||
local TARBALL
|
|
||||||
TARBALL="${tmpdir}/${SLUG}.${COMPRESSION}"
|
|
||||||
local TARBALL_URL
|
local TARBALL_URL
|
||||||
if nvm_version_greater_than_or_equal_to "${VERSION}" 0.1.14; then
|
if nvm_version_greater_than_or_equal_to "${VERSION}" 0.1.14; then
|
||||||
TARBALL_URL="${MIRROR}/${VERSION}/${SLUG}.${COMPRESSION}"
|
TARBALL_URL="${MIRROR}/${VERSION}/${SLUG}.${COMPRESSION}"
|
||||||
@@ -3050,6 +3063,57 @@ nvm_cache_dir() {
|
|||||||
nvm_echo "${NVM_DIR}/.cache"
|
nvm_echo "${NVM_DIR}/.cache"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# args: pattern
|
||||||
|
# Lists versions available in the local cache (not yet installed).
|
||||||
|
# Returns version numbers like "v18.20.4", one per line, sorted.
|
||||||
|
nvm_ls_cached() {
|
||||||
|
local PATTERN
|
||||||
|
PATTERN="${1-}"
|
||||||
|
local CACHE_BIN_DIR
|
||||||
|
CACHE_BIN_DIR="$(nvm_cache_dir)/bin"
|
||||||
|
if [ ! -d "${CACHE_BIN_DIR}" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local NVM_OS
|
||||||
|
NVM_OS="$(nvm_get_os)"
|
||||||
|
local NVM_ARCH
|
||||||
|
NVM_ARCH="$(nvm_get_arch)"
|
||||||
|
local SUFFIX
|
||||||
|
SUFFIX="${NVM_OS}-${NVM_ARCH}"
|
||||||
|
# shellcheck disable=SC2010
|
||||||
|
command ls -1 "${CACHE_BIN_DIR}" \
|
||||||
|
| nvm_grep "^node-v.*-${SUFFIX}\$" \
|
||||||
|
| command sed "s/^node-\\(v[0-9][0-9.]*\\)-${SUFFIX}\$/\\1/" \
|
||||||
|
| nvm_grep "$(nvm_ensure_version_prefix "${PATTERN}")" \
|
||||||
|
| command sort -t. -u -k 1.2,1n -k 2,2n -k 3,3n
|
||||||
|
}
|
||||||
|
|
||||||
|
# args: pattern
|
||||||
|
# Resolves a version pattern to a single version using only locally
|
||||||
|
# installed versions and cached downloads. No network access.
|
||||||
|
nvm_offline_version() {
|
||||||
|
local PATTERN
|
||||||
|
PATTERN="${1-}"
|
||||||
|
|
||||||
|
# First try locally installed versions
|
||||||
|
local VERSION
|
||||||
|
VERSION="$(nvm_version "${PATTERN}")"
|
||||||
|
if [ "_${VERSION}" != '_N/A' ]; then
|
||||||
|
nvm_echo "${VERSION}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Then try cached downloads
|
||||||
|
VERSION="$(nvm_ls_cached "${PATTERN}" | command tail -1)"
|
||||||
|
if [ -n "${VERSION}" ]; then
|
||||||
|
nvm_echo "${VERSION}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
nvm_echo 'N/A'
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
nvm() {
|
nvm() {
|
||||||
if [ "$#" -lt 1 ]; then
|
if [ "$#" -lt 1 ]; then
|
||||||
nvm --help
|
nvm --help
|
||||||
@@ -3130,6 +3194,7 @@ nvm() {
|
|||||||
nvm_echo ' --skip-default-packages When installing, skip the default-packages file if it exists'
|
nvm_echo ' --skip-default-packages When installing, skip the default-packages file if it exists'
|
||||||
nvm_echo ' --latest-npm After installing, attempt to upgrade to the latest working npm on the given node version'
|
nvm_echo ' --latest-npm After installing, attempt to upgrade to the latest working npm on the given node version'
|
||||||
nvm_echo ' --no-progress Disable the progress bar on any downloads'
|
nvm_echo ' --no-progress Disable the progress bar on any downloads'
|
||||||
|
nvm_echo ' --offline Install from cache only, without downloading anything'
|
||||||
nvm_echo ' --alias=<name> After installing, set the alias specified to the version specified. (same as: nvm alias <name> <version>)'
|
nvm_echo ' --alias=<name> After installing, set the alias specified to the version specified. (same as: nvm alias <name> <version>)'
|
||||||
nvm_echo ' --default After installing, set default alias to the version specified. (same as: nvm alias default <version>)'
|
nvm_echo ' --default After installing, set default alias to the version specified. (same as: nvm alias default <version>)'
|
||||||
nvm_echo ' --save After installing, write the specified version to .nvmrc'
|
nvm_echo ' --save After installing, write the specified version to .nvmrc'
|
||||||
@@ -3335,11 +3400,6 @@ nvm() {
|
|||||||
local NVM_OS
|
local NVM_OS
|
||||||
NVM_OS="$(nvm_get_os)"
|
NVM_OS="$(nvm_get_os)"
|
||||||
|
|
||||||
if ! nvm_has "curl" && ! nvm_has "wget"; then
|
|
||||||
nvm_err 'nvm needs curl or wget to proceed.'
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then
|
||||||
version_not_provided=1
|
version_not_provided=1
|
||||||
fi
|
fi
|
||||||
@@ -3347,9 +3407,11 @@ nvm() {
|
|||||||
local nobinary
|
local nobinary
|
||||||
local nosource
|
local nosource
|
||||||
local noprogress
|
local noprogress
|
||||||
|
local NVM_OFFLINE
|
||||||
nobinary=0
|
nobinary=0
|
||||||
noprogress=0
|
noprogress=0
|
||||||
nosource=0
|
nosource=0
|
||||||
|
NVM_OFFLINE=0
|
||||||
local LTS
|
local LTS
|
||||||
local ALIAS
|
local ALIAS
|
||||||
local NVM_UPGRADE_NPM
|
local NVM_UPGRADE_NPM
|
||||||
@@ -3392,6 +3454,10 @@ nvm() {
|
|||||||
noprogress=1
|
noprogress=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--offline)
|
||||||
|
NVM_OFFLINE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--lts)
|
--lts)
|
||||||
LTS='*'
|
LTS='*'
|
||||||
shift
|
shift
|
||||||
@@ -3468,6 +3534,11 @@ nvm() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ "${NVM_OFFLINE}" != 1 ] && ! nvm_has "curl" && ! nvm_has "wget"; then
|
||||||
|
nvm_err 'nvm needs curl or wget to proceed.'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local provided_version
|
local provided_version
|
||||||
provided_version="${1-}"
|
provided_version="${1-}"
|
||||||
|
|
||||||
@@ -3508,8 +3579,27 @@ nvm() {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
local EXIT_CODE
|
local EXIT_CODE
|
||||||
VERSION="$(NVM_VERSION_ONLY=true NVM_LTS="${LTS-}" nvm_remote_version "${provided_version}")"
|
|
||||||
EXIT_CODE="$?"
|
if [ "${NVM_OFFLINE}" = 1 ]; then
|
||||||
|
local OFFLINE_PATTERN
|
||||||
|
OFFLINE_PATTERN="${provided_version}"
|
||||||
|
if [ -n "${LTS-}" ]; then
|
||||||
|
if [ "${LTS}" = '*' ]; then
|
||||||
|
OFFLINE_PATTERN="$(nvm_resolve_alias 'lts/*' 2>/dev/null || nvm_echo)"
|
||||||
|
else
|
||||||
|
OFFLINE_PATTERN="$(nvm_resolve_alias "lts/${LTS}" 2>/dev/null || nvm_echo)"
|
||||||
|
fi
|
||||||
|
if [ -z "${OFFLINE_PATTERN}" ]; then
|
||||||
|
nvm_err "LTS alias '${LTS}' not found locally. Run \`nvm ls-remote --lts\` first to populate LTS aliases."
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
VERSION="$(nvm_offline_version "${OFFLINE_PATTERN}")"
|
||||||
|
EXIT_CODE="$?"
|
||||||
|
else
|
||||||
|
VERSION="$(NVM_VERSION_ONLY=true NVM_LTS="${LTS-}" nvm_remote_version "${provided_version}")"
|
||||||
|
EXIT_CODE="$?"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${VERSION}" = 'N/A' ] || [ $EXIT_CODE -ne 0 ]; then
|
if [ "${VERSION}" = 'N/A' ] || [ $EXIT_CODE -ne 0 ]; then
|
||||||
local LTS_MSG
|
local LTS_MSG
|
||||||
@@ -3525,9 +3615,17 @@ nvm() {
|
|||||||
return 3
|
return 3
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
REMOTE_CMD='nvm ls-remote'
|
if [ "${NVM_OFFLINE}" = 1 ]; then
|
||||||
|
REMOTE_CMD='nvm ls'
|
||||||
|
else
|
||||||
|
REMOTE_CMD='nvm ls-remote'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "${NVM_OFFLINE}" = 1 ]; then
|
||||||
|
nvm_err "Version '${provided_version}' ${LTS_MSG-}not found locally or in cache - try \`${REMOTE_CMD}\` to browse available versions."
|
||||||
|
else
|
||||||
|
nvm_err "Version '${provided_version}' ${LTS_MSG-}not found - try \`${REMOTE_CMD}\` to browse available versions."
|
||||||
fi
|
fi
|
||||||
nvm_err "Version '${provided_version}' ${LTS_MSG-}not found - try \`${REMOTE_CMD}\` to browse available versions."
|
|
||||||
return 3
|
return 3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -3667,7 +3765,7 @@ nvm() {
|
|||||||
|
|
||||||
# skip binary install if "nobinary" option specified.
|
# skip binary install if "nobinary" option specified.
|
||||||
if [ $nobinary -ne 1 ] && nvm_binary_available "${VERSION}"; then
|
if [ $nobinary -ne 1 ] && nvm_binary_available "${VERSION}"; then
|
||||||
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" nvm_install_binary "${FLAVOR}" std "${VERSION}" "${nosource}"
|
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" NVM_OFFLINE="${NVM_OFFLINE}" nvm_install_binary "${FLAVOR}" std "${VERSION}" "${nosource}"
|
||||||
EXIT_CODE=$?
|
EXIT_CODE=$?
|
||||||
else
|
else
|
||||||
EXIT_CODE=-1
|
EXIT_CODE=-1
|
||||||
@@ -3686,7 +3784,7 @@ nvm() {
|
|||||||
nvm_err 'Installing from source on non-WSL Windows is not supported'
|
nvm_err 'Installing from source on non-WSL Windows is not supported'
|
||||||
EXIT_CODE=87
|
EXIT_CODE=87
|
||||||
else
|
else
|
||||||
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" nvm_install_source "${FLAVOR}" std "${VERSION}" "${NVM_MAKE_JOBS}" "${ADDITIONAL_PARAMETERS}"
|
NVM_NO_PROGRESS="${NVM_NO_PROGRESS:-${noprogress}}" NVM_OFFLINE="${NVM_OFFLINE}" nvm_install_source "${FLAVOR}" std "${VERSION}" "${NVM_MAKE_JOBS}" "${ADDITIONAL_PARAMETERS}"
|
||||||
EXIT_CODE=$?
|
EXIT_CODE=$?
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -4520,7 +4618,7 @@ nvm() {
|
|||||||
nvm_binary_available nvm_change_path nvm_strip_path \
|
nvm_binary_available nvm_change_path nvm_strip_path \
|
||||||
nvm_num_version_groups nvm_format_version nvm_ensure_version_prefix \
|
nvm_num_version_groups nvm_format_version nvm_ensure_version_prefix \
|
||||||
nvm_normalize_version nvm_is_valid_version nvm_normalize_lts \
|
nvm_normalize_version nvm_is_valid_version nvm_normalize_lts \
|
||||||
nvm_ensure_version_installed nvm_cache_dir \
|
nvm_ensure_version_installed nvm_cache_dir nvm_ls_cached nvm_offline_version \
|
||||||
nvm_version_path nvm_alias_path nvm_version_dir \
|
nvm_version_path nvm_alias_path nvm_version_dir \
|
||||||
nvm_find_nvmrc nvm_find_up nvm_find_project_dir nvm_tree_contains_path \
|
nvm_find_nvmrc nvm_find_up nvm_find_project_dir nvm_tree_contains_path \
|
||||||
nvm_version_greater nvm_version_greater_than_or_equal_to \
|
nvm_version_greater nvm_version_greater_than_or_equal_to \
|
||||||
|
|||||||
38
test/fast/Unit tests/nvm install --offline
Normal file
38
test/fast/Unit tests/nvm install --offline
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die () { echo "$@" ; exit 1; }
|
||||||
|
|
||||||
|
\. ../../../nvm.sh
|
||||||
|
|
||||||
|
\. ../../common.sh
|
||||||
|
|
||||||
|
# Mock nvm_download to ensure no network access
|
||||||
|
nvm_download() {
|
||||||
|
die "nvm_download should not be called in offline mode"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --offline with an already-installed version should succeed
|
||||||
|
INSTALLED_VERSION="$(nvm ls | command tail -1 | command awk '{print $1}' | command sed 's/\x1b\[[0-9;]*m//g')"
|
||||||
|
if [ -n "${INSTALLED_VERSION}" ] && [ "_${INSTALLED_VERSION}" != '_N/A' ] && [ "_${INSTALLED_VERSION}" != '_system' ]; then
|
||||||
|
try nvm install --offline "${INSTALLED_VERSION}"
|
||||||
|
[ "_$CAPTURED_EXIT_CODE" = "_0" ] \
|
||||||
|
|| die "nvm install --offline with installed version '${INSTALLED_VERSION}' should succeed, got exit code $CAPTURED_EXIT_CODE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --offline with a nonexistent version should fail
|
||||||
|
try_err nvm install --offline 999.999.999
|
||||||
|
[ "_$CAPTURED_EXIT_CODE" != "_0" ] \
|
||||||
|
|| die "nvm install --offline with nonexistent version should fail"
|
||||||
|
|
||||||
|
EXPECTED_ERR="not found locally or in cache"
|
||||||
|
nvm_echo "$CAPTURED_STDERR" | nvm_grep -q "${EXPECTED_ERR}" \
|
||||||
|
|| die "nvm install --offline error should mention 'not found locally or in cache'; got '$CAPTURED_STDERR'"
|
||||||
|
|
||||||
|
# --offline should not require curl or wget
|
||||||
|
nvm_has() { return 1; }
|
||||||
|
try_err nvm install --offline 999.999.999
|
||||||
|
# Should fail with "not found" not "nvm needs curl or wget"
|
||||||
|
nvm_echo "$CAPTURED_STDERR" | nvm_grep -q "curl or wget" \
|
||||||
|
&& die "nvm install --offline should not require curl or wget"
|
||||||
|
alias nvm_has='\nvm_has'
|
||||||
|
unset -f nvm_has
|
||||||
39
test/fast/Unit tests/nvm_offline_version
Normal file
39
test/fast/Unit tests/nvm_offline_version
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die () { echo "$@" ; cleanup ; exit 1; }
|
||||||
|
|
||||||
|
\. ../../../nvm.sh
|
||||||
|
|
||||||
|
\. ../../common.sh
|
||||||
|
|
||||||
|
TEST_DIR="$(pwd)/nvm_offline_version_tmp"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "${TEST_DIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
[ ! -e "${TEST_DIR}" ] && mkdir -p "${TEST_DIR}"
|
||||||
|
|
||||||
|
# nvm_offline_version should find installed versions
|
||||||
|
INSTALLED_VERSION="$(nvm_version node)"
|
||||||
|
if [ "_${INSTALLED_VERSION}" != '_N/A' ] && [ "_${INSTALLED_VERSION}" != '_system' ]; then
|
||||||
|
try nvm_offline_version "${INSTALLED_VERSION}"
|
||||||
|
[ "_$CAPTURED_STDOUT" = "_${INSTALLED_VERSION}" ] \
|
||||||
|
|| die "nvm_offline_version '${INSTALLED_VERSION}' should return '${INSTALLED_VERSION}'; got '$CAPTURED_STDOUT'"
|
||||||
|
[ "_$CAPTURED_EXIT_CODE" = "_0" ] \
|
||||||
|
|| die "nvm_offline_version '${INSTALLED_VERSION}' should exit 0; got '$CAPTURED_EXIT_CODE'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# nvm_offline_version with nonexistent version should return N/A
|
||||||
|
try nvm_offline_version "999.999.999"
|
||||||
|
[ "_$CAPTURED_STDOUT" = "_N/A" ] \
|
||||||
|
|| die "nvm_offline_version '999.999.999' should return 'N/A'; got '$CAPTURED_STDOUT'"
|
||||||
|
[ "_$CAPTURED_EXIT_CODE" = "_3" ] \
|
||||||
|
|| die "nvm_offline_version '999.999.999' should exit 3; got '$CAPTURED_EXIT_CODE'"
|
||||||
|
|
||||||
|
# nvm_ls_cached with nonexistent pattern should return nothing
|
||||||
|
try nvm_ls_cached "999.999"
|
||||||
|
[ -z "$CAPTURED_STDOUT" ] \
|
||||||
|
|| die "nvm_ls_cached '999.999' should return empty; got '$CAPTURED_STDOUT'"
|
||||||
|
|
||||||
|
cleanup
|
||||||
Reference in New Issue
Block a user