Skip to content

Commit 03cd4d7

Browse files
authored
Remove credential filling (#11)
2 parents 37de175 + b0df8b8 commit 03cd4d7

File tree

10 files changed

+185
-362
lines changed

10 files changed

+185
-362
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ $ venv install requirements.txt
105105
```
106106
are equivalent.
107107

108-
The installed packages are then _locked_ into the corresponding `.lock`-file, e.g. running `venv install dev-requirements.txt` will lock those installed packages into `dev-requirements.lock`.
108+
The installed packages are then _locked_ into the corresponding `.lock`-file, e.g. running `venv install dev-requirements.txt` will lock those installed packages into `dev-requirements.lock`[^1].
109109

110110
Installing packages this way makes sure that they are tracked, since installing them with `pip install` will keep no record of which packages have been installed in the environment, making it difficult to reproduce later on.
111111

@@ -126,7 +126,7 @@ matplotlib
126126
The `-r requirements.txt` will make sure that installing development requirements also install production requirements.
127127

128128
## Reproducing environment
129-
To install a reproducible environment, you need to install from a `.lock`-file, since those have all versions of all requirements locked. From a clean environment (no packages installed yet), run
129+
To install a reproducible environment, you need to install from a `.lock`-file, since those have all versions of all requirements locked[^1]. From a clean environment (no packages installed yet), run
130130
```console
131131
$ venv install requirements.lock
132132
```
@@ -185,3 +185,6 @@ Releases are made by creating a branch `release/vX.X.X` from `develop`, where `X
185185
## License
186186

187187
[MIT](https://choosealicense.com/licenses/mit/)
188+
189+
[^1]: A current limitation of using `pip freeze` under the hood is that installing packages from a version control system (VCS) URL that requires authentication, e.g. `private_package @ git+https://USERNAME:[email protected]/my-user/private-package`, the authentication is not locked (see https://github.com/pypa/pip/issues/12365).
190+
These credentials can either be inserted manually into the generated `.lock`-file, or the credentials can instead be stored in a `.netrc` file, which `pip install` will then reference when running `pip install`: https://pip.pypa.io/en/stable/topics/authentication/#netrc-support

src/venv-cli/completions/bash/venv_completion.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,18 @@ _venv() {
3939
COMPREPLY+=( ${python_versions[*]} )
4040
COMPREPLY+=( ${help_options[*]} )
4141
;;
42-
"install"|"lock")
42+
"install")
4343
# Generate completions for requirement and lock file paths
4444
COMPREPLY+=( $(compgen -f -X '!(*.txt|*.lock)' -- "${cur_word}" | sort) )
4545
COMPREPLY+=( ${help_options[*]} )
4646
compopt -o plusdirs +o nosort # Add directories after generated completions
4747
;;
48+
"lock")
49+
# Generate completions for lock file paths
50+
COMPREPLY+=( $(compgen -f -X '!(*.lock)' -- "${cur_word}" | sort) )
51+
COMPREPLY+=( ${help_options[*]} )
52+
compopt -o plusdirs +o nosort # Add directories after generated completions
53+
;;
4854
"sync")
4955
# Generate completions for lock file paths
5056
COMPREPLY+=( $(compgen -f -X '!*.lock' -- "${cur_word}" | sort) )
@@ -62,12 +68,6 @@ _venv() {
6268
# Nothing to generate
6369
;;
6470
esac
65-
66-
# Special case for 'venv lock requirements.txt <TAB>', where only *.lock files should be suggested
67-
if [ "${COMP_WORDS[COMP_CWORD-2]}" == "lock" ] && [[ "${prev_word}" =~ ^.*\.txt$ ]]; then
68-
COMPREPLY+=( $(compgen -f -X '!*.lock' -- "${cur_word}" | sort) )
69-
compopt -o plusdirs +o nosort # Add directories after generated completions
70-
fi
7171
}
7272

