Repository: canonical/charmcraft

Issue:
Getting an internal error for fetch-libs
### Bug Description

I'm getting an internal error when running the fetch-libs command:

```bash
charmcraft fetch-libs
charmcraft internal error: NoKeyringError('No keyring found to store or retrieve credentials from.')
```

### To Reproduce

- `charmcraft init --profile flask-framework`
- `charmcraft fetch-libs`

### Environment

Ubuntu 22.04
charmcraft --version: charmcraft 3.0.0.post59+g775a0fc

### charmcraft.yaml

```shell
# This file configures Charmcraft.
# See https://juju.is/docs/sdk/charmcraft-config for guidance.

name: flask-hello-world

type: charm

bases:
  - build-on:
    - name: ubuntu
      channel: "22.04"
    run-on:
    - name: ubuntu
      channel: "22.04"

# (Required)
summary: A very short one-line summary of the Flask application.

# (Required)
description: |
  A comprehensive overview of your Flask application.

extensions:
  - flask-framework

# Uncomment the integrations used by your application
# Integrations set to "optional: false" will block the charm
# until the applications are integrated.
# requires:
#   mysql:
#     interface: mysql_client
#     optional: false
#     limit: 1
#   postgresql:
#     interface: postgresql_client
#     optional: false
#     limit: 1
#   mongodb:
#     interface: mongodb_client
#     optional: false
#     limit: 1
#   redis:
#     interface: redis
#     optional: false
#     limit: 1
#   s3:
#     interface: s3
#     optional: false
#     limit: 1
#   saml:
#     interface: saml
#     optional: false
#     limit: 1
```


### Relevant log output

```shell
2024-07-04 16:41:26.914 Starting charmcraft, version 3.0.0.post59+g775a0fc
2024-07-04 16:41:26.914 Log verbosity level set to BRIEF
2024-07-04 16:41:26.914 Configuring application...
2024-07-04 16:41:26.914 Preparing application...
2024-07-04 16:41:26.914 Build plan: platform=None, build_for=None
2024-07-04 16:41:26.915 Loading project file '/home/ubuntu/flask-hello-world/charm/charmcraft.yaml'
2024-07-04 16:41:26.917 Processing grammar (on amd64 for amd64)
2024-07-04 16:41:26.917 Processing grammar for plugin: charm
2024-07-04 16:41:26.917 Processing grammar for source: .
2024-07-04 16:41:26.918 Running charmcraft fetch-libs on host
2024-07-04 16:41:26.922 Setting up StoreService
2024-07-04 16:41:26.969 charmcraft internal error: NoKeyringError('No keyring found to store or retrieve credentials from.')
2024-07-04 16:41:26.982 Traceback (most recent call last):
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_application/application.py", line 517, in run
2024-07-04 16:41:26.982     return_code = dispatcher.run() or 0
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_cli/dispatcher.py", line 487, in run
2024-07-04 16:41:26.982     return self._loaded_command.run(self._parsed_command_args)
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/charmcraft/application/commands/store.py", line 1672, in run
2024-07-04 16:41:26.982     store = self._services.store
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_application/services/service_factory.py", line 95, in __getattr__
2024-07-04 16:41:26.982     instance.setup()
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/charmcraft/services/store.py", line 173, in setup
2024-07-04 16:41:26.982     super().setup()
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/charmcraft/services/store.py", line 80, in setup
2024-07-04 16:41:26.982     self.client = self.ClientClass(
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/charmcraft/store/client.py", line 139, in __init__
2024-07-04 16:41:26.982     super().__init__(
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_store/store_client.py", line 93, in __init__
2024-07-04 16:41:26.982     super().__init__(
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_store/base_client.py", line 77, in __init__
2024-07-04 16:41:26.982     self._auth = Auth(
2024-07-04 16:41:26.982   File "/snap/charmcraft/4025/lib/python3.10/site-packages/craft_store/auth.py", line 201, in __init__
2024-07-04 16:41:26.982     raise errors.NoKeyringError
2024-07-04 16:41:26.982 craft_store.errors.NoKeyringError: No keyring found to store or retrieve credentials from.
2024-07-04 16:41:26.982 Full execution log: '/home/ubuntu/.local/state/charmcraft/log/charmcraft-20240704-164126.909389.log'
```


