daktari 0.0.300__tar.gz → 0.0.306__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {daktari-0.0.300/daktari.egg-info → daktari-0.0.306}/PKG-INFO +5 -5
- {daktari-0.0.300 → daktari-0.0.306}/README.md +1 -1
- daktari-0.0.306/daktari/__init__.py +1 -0
- daktari-0.0.306/daktari/checks/npmrc.py +126 -0
- daktari-0.0.306/daktari/checks/pnpm.py +14 -0
- daktari-0.0.306/daktari/checks/test_npmrc.py +161 -0
- daktari-0.0.306/daktari/checks/test_pnpm.py +25 -0
- {daktari-0.0.300 → daktari-0.0.306/daktari.egg-info}/PKG-INFO +5 -5
- {daktari-0.0.300 → daktari-0.0.306}/daktari.egg-info/SOURCES.txt +4 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari.egg-info/requires.txt +3 -3
- {daktari-0.0.300 → daktari-0.0.306}/pyproject.toml +1 -1
- {daktari-0.0.300 → daktari-0.0.306}/requirements.txt +3 -3
- daktari-0.0.300/daktari/__init__.py +0 -1
- {daktari-0.0.300 → daktari-0.0.306}/LICENSE.txt +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/MANIFEST.in +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/__main__.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/asdf.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/check.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/check_runner.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/check_sorter.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/check_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/__init__.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/android.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/aws.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/certs.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/conan.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/direnv.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/docker.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/etc_hosts.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/files.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/flutter.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/git.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/google.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/intellij_idea.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/java.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/kubernetes.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/misc.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/nodejs.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/onepassword.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/python.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/ssh.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/terraform.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_certs.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_intellij_idea.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_java.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_misc.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_onepassword.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_ssh.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/test_yarn.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/xml.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/checks/yarn.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/collection_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/command_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/config.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/file_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/options.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/os.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/resource_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/resources/__init__.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/resources/daktari-local-template.yaml +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/resources/mock_cert.pem +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/result_printer.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_asdf.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_check.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_check_factory.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_check_runner.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_check_sorter.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_check_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_collection_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_config.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_result_printer.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/test_version_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari/version_utils.py +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari.egg-info/dependency_links.txt +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari.egg-info/entry_points.txt +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/daktari.egg-info/top_level.txt +0 -0
- {daktari-0.0.300 → daktari-0.0.306}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: daktari
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.306
|
|
4
4
|
Summary: Assist in setting up and maintaining developer environments
|
|
5
5
|
Author-email: Matt Russell <matthew.russell@sonocent.com>
|
|
6
6
|
License: Copyright 2021 Sonocent Ltd
|
|
@@ -18,8 +18,8 @@ Requires-Dist: ansicolors==1.1.8
|
|
|
18
18
|
Requires-Dist: distro==1.9.0
|
|
19
19
|
Requires-Dist: pyfiglet==1.0.4
|
|
20
20
|
Requires-Dist: importlib_resources==6.5.2
|
|
21
|
-
Requires-Dist: packaging==
|
|
22
|
-
Requires-Dist: setuptools==80.
|
|
21
|
+
Requires-Dist: packaging==26.0
|
|
22
|
+
Requires-Dist: setuptools==80.10.2
|
|
23
23
|
Requires-Dist: requests==2.32.5
|
|
24
24
|
Requires-Dist: responses==0.25.8
|
|
25
25
|
Requires-Dist: semver==3.0.4
|
|
@@ -33,7 +33,7 @@ Requires-Dist: dpath==2.2.0
|
|
|
33
33
|
Requires-Dist: pyOpenSSL==25.3.0
|
|
34
34
|
Requires-Dist: types-pyOpenSSL==24.1.0.20240722
|
|
35
35
|
Requires-Dist: pyclip==0.7.0
|
|
36
|
-
Requires-Dist: urllib3==2.6.
|
|
36
|
+
Requires-Dist: urllib3==2.6.3
|
|
37
37
|
Dynamic: license-file
|
|
38
38
|
|
|
39
39
|
**Daktari** is a tool to help the initial setup and ongoing maintenance of developer environments. It runs a series of checks (for example, that required software is installed) and provides suggestions on how to fix the issue if the check fails.
|
|
@@ -45,7 +45,7 @@ In the root of the project repository, create a `.daktari.py` configuration file
|
|
|
45
45
|
```python
|
|
46
46
|
from daktari.checks.git import *
|
|
47
47
|
|
|
48
|
-
version = "0.0.
|
|
48
|
+
version = "0.0.306"
|
|
49
49
|
title = "My Project"
|
|
50
50
|
|
|
51
51
|
checks = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.306"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
from daktari.check import Check, CheckResult
|
|
8
|
+
from daktari.command_utils import can_run_command
|
|
9
|
+
from daktari.file_utils import file_exists
|
|
10
|
+
from daktari.os import OS
|
|
11
|
+
|
|
12
|
+
PLACEHOLDER_TOKEN = "UPDATE_WITH_TOKEN"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class NpmrcScope:
|
|
17
|
+
name: str
|
|
18
|
+
registry: str
|
|
19
|
+
require_auth_token: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_npmrc_path() -> str:
|
|
23
|
+
return os.path.expanduser("~/.npmrc")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_npmrc_contents() -> List[str]:
|
|
27
|
+
npmrc_path = get_npmrc_path()
|
|
28
|
+
if not file_exists(npmrc_path):
|
|
29
|
+
raise Exception(f"{npmrc_path} does not exist")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
with open(npmrc_path, "r") as npmrc_file:
|
|
33
|
+
return npmrc_file.readlines()
|
|
34
|
+
except Exception:
|
|
35
|
+
logging.error(f"Exception reading {npmrc_path}", exc_info=True)
|
|
36
|
+
raise Exception("Failed to read npmrc")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_registry_host(registry_url: str) -> str:
|
|
40
|
+
parsed = urlparse(registry_url)
|
|
41
|
+
return parsed.netloc
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def npmrc_contains_scope_registry(lines: List[str], scope: NpmrcScope) -> bool:
|
|
45
|
+
expected_line = f"@{scope.name}:registry={scope.registry}"
|
|
46
|
+
for line in lines:
|
|
47
|
+
if line.strip() == expected_line:
|
|
48
|
+
return True
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def npmrc_contains_auth_token(lines: List[str], registry_url: str) -> bool:
|
|
53
|
+
host = get_registry_host(registry_url)
|
|
54
|
+
auth_token_prefix = f"//{host}/:_authToken="
|
|
55
|
+
for line in lines:
|
|
56
|
+
stripped = line.strip()
|
|
57
|
+
if stripped.startswith(auth_token_prefix):
|
|
58
|
+
token_value = stripped[len(auth_token_prefix) :]
|
|
59
|
+
if token_value and token_value != PLACEHOLDER_TOKEN:
|
|
60
|
+
return True
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def npmrc_scope_is_configured(lines: List[str], scope: NpmrcScope) -> bool:
|
|
65
|
+
if not npmrc_contains_scope_registry(lines, scope):
|
|
66
|
+
return False
|
|
67
|
+
if scope.require_auth_token and not npmrc_contains_auth_token(lines, scope.registry):
|
|
68
|
+
return False
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_npmrc_suggestion(scope: NpmrcScope) -> str:
|
|
73
|
+
lines = [f"@{scope.name}:registry={scope.registry}"]
|
|
74
|
+
if scope.require_auth_token:
|
|
75
|
+
host = get_registry_host(scope.registry)
|
|
76
|
+
lines.append(f"//{host}/:_authToken={PLACEHOLDER_TOKEN}")
|
|
77
|
+
return "\n".join(lines)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class NpmrcScopeConfigured(Check):
|
|
81
|
+
name = "npmrc.scopeConfigured"
|
|
82
|
+
|
|
83
|
+
def __init__(self, scope: NpmrcScope, token_instructions: Optional[str] = None):
|
|
84
|
+
self.scope = scope
|
|
85
|
+
self.npmrc_suggestion = get_npmrc_suggestion(scope)
|
|
86
|
+
token_instruction_string = f"\n\n{token_instructions}" if token_instructions else ""
|
|
87
|
+
self.suggestions = {
|
|
88
|
+
OS.GENERIC: f"""Add the lines below to ~/.npmrc:
|
|
89
|
+
|
|
90
|
+
{self.npmrc_suggestion}{token_instruction_string}"""
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def check(self) -> CheckResult:
|
|
94
|
+
try:
|
|
95
|
+
lines = get_npmrc_contents()
|
|
96
|
+
except Exception as e:
|
|
97
|
+
return self.failed(str(e))
|
|
98
|
+
|
|
99
|
+
if not npmrc_scope_is_configured(lines, self.scope):
|
|
100
|
+
return self.failed(f"Scope {self.scope.name} not configured in npmrc")
|
|
101
|
+
|
|
102
|
+
return self.passed(f"Scope {self.scope.name} configured in npmrc")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class NpmrcGithubTokenValid(Check):
|
|
106
|
+
name = "npmrc.githubTokenValid"
|
|
107
|
+
depends_on = [NpmrcScopeConfigured]
|
|
108
|
+
|
|
109
|
+
def __init__(self, scope_name: str, test_package: str):
|
|
110
|
+
self.scope_name = scope_name
|
|
111
|
+
self.test_package = test_package
|
|
112
|
+
self.suggestions = {
|
|
113
|
+
OS.GENERIC: "Please check the token was copied correctly from GitHub."
|
|
114
|
+
" Ensure the token hasn't expired, or has been revoked."
|
|
115
|
+
" Also, ensure it has the correct permissions to read packages."
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def check(self) -> CheckResult:
|
|
119
|
+
package_spec = f"@{self.scope_name}/{self.test_package}"
|
|
120
|
+
command = f"npm view {package_spec} version"
|
|
121
|
+
logging.debug(f"Checking npm registry access with: {command}")
|
|
122
|
+
|
|
123
|
+
if can_run_command(command):
|
|
124
|
+
return self.passed(f"GitHub npm token is valid (can access {package_spec})")
|
|
125
|
+
else:
|
|
126
|
+
return self.failed(f"GitHub npm token is not valid (cannot access {package_spec})")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from daktari.check import Check, CheckResult
|
|
2
|
+
from daktari.os import OS
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PnpmInstalled(Check):
|
|
6
|
+
name = "pnpm.installed"
|
|
7
|
+
|
|
8
|
+
suggestions = {
|
|
9
|
+
OS.OS_X: "<cmd>brew install pnpm</cmd>",
|
|
10
|
+
OS.GENERIC: "<cmd>npm install -g pnpm</cmd>",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def check(self) -> CheckResult:
|
|
14
|
+
return self.verify_install("pnpm")
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest import mock
|
|
3
|
+
|
|
4
|
+
from daktari.check import CheckStatus
|
|
5
|
+
from daktari.checks.npmrc import (
|
|
6
|
+
NpmrcScope,
|
|
7
|
+
NpmrcScopeConfigured,
|
|
8
|
+
NpmrcGithubTokenValid,
|
|
9
|
+
get_registry_host,
|
|
10
|
+
npmrc_contains_scope_registry,
|
|
11
|
+
npmrc_contains_auth_token,
|
|
12
|
+
npmrc_scope_is_configured,
|
|
13
|
+
get_npmrc_suggestion,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
TEST_SCOPE_NAME = "my-org"
|
|
17
|
+
TEST_REGISTRY = "https://npm.pkg.github.com"
|
|
18
|
+
TEST_REGISTRY_HOST = "npm.pkg.github.com"
|
|
19
|
+
TEST_AUTH_TOKEN = "ghp_abc123xyz"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestNpmrcHelpers(unittest.TestCase):
|
|
23
|
+
def test_get_registry_host(self):
|
|
24
|
+
self.assertEqual(get_registry_host("https://npm.pkg.github.com"), "npm.pkg.github.com")
|
|
25
|
+
self.assertEqual(get_registry_host("https://registry.npmjs.org"), "registry.npmjs.org")
|
|
26
|
+
self.assertEqual(get_registry_host("https://npm.example.com/custom/path"), "npm.example.com")
|
|
27
|
+
|
|
28
|
+
def test_npmrc_contains_scope_registry_matching(self):
|
|
29
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
30
|
+
lines = [
|
|
31
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
32
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
|
|
33
|
+
]
|
|
34
|
+
self.assertTrue(npmrc_contains_scope_registry(lines, scope))
|
|
35
|
+
|
|
36
|
+
def test_npmrc_contains_scope_registry_not_matching(self):
|
|
37
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
38
|
+
lines = [
|
|
39
|
+
"@other-scope:registry=https://registry.npmjs.org\n",
|
|
40
|
+
]
|
|
41
|
+
self.assertFalse(npmrc_contains_scope_registry(lines, scope))
|
|
42
|
+
|
|
43
|
+
def test_npmrc_contains_scope_registry_empty_lines(self):
|
|
44
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
45
|
+
lines = []
|
|
46
|
+
self.assertFalse(npmrc_contains_scope_registry(lines, scope))
|
|
47
|
+
|
|
48
|
+
def test_npmrc_contains_auth_token_matching(self):
|
|
49
|
+
lines = [
|
|
50
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
51
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
|
|
52
|
+
]
|
|
53
|
+
self.assertTrue(npmrc_contains_auth_token(lines, TEST_REGISTRY))
|
|
54
|
+
|
|
55
|
+
def test_npmrc_contains_auth_token_not_matching(self):
|
|
56
|
+
lines = [
|
|
57
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
58
|
+
"//registry.npmjs.org/:_authToken=some-token\n",
|
|
59
|
+
]
|
|
60
|
+
self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
|
|
61
|
+
|
|
62
|
+
def test_npmrc_contains_auth_token_placeholder_rejected(self):
|
|
63
|
+
lines = [
|
|
64
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken=UPDATE_WITH_TOKEN\n",
|
|
65
|
+
]
|
|
66
|
+
self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
|
|
67
|
+
|
|
68
|
+
def test_npmrc_contains_auth_token_empty_rejected(self):
|
|
69
|
+
lines = [
|
|
70
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken=\n",
|
|
71
|
+
]
|
|
72
|
+
self.assertFalse(npmrc_contains_auth_token(lines, TEST_REGISTRY))
|
|
73
|
+
|
|
74
|
+
def test_npmrc_scope_is_configured_without_auth(self):
|
|
75
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=False)
|
|
76
|
+
lines = [
|
|
77
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
78
|
+
]
|
|
79
|
+
self.assertTrue(npmrc_scope_is_configured(lines, scope))
|
|
80
|
+
|
|
81
|
+
def test_npmrc_scope_is_configured_with_auth_present(self):
|
|
82
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=True)
|
|
83
|
+
lines = [
|
|
84
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
85
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
|
|
86
|
+
]
|
|
87
|
+
self.assertTrue(npmrc_scope_is_configured(lines, scope))
|
|
88
|
+
|
|
89
|
+
def test_npmrc_scope_is_configured_with_auth_missing(self):
|
|
90
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=True)
|
|
91
|
+
lines = [
|
|
92
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
93
|
+
]
|
|
94
|
+
self.assertFalse(npmrc_scope_is_configured(lines, scope))
|
|
95
|
+
|
|
96
|
+
def test_npmrc_scope_is_configured_registry_missing(self):
|
|
97
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
98
|
+
lines = [
|
|
99
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
|
|
100
|
+
]
|
|
101
|
+
self.assertFalse(npmrc_scope_is_configured(lines, scope))
|
|
102
|
+
|
|
103
|
+
def test_get_npmrc_suggestion_without_auth(self):
|
|
104
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=False)
|
|
105
|
+
suggestion = get_npmrc_suggestion(scope)
|
|
106
|
+
self.assertEqual(suggestion, f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}")
|
|
107
|
+
|
|
108
|
+
def test_get_npmrc_suggestion_with_auth(self):
|
|
109
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=True)
|
|
110
|
+
suggestion = get_npmrc_suggestion(scope)
|
|
111
|
+
expected = f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n//{TEST_REGISTRY_HOST}/:_authToken=UPDATE_WITH_TOKEN"
|
|
112
|
+
self.assertEqual(suggestion, expected)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TestNpmrcScopeConfigured(unittest.TestCase):
|
|
116
|
+
@mock.patch("daktari.checks.npmrc.get_npmrc_contents")
|
|
117
|
+
def test_returns_pass_when_scope_configured(self, mock_get_contents):
|
|
118
|
+
mock_get_contents.return_value = [
|
|
119
|
+
f"@{TEST_SCOPE_NAME}:registry={TEST_REGISTRY}\n",
|
|
120
|
+
f"//{TEST_REGISTRY_HOST}/:_authToken={TEST_AUTH_TOKEN}\n",
|
|
121
|
+
]
|
|
122
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY, require_auth_token=True)
|
|
123
|
+
result = NpmrcScopeConfigured(scope).check()
|
|
124
|
+
self.assertEqual(result.status, CheckStatus.PASS)
|
|
125
|
+
|
|
126
|
+
@mock.patch("daktari.checks.npmrc.get_npmrc_contents")
|
|
127
|
+
def test_returns_fail_when_scope_not_configured(self, mock_get_contents):
|
|
128
|
+
mock_get_contents.return_value = [
|
|
129
|
+
"@other-scope:registry=https://registry.npmjs.org\n",
|
|
130
|
+
]
|
|
131
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
132
|
+
result = NpmrcScopeConfigured(scope).check()
|
|
133
|
+
self.assertEqual(result.status, CheckStatus.FAIL)
|
|
134
|
+
|
|
135
|
+
@mock.patch("daktari.checks.npmrc.get_npmrc_contents")
|
|
136
|
+
def test_returns_fail_when_file_not_found(self, mock_get_contents):
|
|
137
|
+
mock_get_contents.side_effect = Exception("~/.npmrc does not exist")
|
|
138
|
+
scope = NpmrcScope(name=TEST_SCOPE_NAME, registry=TEST_REGISTRY)
|
|
139
|
+
result = NpmrcScopeConfigured(scope).check()
|
|
140
|
+
self.assertEqual(result.status, CheckStatus.FAIL)
|
|
141
|
+
self.assertIn("does not exist", result.summary)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestNpmrcGithubTokenValid(unittest.TestCase):
|
|
145
|
+
@mock.patch("daktari.checks.npmrc.can_run_command")
|
|
146
|
+
def test_returns_pass_when_token_valid(self, mock_can_run):
|
|
147
|
+
mock_can_run.return_value = True
|
|
148
|
+
result = NpmrcGithubTokenValid(scope_name=TEST_SCOPE_NAME, test_package="some-package").check()
|
|
149
|
+
self.assertEqual(result.status, CheckStatus.PASS)
|
|
150
|
+
mock_can_run.assert_called_once_with(f"npm view @{TEST_SCOPE_NAME}/some-package version")
|
|
151
|
+
|
|
152
|
+
@mock.patch("daktari.checks.npmrc.can_run_command")
|
|
153
|
+
def test_returns_fail_when_token_invalid(self, mock_can_run):
|
|
154
|
+
mock_can_run.return_value = False
|
|
155
|
+
result = NpmrcGithubTokenValid(scope_name=TEST_SCOPE_NAME, test_package="some-package").check()
|
|
156
|
+
self.assertEqual(result.status, CheckStatus.FAIL)
|
|
157
|
+
mock_can_run.assert_called_once_with(f"npm view @{TEST_SCOPE_NAME}/some-package version")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
unittest.main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest import mock
|
|
3
|
+
|
|
4
|
+
from daktari.check import CheckStatus
|
|
5
|
+
from daktari.checks.pnpm import PnpmInstalled
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestPnpm(unittest.TestCase):
|
|
9
|
+
@mock.patch("daktari.check.can_run_command")
|
|
10
|
+
def test_pnpm_installed_returns_pass_when_installed(self, mock_can_run_command):
|
|
11
|
+
mock_can_run_command.return_value = True
|
|
12
|
+
result = PnpmInstalled().check()
|
|
13
|
+
self.assertEqual(result.status, CheckStatus.PASS)
|
|
14
|
+
mock_can_run_command.assert_called_once_with("pnpm --version")
|
|
15
|
+
|
|
16
|
+
@mock.patch("daktari.check.can_run_command")
|
|
17
|
+
def test_pnpm_installed_returns_fail_when_not_installed(self, mock_can_run_command):
|
|
18
|
+
mock_can_run_command.return_value = False
|
|
19
|
+
result = PnpmInstalled().check()
|
|
20
|
+
self.assertEqual(result.status, CheckStatus.FAIL)
|
|
21
|
+
mock_can_run_command.assert_called_once_with("pnpm --version")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
unittest.main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: daktari
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.306
|
|
4
4
|
Summary: Assist in setting up and maintaining developer environments
|
|
5
5
|
Author-email: Matt Russell <matthew.russell@sonocent.com>
|
|
6
6
|
License: Copyright 2021 Sonocent Ltd
|
|
@@ -18,8 +18,8 @@ Requires-Dist: ansicolors==1.1.8
|
|
|
18
18
|
Requires-Dist: distro==1.9.0
|
|
19
19
|
Requires-Dist: pyfiglet==1.0.4
|
|
20
20
|
Requires-Dist: importlib_resources==6.5.2
|
|
21
|
-
Requires-Dist: packaging==
|
|
22
|
-
Requires-Dist: setuptools==80.
|
|
21
|
+
Requires-Dist: packaging==26.0
|
|
22
|
+
Requires-Dist: setuptools==80.10.2
|
|
23
23
|
Requires-Dist: requests==2.32.5
|
|
24
24
|
Requires-Dist: responses==0.25.8
|
|
25
25
|
Requires-Dist: semver==3.0.4
|
|
@@ -33,7 +33,7 @@ Requires-Dist: dpath==2.2.0
|
|
|
33
33
|
Requires-Dist: pyOpenSSL==25.3.0
|
|
34
34
|
Requires-Dist: types-pyOpenSSL==24.1.0.20240722
|
|
35
35
|
Requires-Dist: pyclip==0.7.0
|
|
36
|
-
Requires-Dist: urllib3==2.6.
|
|
36
|
+
Requires-Dist: urllib3==2.6.3
|
|
37
37
|
Dynamic: license-file
|
|
38
38
|
|
|
39
39
|
**Daktari** is a tool to help the initial setup and ongoing maintenance of developer environments. It runs a series of checks (for example, that required software is installed) and provides suggestions on how to fix the issue if the check fails.
|
|
@@ -45,7 +45,7 @@ In the root of the project repository, create a `.daktari.py` configuration file
|
|
|
45
45
|
```python
|
|
46
46
|
from daktari.checks.git import *
|
|
47
47
|
|
|
48
|
-
version = "0.0.
|
|
48
|
+
version = "0.0.306"
|
|
49
49
|
title = "My Project"
|
|
50
50
|
|
|
51
51
|
checks = [
|
|
@@ -52,7 +52,9 @@ daktari/checks/java.py
|
|
|
52
52
|
daktari/checks/kubernetes.py
|
|
53
53
|
daktari/checks/misc.py
|
|
54
54
|
daktari/checks/nodejs.py
|
|
55
|
+
daktari/checks/npmrc.py
|
|
55
56
|
daktari/checks/onepassword.py
|
|
57
|
+
daktari/checks/pnpm.py
|
|
56
58
|
daktari/checks/python.py
|
|
57
59
|
daktari/checks/ssh.py
|
|
58
60
|
daktari/checks/terraform.py
|
|
@@ -60,7 +62,9 @@ daktari/checks/test_certs.py
|
|
|
60
62
|
daktari/checks/test_intellij_idea.py
|
|
61
63
|
daktari/checks/test_java.py
|
|
62
64
|
daktari/checks/test_misc.py
|
|
65
|
+
daktari/checks/test_npmrc.py
|
|
63
66
|
daktari/checks/test_onepassword.py
|
|
67
|
+
daktari/checks/test_pnpm.py
|
|
64
68
|
daktari/checks/test_ssh.py
|
|
65
69
|
daktari/checks/test_yarn.py
|
|
66
70
|
daktari/checks/xml.py
|
|
@@ -2,8 +2,8 @@ ansicolors==1.1.8
|
|
|
2
2
|
distro==1.9.0
|
|
3
3
|
pyfiglet==1.0.4
|
|
4
4
|
importlib_resources==6.5.2
|
|
5
|
-
packaging==
|
|
6
|
-
setuptools==80.
|
|
5
|
+
packaging==26.0
|
|
6
|
+
setuptools==80.10.2
|
|
7
7
|
requests==2.32.5
|
|
8
8
|
responses==0.25.8
|
|
9
9
|
semver==3.0.4
|
|
@@ -15,7 +15,7 @@ dpath==2.2.0
|
|
|
15
15
|
pyOpenSSL==25.3.0
|
|
16
16
|
types-pyOpenSSL==24.1.0.20240722
|
|
17
17
|
pyclip==0.7.0
|
|
18
|
-
urllib3==2.6.
|
|
18
|
+
urllib3==2.6.3
|
|
19
19
|
|
|
20
20
|
[:sys_platform == "darwin"]
|
|
21
21
|
pyobjc-core==12.0
|
|
@@ -2,8 +2,8 @@ ansicolors==1.1.8
|
|
|
2
2
|
distro==1.9.0
|
|
3
3
|
pyfiglet==1.0.4
|
|
4
4
|
importlib_resources==6.5.2
|
|
5
|
-
packaging==
|
|
6
|
-
setuptools==80.
|
|
5
|
+
packaging==26.0
|
|
6
|
+
setuptools==80.10.2
|
|
7
7
|
requests==2.32.5
|
|
8
8
|
responses==0.25.8
|
|
9
9
|
semver==3.0.4
|
|
@@ -17,4 +17,4 @@ dpath==2.2.0
|
|
|
17
17
|
pyOpenSSL==25.3.0
|
|
18
18
|
types-pyOpenSSL==24.1.0.20240722
|
|
19
19
|
pyclip==0.7.0
|
|
20
|
-
urllib3==2.6.
|
|
20
|
+
urllib3==2.6.3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.300"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|