7373
complete -F _venv venv

src/venv-cli/venv.sh

Lines changed: 8 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -227,105 +227,41 @@ venv::install() {
227227
}
228228

229229

230-
venv::_fill_credentials() {
231-
### Read VCS URL auth from requirements file and fill in lock file URLs
232-
local requirements_file="$1"
233-
local lock_file="$2"
234-
235-
# Loop over every line in requirements file
236-
while IFS= read -r req_line; do
237-
# Extract package name from requirement
238-
local package=$(echo "${req_line}" | command awk '{print $1}')
239-
# Extract VCS URL from requirement
240-
local url_req=$(echo "${req_line}" | command awk '{print $3}')
241-
242-
# Extract environment variable(s) from the URL
243-
local env_vars=$(echo "${url_req}" | command grep -oE "${_env_var_auth_pattern}")
244-
if [ -z "${env_vars}" ]; then
245-
# No env vars in the url (or no url at all), skip line
246-
continue
247-
fi
248-
249-
# Use sed to fill in $env_vars after "https://"-section, so a line like
250-
# 'asdf-lib @ git+https://github.com/someuser/asdf-lib@commithash'
251-
# becomes e.g.
252-
# 'asdf-lib @ git+https://${AUTH_TOKEN}@github.com/someuser/asdf-lib@commithash'
253-
# or
254-
# 'asdf-lib @ git+https://${USERNAME}:${PASSWORD}@github.com/someuser/asdf-lib@commithash'
255-
local before_auth_pattern="^(${package} @ [a-zA-Z+]*?https?://)"
256-
local after_auth_pattern="(.*?)$"
257-
local fill_pattern="\1${env_vars}@\2"
258-
command sed -i -E "s|${before_auth_pattern}${after_auth_pattern}|${fill_pattern}|" "${lock_file}"
259-
260-
done < "${requirements_file}"
261-
}
262-
263-
264230
venv::lock() {
265231
if venv::_check_if_help_requested "$1"; then
266232
echo "venv lock [<lock file>|<lock file prefix>]"
267-
echo "venv lock <requirements file> <lock file>"
268233
echo
269234
echo "Lock all installed package versions and write them to <lock file>."
270235
echo "The <lock file> must be in the form '*requirements.lock'."
271236
echo
272237
echo "If <lock file prefix> is specified instead, locks the requirements to"
273-
echo "a file called '<lock file prefix>-requirements.lock'."
238+
echo "a file called '<lock file prefix>-requirements.lock', e.g."
239+
echo "'venv lock dev' locks requirements to 'dev-requirements.lock'."
274240
echo
275241
echo "If no <lock file> is specified, defaults to 'requirements.lock'."
276242
echo
277-
echo "This function uses 'pip freeze' to lock the requirements, but since"
278-
echo "'pip freeze' does not include the auth-part of VCS URLs, this command"
279-
echo "needs a reference 'requirements.txt'-file to extract the credentials from."
280-
echo
281-
echo "In the first form, where only <lock file> is specified, this command"
282-
echo "looks for a reference <requirements file> with the same stem as <lock file>,"
283-
echo "and with a '.txt' extension, e.g. 'venv lock dev-requirements.lock' will look"
284-
echo "for a reference file 'dev-requirements.txt'."
285-
echo
286-
echo "In the second form, both the reference <requirements file> and the <lock file>"
287-
echo "are specified."
288-
echo
289243
echo "Examples:"
290244
echo "$ venv lock"
291-
echo "This will lock requirements into 'requirements.lock',"
292-
echo "referencing 'requirements.txt'."
245+
echo "This will lock requirements into 'requirements.lock'."
293246
echo
294247
echo "$ venv lock dev-requirements.lock"
295-
echo "This will lock requirements into 'dev-requirements.lock',"
296-
echo "referencing 'dev-requirements.txt'."
248+
echo "This will lock requirements into 'dev-requirements.lock'."
297249
echo
298-
echo "$ venv lock dev"
299-
echo "This will lock requirements into 'dev-requirements.lock',"
300-
echo "referencing 'dev-requirements.txt'."
250+
echo "$ venv lock ci"
251+
echo "This will lock requirements into 'ci-requirements.lock'."
301252
return "${_success}"
302253
fi
303254

304-
local requirements_file
305255
local lock_file
306256
if [ -z "$1" ]; then
307-
# If nothing was passed, default to "requirements.lock" with "requirements.txt"
308-
# as reference
257+
# If nothing was passed, default to "requirements.lock"
309258
lock_file="requirements.lock"
310-
requirements_file="requirements.txt"
311259

312260
elif [[ "$1" = *"."* ]]; then
313261
# If first argument looks like a file name ...
314-
315262
if venv::_check_lock_requirements_file "$1" -q; then
316-
# In this case, the first argument is a lock file
263+
# ... and is a lock file
317264
lock_file="$1"
318-
requirements_file="$(venv::_get_requirements_from_lock "$1")"
319-
shift
320-
321-
elif $(venv::_check_install_requirements_file "$1" -q \
322-
&& venv::_check_lock_requirements_file "$2" -q); then
323-
# In this case, the first argument is a requirements file and the second
324-
# argument is a lock file
325-
requirements_file="$1"
326-
lock_file="$2"
327-
shift 2
328-
329265
else
330266
venv::raise "Input file(s) had wrong format. See 'venv lock --help' for more info."
331267
return "$?"
@@ -334,23 +270,11 @@ venv::lock() {
334270
else
335271
# If first argument is not a full filename, assume it is a lock file prefix
336272
lock_file="$1-requirements.lock"
337-
requirements_file="$1-requirements.txt"
338-
fi
339-
340-
if [ ! -f "${requirements_file}" ]; then
341-
venv::raise "No reference requirements file found with name '${requirements_file}', aborting."
342-
return "$?"
343273
fi
344274

345275
# Write locked requirements into lock file
346276
pip freeze --require-virtualenv > "${lock_file}"
347277

348-
# Since 'pip freeze' does not include the auth-part of VCS URLs, we have to
349-
# get those from the reference requirements file
350-
if ! venv::_fill_credentials "${requirements_file}" "${lock_file}"; then
351-
return "${_fail}"
352-
fi
353-
354278
venv::color_echo "${_green}" "Locked requirements in ${lock_file}"
355279
}
356280

tests/helpers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import os
22
import subprocess
33
import sys
4+
from functools import wraps
45
from pathlib import Path
6+
from typing import Callable
7+
8+
from tests.types import P, RawFilesDict, RequirementsBase, RequirementsDict
59

610
RequirementFiles = dict[str, Path]
711
current_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
@@ -31,3 +35,20 @@ def run_command(commands: str | list[str], cwd: Path = Path.cwd(), activated: bo
3135

3236
result = subprocess.run(all_commands, cwd=cwd)
3337
result.check_returncode()
38+
39+
40+
def collect_requirements(
41+
func: Callable[P, tuple[RawFilesDict, RequirementsBase]]
42+
) -> Callable[P, tuple[RequirementsDict, RequirementsBase]]:
43+
@wraps(func)
44+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> tuple[RequirementsDict, RequirementsBase]:
45+
files_dict, requirements_base = func(*args, **kwargs)
46+
requirements_dict = {filename: "\n".join(requirements) for filename, requirements in files_dict.items()}
47+
return requirements_dict, requirements_base
48+
49+
return wrapper
50+
51+
52+
def write_files(files: RequirementsDict, dir: Path) -> None:
53+
for filename, contents in files.items():
54+
(dir / filename).write_text(contents + "\n")

tests/test_venv_fill_credentials.py

Lines changed: 0 additions & 125 deletions
This file was deleted.

0 commit comments

Comments
 (0)