anancarv / python-artifactory Goto Github PK
View Code? Open in Web Editor NEWTyped interactions with the Jfrog Artifactory REST API
License: MIT License
Typed interactions with the Jfrog Artifactory REST API
License: MIT License
Describe the bug
pip-compile throws dependency errors for pyartifactory 1.8.0
To Reproduce
Steps to reproduce the behavior:
requirements.in
which contains pyartifactory==1.8.0
pip-compile requirements.in
Could not find a version that matches typing-extensions<4.0.0,>=3.7.4,>=4.1.0 (from pyartifactory==1.8.0->-r requirements.in (line 1))
Tried: 3.6.2, 3.6.2, 3.6.2.1, 3.6.2.1, 3.6.5, 3.6.5, 3.6.6, 3.6.6, 3.7.2, 3.7.2, 3.7.4, 3.7.4, 3.7.4.1, 3.7.4.1, 3.7.4.2, 3.7.4.2, 3.7.4.3, 3.7.4.3, 3.10.0.0, 3.10.0.0, 3.10.0.1, 3.10.0.1, 3.10.0.2, 3.10.0.2, 4.0.0, 4.0.0, 4.0.1, 4.0.1, 4.1.0, 4.1.0, 4.1.1, 4.1.1, 4.2.0, 4.2.0, 4.3.0, 4.3.0
There are incompatible versions in the resolved dependencies:
typing-extensions>=4.1.0 (from pydantic==1.10.1->pyartifactory==1.8.0->-r requirements.in (line 1))
typing_extensions<4.0.0,>=3.7.4 (from pyartifactory==1.8.0->-r requirements.in (line 1))
Expected behavior
Dependency graph is built without errors.
Environment:
requirements.in
From the mypy documentation,
If you would like to publish a library package to a package repository (e.g. PyPI) for either internal or external use in type checking, packages that supply type information via type comments or annotations in the code should put a
py.typed
file in their package directory.
That will tell users' mypy that the lib has typings and that they should be checked.
Not sure how it would work with Poetry when publishing the package though, the documentation shows how to use setup.py to do it.
tox
(or Travis build matrix to test on multiple Python versions)mypy
(for now it isn't used ^^')Add more if you can 😉
I'm struggling with the copy/move example. Could you make it more precise.
The artifacts.info
is described as art.artifacts.info("<ARTIFACT_PATH_IN_ARTIFACTORY>")
,
which works when using:
art.artifacts.info(f"{repository_name}/{user_name}/{package_name}/{version}")
However, the copy/move operation is also described with <CURRENT_ARTIFACT_PATH_IN_ARTIFACTORY>
and <NEW_ARTIFACT_PATH_IN_ARTIFACTORY>
but does not work when using it via:
art.artifacts.move(
"{repository_name_1}/{user_name}/{package_name}/{version}",
"{repository_name_2}/{user_name}/{package_name}/{version}",
dryrun=True
)
Describe the solution you'd like
A few things:
Describe the bug
When creating a remote repository, setting xrayIndex=True as the parameter does not take into effect.
To Reproduce
I used flowing code to create a remote repo in jfrog, have set xrayIndex=True, but checked in the jfrog ui and found that "Enable Indexing In Xray" was not enabled.
remote_repo = RemoteRepository(key="test-1", url="https://registry.npmjs.org", packageType="npm", xrayIndex=True)
new_remote_repo = art.repositories.create_repo(remote_repo)
Expected behavior
This parameter setting of xrayIndex should be working on the remote repos. Creating a local repository does not have this issue.
Possible Fix
I have added the last line to https://github.com/anancarv/python-artifactory/blob/master/pyartifactory/models/repository.py
in the Class RemoteRepository(BaseRepositoryModel):
class RemoteRepository(BaseRepositoryModel):
"""Models a remote Repository."""
rclass: Literal[RClassEnum.remote] = RClassEnum.remote
url: str
username: Optional[str] = None
...
nuget: Nuget = Nuget()
xrayIndex: bool = False # line added
After added the last line, it worked.
Environment:
Is your feature request related to a problem? Please describe.
pydantic v2 is released, but this project has an upper bound on pydantic<2.0.
This leads to incompatibilities with other dependencies. Plus, pydantic v2 is faster than v1, therefore a migration is a good idea.
Describe the solution you'd like
Support either v1 and v2 or migrate fully to v2, depending on the complexity of migration.
Note that supporting both versions at the same time would be cumbersome, due to many deprecations in v2.
This also requires bumping email-validator to v2 and due to that dropping support for Python 3.6
Thanks for your work. It's one of the best artifactory library. It would be great if it also would contain access token DML features
Is your feature request related to a problem? Please describe.
Artifacts deployed with the deploy
method end up with the following warning in the Artifactory web interface:
Client did not publish a checksum value.
If you trust the uploaded artifact you can accept the actual checksum by clicking the 'Fix Checksum' button.
Describe the solution you'd like
I want all three checksums (SHA-256, SHA-1, MD5) to be provided and checked.
Describe alternatives you've considered
Don't use python-artifactory, just upload directory with HTTP
I am willing to develop that feature, the question I have is the project active and accepts pull requests?
Describe the bug
Can`t communicate with artifactory
Expected behavior
The script downloads the file to the given location
Environment:
Environment:
Also, using the artifactory URL and the credentials(username, password) in a browser, the download works without problem.
At the company the same script runs without problem on other machines, so the problem is most likely machine specific.
Hi when i run this statement:
from pyartifactory import Artifactory
art = Artifactory(url="ARTIFACTORY_URL", auth=('USERNAME','PASSWORD_OR_API_KEY'), api_version=1)
It shows 400 client error. When I visit http://my-artifactory/api/security/uers according to the URL where the error occurred, the final result shows:
"This REST API is available only in Artifactory Pro (see: jfrog.com/artifactory/features). If you are already running Artifactory Pro please make sure your server is activated with a valid license key.\n"
Is this because my artifactory website doesn't have this feature? But I use dohq-artifactory (which is also a python third-party library), but it is used normally, and can be accessed normally according to the apikey of the artifactory, such as uploading and downloading the artifactory.
Please add the ability to interact with a repository for Terraform packages, for storing/retrieving/etc. Terraform modules.
When I try to create a new nuget remote repo, I see the following error in Artifactory logs:
Failed creating/replacing repository. Reason: NuGet Repository configuration is missing mandatory field downloadContextPath
java.lang.IllegalArgumentException: NuGet Repository configuration is missing mandatory field downloadContextPath
You can see here that the JSON does not nest the nuget properties:
Using this inherited class fixed my issue:
class RemoteRepositoryFixed(RemoteRepository):
def dict(self):
d = RemoteRepository.dict(self)
d['downloadContextPath'] = self.nuget.downloadContextPath
d['feedContextPath'] = self.nuget.feedContextPath
d['v3FeedUrl'] = self.nuget.v3FeedUrl
return d
By the way, thanks for this project!! It looks like a lot of work went into it, and it's been enjoyable to use.
Describe the bug
Got "pydantic.error_wrappers.ValidationError" when called arti.repositories.get_repo(key) for Conan repository
To Reproduce
arti = Artifactory(url=your_instance_url, auth=(username, password))
for r in arti.repositories.list():
print(r)
print(arti.repositories.get_repo(r.key))
Traceback (most recent call last):
File "~/.local/lib/python3.8/site-packages/pyartifactory/objects.py", line 439, in get_repo
repo: AnyRepositoryResponse = parse_obj_as(
File "pydantic/tools.py", line 35, in pydantic.tools.parse_obj_as
File "pydantic/main.py", line 362, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 6 validation errors for ParsingModel[Union[pyartifactory.models.repository.LocalRepositoryResponse, pyartifactory.models.repository.VirtualRepositoryResponse, pyartifactory.models.repository.RemoteRepositoryResponse]]
__root__ -> packageType
value is not a valid enumeration member; permitted: 'maven', 'gradle', 'ivy', 'sbt', 'helm', 'cocoapods', 'opkg', 'rpm', 'nuget', 'cran', 'gems', 'npm', 'bower', 'debian', 'pypi', 'docker', 'yum', 'vcs', 'composer', 'go', 'p2', 'chef', 'puppet', 'generic' (type=type_error.enum; enum_values=[<PackageTypeEnum.maven: 'maven'>, <PackageTypeEnum.gradle: 'gradle'>, <PackageTypeEnum.ivy: 'ivy'>, <PackageTypeEnum.sbt: 'sbt'>, <PackageTypeEnum.helm: 'helm'>, <PackageTypeEnum.cocoapods: 'cocoapods'>, <PackageTypeEnum.opkg: 'opkg'>, <PackageTypeEnum.rpm: 'rpm'>, <PackageTypeEnum.nuget: 'nuget'>, <PackageTypeEnum.cran: 'cran'>, <PackageTypeEnum.gems: 'gems'>, <PackageTypeEnum.npm: 'npm'>, <PackageTypeEnum.bower: 'bower'>, <PackageTypeEnum.debian: 'debian'>, <PackageTypeEnum.pypi: 'pypi'>, <PackageTypeEnum.docker: 'docker'>, <PackageTypeEnum.yum: 'yum'>, <PackageTypeEnum.vcs: 'vcs'>, <PackageTypeEnum.composer: 'composer'>, <PackageTypeEnum.go: 'go'>, <PackageTypeEnum.p2: 'p2'>, <PackageTypeEnum.chef: 'chef'>, <PackageTypeEnum.puppet: 'puppet'>, <PackageTypeEnum.generic: 'generic'>])
__root__ -> rclass
unexpected value; permitted: <RClassEnum.virtual: 'virtual'> (type=value_error.const; given=local; permitted=(<RClassEnum.virtual: 'virtual'>,))
__root__ -> packageType
value is not a valid enumeration member; permitted: 'maven', 'gradle', 'ivy', 'sbt', 'helm', 'cocoapods', 'opkg', 'rpm', 'nuget', 'cran', 'gems', 'npm', 'bower', 'debian', 'pypi', 'docker', 'yum', 'vcs', 'composer', 'go', 'p2', 'chef', 'puppet', 'generic' (type=type_error.enum; enum_values=[<PackageTypeEnum.maven: 'maven'>, <PackageTypeEnum.gradle: 'gradle'>, <PackageTypeEnum.ivy: 'ivy'>, <PackageTypeEnum.sbt: 'sbt'>, <PackageTypeEnum.helm: 'helm'>, <PackageTypeEnum.cocoapods: 'cocoapods'>, <PackageTypeEnum.opkg: 'opkg'>, <PackageTypeEnum.rpm: 'rpm'>, <PackageTypeEnum.nuget: 'nuget'>, <PackageTypeEnum.cran: 'cran'>, <PackageTypeEnum.gems: 'gems'>, <PackageTypeEnum.npm: 'npm'>, <PackageTypeEnum.bower: 'bower'>, <PackageTypeEnum.debian: 'debian'>, <PackageTypeEnum.pypi: 'pypi'>, <PackageTypeEnum.docker: 'docker'>, <PackageTypeEnum.yum: 'yum'>, <PackageTypeEnum.vcs: 'vcs'>, <PackageTypeEnum.composer: 'composer'>, <PackageTypeEnum.go: 'go'>, <PackageTypeEnum.p2: 'p2'>, <PackageTypeEnum.chef: 'chef'>, <PackageTypeEnum.puppet: 'puppet'>, <PackageTypeEnum.generic: 'generic'>])
__root__ -> rclass
unexpected value; permitted: <RClassEnum.remote: 'remote'> (type=value_error.const; given=local; permitted=(<RClassEnum.remote: 'remote'>,))
__root__ -> packageType
value is not a valid enumeration member; permitted: 'maven', 'gradle', 'ivy', 'sbt', 'helm', 'cocoapods', 'opkg', 'rpm', 'nuget', 'cran', 'gems', 'npm', 'bower', 'debian', 'pypi', 'docker', 'yum', 'vcs', 'composer', 'go', 'p2', 'chef', 'puppet', 'generic' (type=type_error.enum; enum_values=[<PackageTypeEnum.maven: 'maven'>, <PackageTypeEnum.gradle: 'gradle'>, <PackageTypeEnum.ivy: 'ivy'>, <PackageTypeEnum.sbt: 'sbt'>, <PackageTypeEnum.helm: 'helm'>, <PackageTypeEnum.cocoapods: 'cocoapods'>, <PackageTypeEnum.opkg: 'opkg'>, <PackageTypeEnum.rpm: 'rpm'>, <PackageTypeEnum.nuget: 'nuget'>, <PackageTypeEnum.cran: 'cran'>, <PackageTypeEnum.gems: 'gems'>, <PackageTypeEnum.npm: 'npm'>, <PackageTypeEnum.bower: 'bower'>, <PackageTypeEnum.debian: 'debian'>, <PackageTypeEnum.pypi: 'pypi'>, <PackageTypeEnum.docker: 'docker'>, <PackageTypeEnum.yum: 'yum'>, <PackageTypeEnum.vcs: 'vcs'>, <PackageTypeEnum.composer: 'composer'>, <PackageTypeEnum.go: 'go'>, <PackageTypeEnum.p2: 'p2'>, <PackageTypeEnum.chef: 'chef'>, <PackageTypeEnum.puppet: 'puppet'>, <PackageTypeEnum.generic: 'generic'>])
__root__ -> url
field required (type=value_error.missing)
Expected behavior
No error occurred
Environment:
Additional context
print(r) outputs
key='xxx-conan-local' type='LOCAL' description='yyy' url='https://zzz:443/artifactory/xxx-conan-local' packageType='Conan'
Describe the bug
I've been using this library to create repositories for my Artifactory Instance. I started adding a RemoteRepository for docker and came upon this error that states: "Object of type SecretStr is not JSON serializable"
To Reproduce
Steps to reproduce the behavior:
from pyartifactory import Artifactory
from pyartifactory.models import RemoteRepository
url = <url to artifactory instance>
auth = (<username>, <password>)
remote_docker_repo_cfg = {
"key": "docker-remote",
"url": "https://registry-1.docker.io/",
"packageType": "docker",
"username": <dockerhub username>,
"password": <dockerhub_password>,
}
rt = Artifactory(url=url, auth=auth)
remote_repo = RemoteRepository(**remote_docker_repo_cfg)
rt.repositories.create_repo(remote_repo)
This sequence of events will cause the following error to be thrown:
Repository docker-remote does not exist
Traceback (most recent call last):
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 434, in get_repo
response = self._get(f"api/{self._uri}/{repo_name}")
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 97, in _get
return self._generic_http_method_request("get", route, **kwargs)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 142, in _generic_http_method_request
response.raise_for_status()
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/models.py", line 943, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: http://artifactory.prod.vyopta.com:8082/artifactory/api/repositories/docker-remote
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 460, in create_repo
self.get_repo(repo_name)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 447, in get_repo
raise RepositoryNotFoundException(
pyartifactory.exception.RepositoryNotFoundException: Repository docker-remote does not exist
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 466, in create_repo
self._put(f"api/{self._uri}/{repo_name}", json=repo.dict())
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 113, in _put
return self._generic_http_method_request("put", route, **kwargs)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/pyartifactory/objects.py", line 134, in _generic_http_method_request
response: Response = http_method(
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/sessions.py", line 602, in put
return self.request('PUT', url, data=data, **kwargs)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/sessions.py", line 528, in request
prep = self.prepare_request(req)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/sessions.py", line 456, in prepare_request
p.prepare(
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/models.py", line 319, in prepare
self.prepare_body(data, files, json)
File "/Users/aj.rasmussen/Library/Python/3.8/lib/python/site-packages/requests/models.py", line 469, in prepare_body
body = complexjson.dumps(json)
File "/usr/local/Cellar/[email protected]/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/local/Cellar/[email protected]/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/local/Cellar/[email protected]/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/local/Cellar/[email protected]/3.8.6_2/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type SecretStr is not JSON serializable
Expected behavior
I expected the docker-remote repository to be created with the credentials in place but it seems that the "SecretStr" object is being passed to the JSON serializer in the code.
Screenshots
If applicable, add screenshots to help explain your problem.
Environment:
python3.8 -c "import pyartifactory; print(pyartifactory.__version__)"
python3.8 --version
Instead of building every request
from scratch every time, we could store a preconfigured "requester" object (with the base URL and authentication).
Example (pseudo-code probably not working as-is):
import requests
class Client():
def __init__(username: str, password: str, base_url: str):
self.requester = requests.Session()
self.requester.auth = ("username", "password")
self.base_url = base_url
def get(url, *args, **kwargs):
return self.requester.get(f"{self.base_url}{url}", *args, **kwargs)
client = Client("username", "password", "https://api.artifactory.com/api")
# And in another file
from client import client
client.get("/security/users/username")
Look at https://realpython.com/python-requests/#the-session-object or http://docs.python-requests.org/en/master/user/advanced/#session-objects
Hi,
Excellent job. Is it possible to implement wrapping to feature addressed in https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-BuildsforDependency?
Or even all search features in https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-SEARCHES.
Thanks.
Originally posted by dr4kk4r April 19, 2023
From what I can tell currently Artifactory class only allows for authorization via user name and password/api key.
How can I use access token instead?
Calling art.artifacts.download('some-directory/', 'some-path')
I expected the "some-directory/" folder to be downloaded either as "some-path/", or into "some-path/".
Instead, I'm receiving this stack-trace:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/stefan/.local/lib/python3.6/site-packages/pyartifactory/objects.py", line 810, in download
with open(local_file_full_path, "wb") as file:
IsADirectoryError: [Errno 21] Is a directory: 'some-path/'
This seems wrong. Either I have called the download
method with invalid arguments, in which case I would expect an exception telling me exactly that, or this is supposed to work but due to some bug it doesn't.
(Note that I also tried to figure out how to up- and download directories recursively using curl
following the jfrog
online docs, but couldn't get things to work. So it isn't entirely clear to me whether recursive downloads and uploads are supposed to work or whether I have to write my own wrapper API to recurse into the directory and up-/download files individually.)
I'm using artifactory 1.7.2 with Python 3.6.
Thanks !
Instead of doing everything from a "master" object (an artifactory instance), we should provide methods on every object, such as:
user.save()
to save any modification made to the objectregistry.delete()
Hi,
I'm trying to use pyartifactory
to update an existing virtual repo, but it seems like the update isn't straightforward.
so I've tried to find the repo first and update the properties but seems like the returned type of art.repositories.get_virtual_repo()
is VirtualRepositoryResponse
and art.repositories.update_virtual_repo
requires an input of type VirtualRepository
and there is no straightforward method to convert the VirtualRepositoryResponse
to VirtualRepository
or copy the properties over to avoid any undesirable changes!
creating a VirtualRepository
object via pyartifactory.models.VirtualRepository(key="test-virtual")
creates the required input for the update but a) with defaults that could be different than the config of existing repo b) surprisingly, creates the object with rclass
of type 'rclass' <RClassEnum.local: 'local'>
eg.
In [96]: from pyartifactory.models import VirtualRepository
In [97]: VirtualRepository(key='test').dict()
Out[97]:
{'key': 'test',
'rclass': <RClassEnum.local: 'local'>,
'packageType': <PackageTypeEnum.generic: 'generic'>,
'description': None,
'notes': None,
'includesPattern': '**/*',
'excludesPattern': '',
'repoLayoutRef': 'maven-2-default',
'repositories': None,
'artifactoryRequestsCanRetrieveRemoteArtifacts': False,
'debianTrivialLayout': False,
'keyPair': None,
'pomRepositoryReferencesCleanupPolicy': <PomRepoRefCleanupPolicy.discard_active_reference: 'discard_active_reference'>,
'defaultDeploymentRepo': None,
'forceMavenAuthentication': False,
'externalDependenciesEnabled': False,
'externalDependenciesPatterns': None,
'externalDependenciesRemoteRepo': None}
Describe the bug
The current implementation of method properies(self, artifact_path) defined in 'pyartifactory/objects.py' uses the rest endpoint url format string - "api/storage/{artifact_path}?properties[=x[,y]]"
Here "?properties[=x[,y]]" is a bug as the user cannot set it's value using arguments.
Due to that, it's not possible to provide the arguments for 'properties' querystring param. Ideally the format string should simply be "api/storage/{artifact_path}", thus user can pass the artifact_path for eg: "my-repository/my/new/artifact/directory/file.txt?properties=buildname,jobid" etc.
By default artifactory rest api just sends general properties. To get other properties, you need to pass the name of properties in querystring.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
It should allow the user of the method to pass the value of properties querystring param.
Screenshots
If applicable, add screenshots to help explain your problem.
Environment:
python -c "import pyartifactory; print(pyartifactory.__version__)"
python --version
Additional context
Add any other context about the problem here.
from pyartifactory.models.Repository import LocalRepository
does not feel very Pythonic, we should create a package that directly exports all models (eg. from pyartifactory.models import LocalRepository
.
This project has quite few great features to keep code quality/formatting in order, but it misses basic linting.
I would suggest adding the following
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
to the pre-commit config.
as this is an easy change but it brings a LOT of linting issues, then I figured I'd offer to implement that as well as fix the issues with the MR. Would this be wanted?
Python: 3.9.6
pyartifactory: 1.11.0
requests: 2.28.2
urllib3: 1.26.14
Artifactory: 6.23.23
OS: OSX Monterey
When i try to set or update the properties of an artifact the property name is uploaded correctly, but the value is uploaded with semicolons
set_art_props = art.artifacts.update_properties(
"internal-ceph/pool/main/quincy/ceph-base_17.2.5-1_amd64.deb", {"prop1": "value", "prop2": "value"}, recursive=False
)
Describe the bug
A clear and concise description of what the bug is.
To Reproduce
Steps to reproduce the behavior:
art = Artifactory(url=URL, auth=(USERNAME , API_KEY), verify=False, cert=None, api_version=1)
repositories = art.repositories.list()
print(repositories)
Traceback (most recent call last):
File "...", line 30, in <module>
get_latest_artifactory_commit()
File "...", line 19, in get_latest_artifactory_commit
repositories = art.repositories.list()
File "/opt/homebrew/lib/python3.9/site-packages/pyartifactory/objects/repository.py", line 129, in list
return [SimpleRepository(**repository) for repository in response.json()]
File "/opt/homebrew/lib/python3.9/site-packages/pyartifactory/objects/repository.py", line 129, in <listcomp>
return [SimpleRepository(**repository) for repository in response.json()]
File "/opt/homebrew/lib/python3.9/site-packages/pydantic/main.py", line 164, in __init__
__pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for SimpleRepository
type_
Field required [type=missing, input_value={'key': 'PRIVATE_REPO...'packageType': 'Docker'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.4/v/missing
Expected behavior
Should list repositories
Screenshots
N/A
Environment:
2.1.1
python3.9
Additional context
Seems to be a problem when validating SimpleRepository
through pydantic
but I explicitly passed all the required parameters to the Artifactory
contructor, so not sure what 'Field' is 'missing'.
Describe the bug
Object of type datetime is not JSON serializable
To Reproduce
Update a user groups
Stacktrace :
Object of type datetime is not JSON serializable
Traceback (most recent call last):
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/flaskit/resource.py", line 363, in wrapped
r = function(*args, **kwargs)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/resources/ArtifactoryTeamMembersSyncPut.py", line 77, in ArtifactoryTeamMembersSyncPut
user = artifactory_client.users.update(existing_user)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/pyartifactory/objects.py", line 201, in update
self._post(f"api/{self._uri}/{username}", json=user.dict())
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/pyartifactory/objects.py", line 104, in _post
return self._generic_http_method_request("post", route, **kwargs)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/pyartifactory/objects.py", line 133, in _generic_http_method_request
response: Response = http_method(
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/requests/sessions.py", line 581, in post
return self.request('POST', url, data=data, json=json, **kwargs)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/requests/sessions.py", line 519, in request
prep = self.prepare_request(req)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/requests/sessions.py", line 452, in prepare_request
p.prepare(
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/requests/models.py", line 316, in prepare
self.prepare_body(data, files, json)
File "/Users/alexandre/Clients/Adeo/Github/software-factory--self-service-api/.venv/lib/python3.8/site-packages/requests/models.py", line 466, in prepare_body
body = complexjson.dumps(json)
File "/Users/alexandre/.pyenv/versions/3.8.2/lib/python3.8/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/Users/alexandre/.pyenv/versions/3.8.2/lib/python3.8/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/Users/alexandre/.pyenv/versions/3.8.2/lib/python3.8/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/Users/alexandre/.pyenv/versions/3.8.2/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable
Error executing API ArtifactoryTeamMembersSyncPut (Object of type datetime is not JSON serializable)
Environment:
Hi,
In a simple call as :
art = Artifactory(url="https://uala.jfrog.io/uala", auth=('user','pass'), verify=False)
remote_repo = art.repositories.get_remote_repo("libs-release")
I'm getting the following error:
Traceback (most recent call last):
File "/Users/gonzalojavierclavell/Workspace/repository/uala-scripts-actions/scripts/upload-artifactory.py", line 55, in <module>
remote_repo = art.repositories.get_remote_repo("libs-release")
File "/Users/xxx/.pyenv/versions/3.7.3/lib/python3.7/site-packages/pyartifactory/objects.py", line 528, in get_remote_repo
return RemoteRepositoryResponse(**r.json())
File "/Users/xxx/.pyenv/versions/3.7.3/lib/python3.7/site-packages/pydantic/main.py", line 283, in __init__
raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for RemoteRepositoryResponse
url
field required (type=value_error.missing)
repos = art.repositories.list() is working fine.
Thanks in advance
Describe the bug
Trying to update an existing Maven remote repository configuration fails with "TypeError: Object of type 'SecretStr' is not JSON serializable"
To Reproduce
Steps to reproduce the behavior:
from pyartifactory import Artifactory
url = <url to artifactory instance>
auth = (<username>, <password>)
art = Artifactory(url=url, auth=auth)
remote_repo = art.repositories.get('ml-test-remote')
remote_repo.excludesPattern = '**/fake-*'
art.repositories.update_repo(remote_repo)
Executing the above code causes the following error:
Traceback (most recent call last):
File "./art_test.py", line 13, in <module>
art.repositories.update_repo(remote_repo)
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/objects.py", line 552, in update_repo
self._post(f"api/{self._uri}/{repo_name}", json=repo.dict())
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/artifactory_object.py", line 38, in _post
return self._generic_http_method_request("post", route, **kwargs)
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/artifactory_object.py", line 72, in _generic_http_method_request
cert=self._cert,
File "/usr/local/lib/python3.6/site-packages/requests/sessions.py", line 581, in post
return self.request('POST', url, data=data, json=json, **kwargs)
File "/usr/local/lib/python3.6/site-packages/requests/sessions.py", line 519, in request
prep = self.prepare_request(req)
File "/usr/local/lib/python3.6/site-packages/requests/sessions.py", line 462, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "/usr/local/lib/python3.6/site-packages/requests/models.py", line 316, in prepare
self.prepare_body(data, files, json)
File "/usr/local/lib/python3.6/site-packages/requests/models.py", line 466, in prepare_body
body = complexjson.dumps(json)
File "/usr/lib64/python3.6/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/lib64/python3.6/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib64/python3.6/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/lib64/python3.6/json/encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'SecretStr' is not JSON serializable
Expected behavior
Remote repository configuration is updated.
Environment:
Additional context
I think this is related to #74 and #75 where a similar problem with create_repo()
was fixed.
I tried to hack the same changes to update_repo()
:
def update_repo(self, repo: AnyRepository) -> AnyRepositoryResponse:
"""
Updates a local, virtual or remote repository
:param repo: Either a local, virtual or remote repository
:return: LocalRepositoryResponse, VirtualRepositoryResponse or RemoteRepositoryResponse object
"""
repo_name = repo.key
self.get_repo(repo_name)
data = json.dumps(repo, default=custom_encoder)
self._post(
f"api/{self._uri}/{repo_name}",
headers={"Content-Type": "application/json"},
data=data,
)
logger.debug("Repository %s successfully updated", repo_name)
return self.get_repo(repo_name)
and that got me past the JSON serialization error but the operation still failed, this time with HTTP 400 error response:
Traceback (most recent call last):
File "./art_test.py", line 13, in <module>
art.repositories.update_repo(remote_repo)
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/objects.py", line 556, in update_repo
data=data,
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/artifactory_object.py", line 38, in _post
return self._generic_http_method_request("post", route, **kwargs)
File "/home/mlaakson/.local/lib/python3.6/site-packages/pyartifactory/artifactory_object.py", line 75, in _generic_http_method_request
response.raise_for_status()
File "/usr/local/lib/python3.6/site-packages/requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://REDACTED/api/repositories/ml-test-remote
and on the Artifactory side the following error was printed in artifactory-service.log
:
2021-10-07T12:51:54.149Z [jfrt ] [ERROR] [8c6c5edf8c4c65e3] [o.a.a.r.RestAddonImpl:1306 ] [-nio-8081-exec-10747] - Unable to update repository: null
java.lang.reflect.InvocationTargetException: null
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
...
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException: null
at org.artifactory.repo.HttpRepositoryConfigurationImpl.setBowerRegistryUrl(HttpRepositoryConfigurationImpl.java:700)
... 87 common frames omitted
After removing the following properties from the remote_repo
object defined in the example above, the update_repo()
function call succeeded:
bowerRegistryUrl
composerRegistryUrl
pyPIRegistryUrl
vcsType
vcsGitProvider
vcsGitDownloadUrl
Is your feature request related to a problem? Please describe.
When downloading something from the artifactory, an error handling of download issues would be nice (Connection failure, timeout, etc.)
Describe the solution you'd like
Print a message or raise an error.
Additional context
In my code as I download something, it is hard to catch a the problem is coming from my side or from JFrog, or from pyartifactory side.
Thank you for your effort!
A simple TODO list for next changes :
Build artifacts
etc ...
General question. Say I have a CSV file with a list of artifacts to delete. What do you suggest for passing CSV data into art.artifacts.delete("<ARTIFACT_PATH_IN_ARTIFACTORY>")
instead of hard coding a single repository path?
Currently,
Depending of our repository type (local, virtual or remote), we have to use a specific methods (get_local_repo, get_virtual_repo, update_remote_repo ...). It would be better to have global methods that do not bother with the repo type.
Describe the bug
When deploying an artifact the local file's bytes get embedded in a multipart MIME message.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Downloading the artifact using any client, including the UI, should produce a file which is binary equivalent to the file uploaded with this client.
Environment:
Is your feature request related to a problem? Please describe.
The old Create Token API that is used by ArtifactorySecurity.create_access_token()
is deprecated: https://jfrog.com/help/r/jfrog-rest-apis/delete-group?tocId=2_OrHvmQlC6dtFFR8F9i3w
Describe the solution you'd like
Add support for or use the new Create Token API instead: https://jfrog.com/help/r/jfrog-rest-apis/create-token
Additional context
We ran into an issue today where all the tokens generated with pyartifactory
didn't actually have any permissions. It seems the "scope" value of the deprecated API endpoint expects the format of the new endpoint (e.g. "applied-permissions/user" is what we're using now after monkey-patching pyartifactory).
Sometimes we log, sometimes we don't.
Also we could have a single preconfigured logger defined at the beginning.
Describe the bug
When creating an Artifactory repository using the create_repo function and a LocalRepository as input, an error is thrown rather than a LocalRepositoryResponse being returned. The local repository is created successfully, but something goes wrong when the create_repo function should return a value.
Error:
3 validation errors for RemoteRepositoryResponse\nrclass\n Input should be <RClassEnum.remote: 'remote'> [type=literal_error, input_value='local', input_type=str]\n For further information visit https://errors.pydantic.dev/2.4/v/literal_error\nurl\n Field required [type=missing, input_value={'key': '..., 'rclass': 'local'}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.4/v/missing\ncdnRedirect\n Input should be a valid string [type=string_type, input_value=False, input_type=bool]\n For further information visit https://errors.pydantic.dev/2.4/v/string_type")
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Creation of a local repository and response that the local repository has been created successfully rather than Py.
Environment:
OS: Linux (Debian 11)
pyartifactory version: 2:1.2
Python version: 3.8
Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Describe the solution you'd like
A clear and concise description of what you want to happen.
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context or screenshots about the feature request here.
Add a method that checks whether an artifact exists:
artifact_info = art.artifacts.exists("<ARTIFACT_PATH_IN_ARTIFACTORY>")
Currently, this can be done with the info method, combined with try/except.
Instead of just raising the HTTP exception, we should encapsulate it in our own PyArtifactory exceptions.
Describe the bug
When calling download with a source path with a space in the filename the download function fails with error:
ERROR: Artifact ***/***/***/40.x.7.13 (#1173)/R255_40_13D7_13.zip does not exist
when using urllib.parse.quote to encode the source path the download function fails with error:
Input string, '***/***/***/40.x.7.13 (#1173)/R255_40_13D7_13.zip', doesn't have the prefix: '***/***/***/40.x.7.13%20%28%231173%29/'
(actual text replaced with ***)
To Reproduce
Steps to reproduce the behavior:
Note: function calls info and properties work with the encoded url as expected.
Describe the bug
Wrong dependency management with typing_extensions
The conflict is caused by:
pyartifactory 1.10.1 depends on typing_extensions<4.0.0 and >=3.7.4; python_version >= "3.6" and python_version < "4.0"
pyartifactory 1.10.1 depends on typing_extensions<5 and >=4; python_version >= "3.7" and python_version < "4.0"
To Reproduce
Steps to reproduce the behavior:
With Python 3.10:
pip install --upgrade typing_extensions
pip install --upgrade pyartifatory
Expected behavior
To be compatible with latest versions of typing_extensions.
Screenshots
N/A
Environment:
Additional context
Need to fix pyproject.toml
:
typing_extensions = [
{ version = "^3.7.4", python = "^3.6" },
{ version = "^4", python = "^3.7" },
]
Observed while installing other packages depending on the latest typing_extensions module.
All our exceptions extend from Exception
, we should create an ArtifactoryException
to extend from
Is your feature request related to a problem? Please describe.
So Im trying to get an image's digest without needing to pull the image. I can see on the artifactory UI that there is a docker info tab, and in there it has the images digest and some extra info.
Describe the solution you'd like
I would like to be able to access the docker info using the api / pyartifactory.
Is your feature request related to a problem? Please describe.
When uploading a build I need to set a key=value properties for that artifact/build. currently only get is implemented for properties.
Describe the solution you'd like
provide two new methods in artifact class to implement:
Set Item Properties
Update Item Properties
Describe alternatives you've considered
Wrapping jfrog CLI for now
Additional context
https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-SetItemProperties
Hi,
I want to find the managers in a permission. For that, I read the details of a permission to extract the users with the right "m".
The problem is that it seems to work only for some permissions but not all.
I don't know in which cases it works or not.
Can you confirm the problem ? Or do I use the librairie in a bad way ?
Do you have any solutions ?
Describe the bug
For some of ours permissions, the function "pyart.permissions.get(permission_name)" abort in error, whereas artifactory/api/security/permissions/permission_name return the result.
To Reproduce
pyart = pyArtifactory(url=V_url_artif_formatpyart, auth=(V_username, V_password), api_version=1)
perm_detail_test = pyart.permissions.get('permission_name')
logger.debug("Accès au détail de la permission : %s", perm_detail_test)
curl -u login:pwd "https://artifactory-url/artifactory/api/security/permissions/permission_name"
(but I don't know which permissions to chose)
Expected behavior
I want to display the users with their rights for a permission name.
Screenshots
NA
Environment:
Additional context
The request with the library :
pyart = pyArtifactory(url=V_url_artif_formatpyart, auth=(V_username, V_password), api_version=1)
perm_detail_test = pyart.permissions.get('generic-local-perms')
logger.debug("Accès au détail de la permission : %s", perm_detail_test)
perm_detail_test = pyart.permissions.get('my_test_perm')
logger.debug("Accès au détail de la permission : %s", perm_detail_test)
Result for the permission generic-local-perms (that works) :
2023-06-12 14:17:51 [DEBUG] _make_request. https://artifactory-url:443 "GET /artifactory/api/security/permissions/generic-local-perms HTTP/1.1" 200 None (connectionpool.py:456)
2023-06-12 14:17:51 [DEBUG] get. Permission generic-local-perms found (objects.py:228)
2023-06-12 14:17:51 [DEBUG] main. Accès au détail de la permission : name='generic-local-perms' includesPattern='**' excludesPattern='' repositories=['generic-local'] principals=PrincipalsPermission(users={'user1': [<PermissionEnum.read: 'r'>, <PermissionEnum.delete: 'd'>, <PermissionEnum.deploy: 'w'>, <PermissionEnum.admin: 'm'>, <PermissionEnum.annotate: 'n'>]}, groups=None) (test-artif_lib-v1-4.py:346)
Result for the permission my_test_perm (that doesn't work) :
2023-06-12 14:17:51 [DEBUG] _make_request. https://artifactory-url:443 "GET /artifactory/api/security/permissions/my_test_perm HTTP/1.1" 200 None (connectionpool.py:456)
2023-06-12 14:17:51 [DEBUG] get. Permission my_test_perm found (objects.py:228)
Traceback (most recent call last):
File "test-artif_lib-v1-4.py", line 408, in
main()
File "test-artif_lib-v1-4.py", line 347, in main
perm_detail_test = pyart.permissions.get('my_test_perm')
File "/myrep/.venv/lib/python3.8/site-packages/pyartifactory/objects.py", line 230, in get
Permission(**response.json())
File "pydantic/main.py", line 341, in pydantic.main.BaseModel.init
pydantic.error_wrappers.ValidationError: 1 validation error for Permission
principals -> users -> usra -> 1
value is not a valid enumeration member; permitted: 'm', 'd', 'w', 'n', 'r' (type=type_error.enum; enum_values=[<PermissionEnum.admin: 'm'>, <PermissionEnum.delete: 'd'>, <PermissionEnum.deploy: 'w'>, <PermissionEnum.annotate: 'n'>, <PermissionEnum.read: 'r'>])
The same request with the API :
curl -u login:pwd "https://artifactory-url/artifactory/api/security/permissions/my_test_perm"
The result (it works) :
{
"name" : "my_test_perm",
"includesPattern" : "**",
"excludesPattern" : "",
"repositories" : [ "docker-7g-local", "docker-local", "conda-local", "docker-local", "pip-dev-local", "pip-local" ],
"principals" : {
"users" : {
"usra" : [ "r", "mxm", "d", "w", "m", "n" ]
}
}
}
Previously when creating a local repository using ArtifactoryRepository.create_repo(), a Local Respository object was expected as input. Creating a local repository in the latest version expects a LocalRepositoryResponse object rather than a LocalRepository object. Why is the response object expected as input here? Using a LocalRepository throws a Pydantic validation error. This was working in pyartifactory version 1.13.0
To Reproduce
Steps to reproduce the behavior:
Expected behavior
I would expect that a LocalRepository object would be expected as input here rather than a LocalRepositoryResponse object
Environment:
Would there be an interest to add a command-line "frontend" to this project that acts somewhat like the jfrog
tool, but uses all the functionality implemented by this pyartifactory
Python package ?
I'd be happy to start this, covering the "artifact" domain, i.e. moving data around. Someone else could then expand this to other domains such as repo and user management.
Hi!
Is there any documentation/examples how one should define which repositories are included in the virtual repository?
class VirtualRepository(BaseRepositoryModel):
"""Models a virtual repository."""
rclass: Literal[RClassEnum.virtual] = RClassEnum.virtual
repositories: Optional[List[str]] = None
Are the 'repositories' the repo keys?
Another related question, are there any tools that convert between the JFrog Artifactory repository .json format and Base|Local|Remote|VirtualRepository structures? It would be handy to store the .json locally and deploy the changes to Artifactory via python-artifactory?
The REST API mentions a feature named "File List" as described on https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-FileList. Is there any plan to implement this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.