Trying to publish a Poetry package to AWS CodeArtifact. It supports pip
which should indicate that it supports poetry
as well since poetry
can upload to PyPi servers.
I've configured the domain like so:
export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain XXXX --domain-owner XXXXXXXXXXXX --query authorizationToken --output text`
poetry config repositories.the_aws_repo https://aws:$CODEARTIFACT_AUTH_TOKEN@XXXX-XXXXXXXXXXXX.d.codeartifact.eu-central-1.amazonaws.com/pypi/XXXX/simple/
poetry config pypi-token.the_aws_repo $CODEARTIFACT_AUTH_TOKEN
But I'm getting 404 when trying to publish the package:
❯ poetry publish --repository the_aws_repo -vvv
No suitable keyring backend found
No suitable keyring backends were found
Using a plaintext file to store and retrieve credentials
Publishing xxx (0.1.5) to the_aws_repo
- Uploading xxx-0.1.5-py3-none-any.whl 100%
Stack trace:
7 ~/.poetry/lib/poetry/_vendor/py3.8/clikit/console_application.py:131 in run
129│ parsed_args = resolved_command.args
130│
→ 131│ status_code = command.handle(parsed_args, io)
132│ except KeyboardInterrupt:
133│ status_code = 1
6 ~/.poetry/lib/poetry/_vendor/py3.8/clikit/api/command/command.py:120 in handle
118│ def handle(self, args, io): # type: (Args, IO) -> int
119│ try:
→ 120│ status_code = self._do_handle(args, io)
121│ except KeyboardInterrupt:
122│ if io.is_debug():
5 ~/.poetry/lib/poetry/_vendor/py3.8/clikit/api/command/command.py:171 in _do_handle
169│ handler_method = self._config.handler_method
170│
→ 171│ return getattr(handler, handler_method)(args, io, self)
172│
173│ def __repr__(self): # type: () -> str
4 ~/.poetry/lib/poetry/_vendor/py3.8/cleo/commands/command.py:92 in wrap_handle
90│ self._command = command
91│
→ 92│ return self.handle()
93│
94│ def handle(self): # type: () -> Optional[int]
3 ~/.poetry/lib/poetry/console/commands/publish.py:77 in handle
75│ )
76│
→ 77│ publisher.publish(
78│ self.option("repository"),
79│ self.option("username"),
2 ~/.poetry/lib/poetry/publishing/publisher.py:93 in publish
91│ )
92│
→ 93│ self._uploader.upload(
94│ url,
95│ cert=cert or get_cert(self._poetry.config, repository_name),
1 ~/.poetry/lib/poetry/publishing/uploader.py:119 in upload
117│
118│ try:
→ 119│ self._upload(session, url, dry_run)
120│ finally:
121│ session.close()
UploadError
HTTP Error 404: Not Found
at ~/.poetry/lib/poetry/publishing/uploader.py:216 in _upload
212│ self._register(session, url)
213│ except HTTPError as e:
214│ raise UploadError(e)
215│
→ 216│ raise UploadError(e)
217│
218│ def _do_upload(
219│ self, session, url, dry_run=False
220│ ): # type: (requests.Session, str, Optional[bool]) -> None
My AWS IAM user has permission to do this since I gave it the relevant permissions in the repo.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::XXXXXXXXXXXX:user/ShayN"
},
"Action": [
"codeartifact:AssociateExternalConnection",
"codeartifact:CopyPackageVersions",
"codeartifact:DeletePackageVersions",
"codeartifact:DeleteRepository",
"codeartifact:DeleteRepositoryPermissionsPolicy",
"codeartifact:DescribePackageVersion",
"codeartifact:DescribeRepository",
"codeartifact:DisassociateExternalConnection",
"codeartifact:DisposePackageVersions",
"codeartifact:GetPackageVersionReadme",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ListPackageVersionAssets",
"codeartifact:ListPackageVersionDependencies",
"codeartifact:ListPackageVersions",
"codeartifact:ListPackages",
"codeartifact:PublishPackageVersion",
"codeartifact:PutPackageMetadata",
"codeartifact:PutRepositoryPermissionsPolicy",
"codeartifact:ReadFromRepository",
"codeartifact:UpdatePackageVersionsStatus",
"codeartifact:UpdateRepository"
],
"Resource": "*"
}
]
}
What am I missing?
The problem is the /simple/
at the end of the repo url. This part should only be added when pulling from that repo, not when publishing to it. If you look closely to the documentation of AWS CodeArtifact on how to publish with twine
, you'll see that it's also not there.
This works:
# This will give the repo url without the /simple/ part
# Example: https://<my-domain>-<domain-owner-id>.d.codeartifact.<region>.amazonaws.com/pypi/<my-repo>/
# Note the lack of the "aws:auth-token@" part
export CODEARTIFACT_REPOSITORY_URL=`aws codeartifact get-repository-endpoint --domain my-domain --domain-owner domain-owner-id --repository my-repo --format pypi --query repositoryEndpoint --output text`
# This will give the token to access the repo
export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain my-domain --domain-owner domain-owner-id --query authorizationToken --output text`
# This specifies the user who accesses the repo
export CODEARTIFACT_USER=aws
# Now use all of these when configuring the repo in poetry
poetry config repositories.<my-repo-name-for-poetry> $CODEARTIFACT_REPOSITORY_URL
poetry config http-basic.<my-repo-name-for-poetry> $CODEARTIFACT_USER $CODEARTIFACT_AUTH_TOKEN
Note that the authentication token will expire when your AWS login session ends. Hence, you'll have to set the http-basic.<my-repo-name-for-poetry>
with the new token every time it expires.
FYI, I had the same problem and it took me hours to figure this out. But in the end, more carefully reading the documentation should have helped me.