Discussion:

Patch:
diff --git a/charmcraft/services/store.py b/charmcraft/services/store.py
index 546a7996..90334fb0 100644
--- a/charmcraft/services/store.py
+++ b/charmcraft/services/store.py
@@ -21,6 +21,7 @@ from collections.abc import Collection, Mapping, Sequence
 
 import craft_application
 import craft_store
+from craft_cli import emit
 from craft_store import models
 from overrides import override
 
@@ -77,14 +78,30 @@ class BaseStoreService(craft_application.AppService):
         """Set up the store service."""
         super().setup()
 
-        self.client = self.ClientClass(
-            application_name=self._app.name,
-            base_url=self._base_url,
-            storage_base_url=self._storage_url,
-            endpoints=self._endpoints,
-            environment_auth=self._environment_auth,
-            user_agent=self._user_agent,
-        )
+        try:
+            self.client = self.ClientClass(
+                application_name=self._app.name,
+                base_url=self._base_url,
+                storage_base_url=self._storage_url,
+                endpoints=self._endpoints,
+                environment_auth=self._environment_auth,
+                user_agent=self._user_agent,
+            )
+        except craft_store.errors.NoKeyringError:
+            emit.progress(
+                "WARNING: Cannot get a keyring. Every store interaction that requires "
+                "authentication will require you to log in again.",
+                permanent=True,
+            )
+            self.client = self.ClientClass(
+                application_name=self._app.name,
+                base_url=self._base_url,
+                storage_base_url=self._storage_url,
+                endpoints=self._endpoints,
+                environment_auth=self._environment_auth,
+                user_agent=self._user_agent,
+                ephemeral=True,
+            )
 
     def _get_description(self, description: str | None = None) -> str:
         """Return the given description or a default one."""
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 55ed641c..ce5df05e 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -4,7 +4,7 @@ cffi==1.16.0
 charset-normalizer==3.3.2
 coverage==7.5.3
 craft-application==3.2.0
-craft-archives==1.1.3
+craft-archives==1.2.0
 craft-cli==2.6.0
 craft-grammar==1.2.0
 craft-parts==1.33.0
diff --git a/requirements.txt b/requirements.txt
index 0ce693a7..b0cb6c7d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@ certifi==2024.7.4
 cffi==1.16.0
 charset-normalizer==3.3.2
 craft-application==3.2.0
-craft-archives==1.1.3
+craft-archives==1.2.0
 craft-cli==2.6.0
 craft-grammar==1.2.0
 craft-parts==1.33.0


Test patch:
diff --git a/tests/unit/services/test_store.py b/tests/unit/services/test_store.py
index 540b6629..53d2daf7 100644
--- a/tests/unit/services/test_store.py
+++ b/tests/unit/services/test_store.py
@@ -21,6 +21,7 @@ from unittest import mock
 import craft_store
 import distro
 import pytest
+from craft_cli.pytest_plugin import RecordingEmitter
 from craft_store import models
 from hypothesis import given, strategies
 
@@ -89,6 +90,19 @@ def test_ua_system_info_linux(
     )
 
 
+def test_setup_with_error(emitter: RecordingEmitter, store):
+    store.ClientClass = mock.Mock(side_effect=[craft_store.errors.NoKeyringError, "I am a store!"])
+
+    store.setup()
+
+    assert store.client == "I am a store!"
+    emitter.assert_progress(
+        "WARNING: Cannot get a keyring. Every store interaction that requires "
+        "authentication will require you to log in again.",
+        permanent=True,
+    )
+
+
 def test_get_description_default(monkeypatch, store):
     monkeypatch.setattr(platform, "node", lambda: "my-hostname")