-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Keyring backend for pip auth with ADO feeds #54
Conversation
src/artifacts-helper/codespaces_artifacts_helper_keyring/noxfile.py
Outdated
Show resolved
Hide resolved
src/artifacts-helper/codespaces_artifacts_helper_keyring/pyproject.toml
Outdated
Show resolved
Hide resolved
...artifacts_helper_keyring/src/codespaces_artifacts_helper_keyring/artifacts_helper_wrapper.py
Outdated
Show resolved
Hide resolved
...artifacts_helper_keyring/src/codespaces_artifacts_helper_keyring/artifacts_helper_wrapper.py
Outdated
Show resolved
Hide resolved
...despaces_artifacts_helper_keyring/src/codespaces_artifacts_helper_keyring/keyring_backend.py
Show resolved
Hide resolved
src/artifacts-helper/codespaces_artifacts_helper_keyring/tests/test_artifacts_helper_wrapper.py
Outdated
Show resolved
Hide resolved
src/artifacts-helper/codespaces_artifacts_helper_keyring/README.md
Outdated
Show resolved
Hide resolved
Creating a release and installing from that makes the most sense. Will dependencies auto install? Is this installed globally or specific to the repo that is being worked on? As you said, we do not want to pollute anyone's repository.
I would skip this. Especially if it lets you lose any dependencies. In npm we just use "codespaces" as the username. |
Also renames the test files to correspond to the exercised source file.
Add rules to detect lines exceeding max length, missing docstrings, etc. See diff and ruff rules for more details.
Sounds good! Let's go with that.
Yep, I've specified the dependencies in the package metadata and the package manager will download them. These are the depdencies: we use $ pkginfo -f requires_dist dist/*.whl
requires_dist: ['jaraco-classes >=3.0.0', 'keyring >=20.0.0', 'requests >=2.20.0']
It can be installed globally to provide ADO auth for the entire codespace. If the user only wants authentication for a specific project, I believe they can create a virtual environment and only install our keyring backend inside that environment. Maybe we could create a script similar to # Install globally
install-pip-keyring.sh
# Or, install in only one project
python -m venv venv
. venv/activate
install-pip-keyring.sh
🫡 I've removed the jwt parsing (yay, one less dependency) and used |
I do not think we need to complicate this. Codespaces is a very specific scenario and this feature is optional, and including Python support will be an optional setting of the feature. So if someone installs this feature AND says to setup pip then I think we can safely just do that. As long as a global installation is likely to work then that is likely what we should do. When the |
Sounds good!
If we install it as the root user, it will work system-wide. However, it's generally best practice to avoid installing pip packages as root, explained by this warning when running pip operations as root:
There's a risk of conflict between our package's dependencies installed via pip and those installed by the system's package manager. For instance, the 'requests' package is available both as a Debian package and a pip package. If both are installed, pip could overwrite the one installed with So, I think it's best if we stick to running the install for the remote user only and leave the root environment alone (unless the consumer of the devcontainer feature explicitly specifies |
I think next steps are to get this merged. I would like to see a workflow added that builds the package and uploads it as an artifact to the build. I can help with the latter if you need it but having a workflow do the build would be best if you do. Once we have this I will just manually add a Release with the build attached. Then I can start working on changing the feature to download and install from the release as the user. |
This also modifies the scripts used to run the linting and formatting tasks.
I've added a CI workflow for linting, formatting, testing, etc for now. I'll work on the release workflow this week! |
This adds a workflow to build the Python package when a tag matching `feature_artifacts-helper_*` is created. It will upload the build artifacts (`tar.gz` sdist and `.whl` bdist). There is also the option of automatically creating a release and attaching the build artifacts to it. See eebf0a5 and https://github.com/delilahw/codespace-features/releases/tag/feature_artifacts-helper_1.1-alpha for an example.
Okay done! I've added a release workflow in This might be handy, here's the process I'm using to install and test it: # Fetch artifact from GitHub releases
WHEEL_URL=$(
curl -L \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/delilahw/codespace-features/releases/latest |
jq -r 'first( .assets[] | select( .name | endswith("whl") ) ).browser_download_url'
)
wget -O package.whl "$WHEEL_URL"
# The wheel package is required to install wheels
python3 -m pip install wheel
# Install the package and its dependencies
python3 -m pip install package.whl
# Delete wheel file
rm package.whl
# Connect to Azure feed and download something
python3 -m pip download yapf -i "https://pkgs.dev.azure.com/<org_name>/_packaging/<feed_name>/pypi/simple" |
…isort Previously, when we imported our own module `codespaces_artifacts_helper_keyring` in tests, it was not being detected by ruff (linter) as a first-party module. This affected the linter's ability to accurately separate import blocks. We fix the issue in this commit by including the `/test` directory in ruff's `src`s. Now, the linter will put our own modules in a separate first-party import block, as expected.
I'm pretty happy with the changes now, so I'm taking the PR out of draft. Let me know if there's any more feedback y'all have, @markphip and team! Also @embetten, who's maintaining artifacts-keyring Thanks all 😇 |
This PR implements a custom keyring backend that interfaces with
~/ado-auth-helper
to provide authentication when pip needs to access an ADO feed. It is in the form of a Python package, namedcodespaces_artifacts_helper_keyring
(open to renaming it).Background
The
artifacts-helper
codespace feature provides authentication helpers for npm, yarn, etc. For many of these package managers, we simply use shell alises and environment variable substitution to inject the authentication tokens prior to calling the underlying package manager. However, this approach does not work with pip, namely because it has multiple entrypoints (e.g.python -m pip
) and not all of them will respect shell aliases.Luckily, pip has builtin support for keyring. Keyring is a framework that supports providing multiple authentication backends. In this PR, we implement a custom keyring backend to be used against ADO package feeds when the
~/ado-auth-helper
tool is detected.Inner Workings
The package works by providing a custom
keyring.backend.KeyringBackend
. This will automatically be picked up bypip
and thekeyring
package when ourcodespaces_artifacts_helper_keyring
is installed.When our backend is called we:
dev.azure.com
, etc?)~/ado-auth-helper
is executable.Usage
Usage on the target machine will require these prerequisites:
a. https://github.com/microsoft/ado-codespaces-auth is installed and
~/ado-auth-helper
is executableb.
keyring
is installedc. the feed being accessed has a domain matching dev.azure.com, etc
To install the
codespaces_artifacts_helper_keyring
package from source:To use the keyring backend
Testing
I've included some unit tests that are based on existing tests over at https://github.com/microsoft/artifacts-keyring. I've also added tests to exercise our process of calling the auth helper tool. The tests would be parameterized against multiple Python versions using
nox
.Assumptions
I've assumed that the returned credentials will always be a valid JWT with the payload containing the username inupn
orunique_name
. Although, we can probably just pick another username to authenticate against ADO and it will still work.~/ado-auth-helper
is always the location of the helper tool.Considerations
Packaging and Distribution
We considered these alternatives and we've settled on GitHub releases for now.
build and install from sourcebuild the package on CI and release it on PyPI:this might be considered a separate project and we might have to get approval for the release of a new publicly available pip package