aplos-nca-saas-sdk 0.0.1__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.
- aplos_nca_saas_sdk-0.0.1/.gitignore +13 -0
- aplos_nca_saas_sdk-0.0.1/.pypirc +7 -0
- aplos_nca_saas_sdk-0.0.1/.vscode/launch.json +57 -0
- aplos_nca_saas_sdk-0.0.1/.vscode/settings.json +35 -0
- aplos_nca_saas_sdk-0.0.1/LICENSE +21 -0
- aplos_nca_saas_sdk-0.0.1/PKG-INFO +49 -0
- aplos_nca_saas_sdk-0.0.1/README.md +33 -0
- aplos_nca_saas_sdk-0.0.1/devops/code_artifacts/check_and_setup.py +44 -0
- aplos_nca_saas_sdk-0.0.1/devops/code_artifacts/environment_vars.py +36 -0
- aplos_nca_saas_sdk-0.0.1/devops/code_artifacts/local_test_support.py +68 -0
- aplos_nca_saas_sdk-0.0.1/devops/code_artifacts/main.py +224 -0
- aplos_nca_saas_sdk-0.0.1/devops/code_artifacts/pip_code_artifact_setup.py +66 -0
- aplos_nca_saas_sdk-0.0.1/devops/pypi/build.py +128 -0
- aplos_nca_saas_sdk-0.0.1/devops/pypi/readme.md +23 -0
- aplos_nca_saas_sdk-0.0.1/devops/requirements.txt +4 -0
- aplos_nca_saas_sdk-0.0.1/docs/images/API_Configuration_blur.png +0 -0
- aplos_nca_saas_sdk-0.0.1/docs/images/aplos-nca-commandline-cropped.png +0 -0
- aplos_nca_saas_sdk-0.0.1/docs/images/aplos-nca-commandline.png +0 -0
- aplos_nca_saas_sdk-0.0.1/mypy.ini +15 -0
- aplos_nca_saas_sdk-0.0.1/mypy_checks.py +25 -0
- aplos_nca_saas_sdk-0.0.1/pyproject.toml +30 -0
- aplos_nca_saas_sdk-0.0.1/readme.md +33 -0
- aplos_nca_saas_sdk-0.0.1/requirements.txt +5 -0
- aplos_nca_saas_sdk-0.0.1/run-checks.sh +4 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/__init__.py +0 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/aws_resources/aws_cognito.py +188 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/aws_resources/aws_s3_presigned_payload.py +49 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/aws_resources/aws_s3_presigned_upload.py +116 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/configuration_single_ev.json +51 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/meta_data.json +17 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/single_ev.csv +121 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/integration_test_base.py +34 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/integration_test_factory.py +62 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/integration_test_suite.py +84 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/main.py +28 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/readme.md +18 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/tests/app_configuration_test.py +30 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/tests/app_execution_test.py +5 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/tests/app_login_test.py +32 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/integration_testing/tests/app_validation_test.py +5 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/nca_resources/nca_app_configuration.py +69 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/nca_resources/nca_endpoints.py +54 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/nca_resources/nca_executions.py +378 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/nca_resources/nca_login.py +104 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/utilities/commandline_args.py +332 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/utilities/environment_services.py +81 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/utilities/environment_vars.py +23 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/utilities/http_utility.py +30 -0
- aplos_nca_saas_sdk-0.0.1/src/aplos_nca_saas_sdk/version.py +4 -0
- aplos_nca_saas_sdk-0.0.1/tests/__init__.py +0 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
3
|
+
// Hover to view descriptions of existing attributes.
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
5
|
+
"version": "0.2.0",
|
6
|
+
"configurations": [
|
7
|
+
{
|
8
|
+
"name": "Python Debugger: Current File",
|
9
|
+
"type": "debugpy",
|
10
|
+
"request": "launch",
|
11
|
+
"program": "${file}",
|
12
|
+
"console": "integratedTerminal",
|
13
|
+
"env": {
|
14
|
+
|
15
|
+
"ENVRIONMENT": "dev",
|
16
|
+
"DEBUG_MODE": "True",
|
17
|
+
"PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/src:${workspaceFolder}/devops:${workspaceFolder}:${workspaceFolder}/tests"
|
18
|
+
}
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"name": "Build & Deploy To PyPi",
|
22
|
+
"type": "debugpy",
|
23
|
+
"request": "launch",
|
24
|
+
"program": "${workspaceFolder}/devops/pypi/build.py",
|
25
|
+
"console": "integratedTerminal"
|
26
|
+
},
|
27
|
+
|
28
|
+
{
|
29
|
+
"name": "Build & Deploy To Code Artifacts",
|
30
|
+
"type": "debugpy",
|
31
|
+
"request": "launch",
|
32
|
+
"program": "${workspaceFolder}/devops/code_artifacts/main.py",
|
33
|
+
"console": "integratedTerminal",
|
34
|
+
"env": {
|
35
|
+
|
36
|
+
"ENVRIONMENT": "dev",
|
37
|
+
"DEBUG_MODE": "True",
|
38
|
+
"PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/src:${workspaceFolder}/devops:"
|
39
|
+
}
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"name": "Run Integration Tests - dev",
|
43
|
+
"type": "debugpy",
|
44
|
+
"request": "launch",
|
45
|
+
"program": "${workspaceFolder}/src/aplos_nca_saas_sdk/integration_testing/main.py",
|
46
|
+
"console": "integratedTerminal",
|
47
|
+
"env": {
|
48
|
+
|
49
|
+
"ENVRIONMENT": "dev",
|
50
|
+
"ENV_FILE": ".env.development",
|
51
|
+
"DEBUG_MODE": "True",
|
52
|
+
"PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/src:${workspaceFolder}/devops:${workspaceFolder}/devops"
|
53
|
+
}
|
54
|
+
},
|
55
|
+
|
56
|
+
]
|
57
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
{
|
2
|
+
|
3
|
+
"python.testing.pytestEnabled": false,
|
4
|
+
"python.testing.unittestEnabled": true,
|
5
|
+
"python.testing.unittestArgs": [
|
6
|
+
"-v",
|
7
|
+
"-s",
|
8
|
+
"./tests",
|
9
|
+
"-p",
|
10
|
+
"*_test.py"
|
11
|
+
],
|
12
|
+
"python.testing.cwd": "${workspaceFolder}",
|
13
|
+
"python.analysis.extraPaths": [
|
14
|
+
"${workspaceFolder}/src",
|
15
|
+
"${workspaceFolder}/src/aplos_nca_saas_sdk",
|
16
|
+
"${workspaceFolder}/tests",
|
17
|
+
"${workspaceFolder}/devops",
|
18
|
+
"${workspaceFolder}/devops/cdk",
|
19
|
+
|
20
|
+
],
|
21
|
+
"python.analysis.typeCheckingMode": "off",
|
22
|
+
"pylint.args": [
|
23
|
+
"--max-line-length=150"
|
24
|
+
],
|
25
|
+
"[python]": {
|
26
|
+
|
27
|
+
"editor.formatOnSave": true,
|
28
|
+
// "editor.codeActionsOnSave": {
|
29
|
+
// "source.fixAll": "explicit",
|
30
|
+
// "source.organizeImports": "explicit"
|
31
|
+
// },
|
32
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
33
|
+
},
|
34
|
+
|
35
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) [year] [fullname]
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,49 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: aplos_nca_saas_sdk
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Aplos NCA SaaS SDK
|
5
|
+
Author-email: Eric Wilson <eric.wilson@aplosanalytics.com>
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
7
|
+
Classifier: Operating System :: OS Independent
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Requires-Python: >=3.10
|
10
|
+
Requires-Dist: aws-lambda-powertools==2.38.1
|
11
|
+
Requires-Dist: boto3==1.34.110
|
12
|
+
Requires-Dist: pyjwt==2.9.0
|
13
|
+
Requires-Dist: python-dotenv==1.0.1
|
14
|
+
Requires-Dist: requests==2.31.0
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
|
17
|
+
# Aplos NCA SaaS SDK for python
|
18
|
+
|
19
|
+
You can use 🐍 python to perform analysis with Aplos NCA using API calls via secure HTTPS requests.
|
20
|
+
|
21
|
+
> 👉 We use this for our demos 🚀🚀 👈
|
22
|
+
> We'll keep this maintained as we use it for our customer demos. So keep coming back for new features.
|
23
|
+
|
24
|
+
|
25
|
+
## API Settings
|
26
|
+
|
27
|
+
> 🛑 A valid Aplos NCA Account is required. 🛑
|
28
|
+
> In order to run the executions and connect to our system, you will need a valid account with an active subscription.
|
29
|
+
>
|
30
|
+
> You'll need your username (email address and a password) a Cognito Client Id
|
31
|
+
> which allows the USER_PASSWORD_AUTH flow, and your API URL.
|
32
|
+
>
|
33
|
+
> The Client Id (aka Cognito Client Key) and API URL can be found in your user account.
|
34
|
+
> Your username is your email address and your password would have been provided to you during your
|
35
|
+
> onboarding or with a "forgot password" request and reset.
|
36
|
+
|
37
|
+
|
38
|
+
## API
|
39
|
+
You can use the API to integrate the Aplos NCA SaaS Cloud Solution into your workflow.
|
40
|
+
|
41
|
+
## API Integration Testing
|
42
|
+
You can use the integration testing to test your environment. You can also use the Integration Testing modules
|
43
|
+
to learn how to use the API.
|
44
|
+
|
45
|
+
## Source
|
46
|
+
Full source code can also be found on [GitHub](https://github.com/AplosAnalytics/Aplos-NCA-SaaS-SDK)
|
47
|
+
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Aplos NCA SaaS SDK for python
|
2
|
+
|
3
|
+
You can use 🐍 python to perform analysis with Aplos NCA using API calls via secure HTTPS requests.
|
4
|
+
|
5
|
+
> 👉 We use this for our demos 🚀🚀 👈
|
6
|
+
> We'll keep this maintained as we use it for our customer demos. So keep coming back for new features.
|
7
|
+
|
8
|
+
|
9
|
+
## API Settings
|
10
|
+
|
11
|
+
> 🛑 A valid Aplos NCA Account is required. 🛑
|
12
|
+
> In order to run the executions and connect to our system, you will need a valid account with an active subscription.
|
13
|
+
>
|
14
|
+
> You'll need your username (email address and a password) a Cognito Client Id
|
15
|
+
> which allows the USER_PASSWORD_AUTH flow, and your API URL.
|
16
|
+
>
|
17
|
+
> The Client Id (aka Cognito Client Key) and API URL can be found in your user account.
|
18
|
+
> Your username is your email address and your password would have been provided to you during your
|
19
|
+
> onboarding or with a "forgot password" request and reset.
|
20
|
+
|
21
|
+
|
22
|
+
## API
|
23
|
+
You can use the API to integrate the Aplos NCA SaaS Cloud Solution into your workflow.
|
24
|
+
|
25
|
+
## API Integration Testing
|
26
|
+
You can use the integration testing to test your environment. You can also use the Integration Testing modules
|
27
|
+
to learn how to use the API.
|
28
|
+
|
29
|
+
## Source
|
30
|
+
Full source code can also be found on [GitHub](https://github.com/AplosAnalytics/Aplos-NCA-SaaS-SDK)
|
31
|
+
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import subprocess
|
2
|
+
import sys
|
3
|
+
import os
|
4
|
+
from aws_lambda_powertools import Logger
|
5
|
+
from devops.code_artifacts.environment_vars import envrionment_vars
|
6
|
+
|
7
|
+
logger = Logger()
|
8
|
+
|
9
|
+
|
10
|
+
def is_logged_in():
|
11
|
+
"""Check to see if i'm logged in"""
|
12
|
+
try:
|
13
|
+
commands = ["aws", "codeartifact", "list-repositories"]
|
14
|
+
profile = envrionment_vars.code_artifact_repository_profile
|
15
|
+
if profile:
|
16
|
+
commands.append("--profile")
|
17
|
+
commands.append(profile)
|
18
|
+
result = subprocess.run(
|
19
|
+
commands,
|
20
|
+
stdout=subprocess.PIPE,
|
21
|
+
stderr=subprocess.PIPE,
|
22
|
+
text=True,
|
23
|
+
check=True,
|
24
|
+
)
|
25
|
+
return result.returncode == 0
|
26
|
+
except Exception as e: # pylint: disable=w0718
|
27
|
+
logger.exception(f"Error checking login status: {e}")
|
28
|
+
return False
|
29
|
+
|
30
|
+
|
31
|
+
def run_setup():
|
32
|
+
"""Run the setup"""
|
33
|
+
try:
|
34
|
+
subprocess.check_call([sys.executable, "./devops/pip_code_artifact_setup.py"])
|
35
|
+
except subprocess.CalledProcessError as e:
|
36
|
+
logger.exception(f"Setup script failed: {e}")
|
37
|
+
sys.exit(1)
|
38
|
+
|
39
|
+
|
40
|
+
if not is_logged_in():
|
41
|
+
logger.info("Not logged in to CodeArtifact. Running setup script...")
|
42
|
+
run_setup()
|
43
|
+
else:
|
44
|
+
logger.info("Already logged in to CodeArtifact.")
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
|
4
|
+
class EnvironmentVars:
|
5
|
+
"""Environment variables"""
|
6
|
+
|
7
|
+
def __init__(self):
|
8
|
+
pass
|
9
|
+
|
10
|
+
@property
|
11
|
+
def code_artifact_account_number(self):
|
12
|
+
"""Gets thd code artifacti account number"""
|
13
|
+
return os.getenv("CODEARTIFACT_AWS_ACCCOUNT_NUMBER") # "974817967438"
|
14
|
+
|
15
|
+
@property
|
16
|
+
def code_artifact_domain(self):
|
17
|
+
"""Gets the code artifact domain"""
|
18
|
+
return os.getenv("CODEARTIFACT_DOMAIN") # "aplos-nca"
|
19
|
+
|
20
|
+
@property
|
21
|
+
def code_artifact_repository_name(self):
|
22
|
+
"""Gets the code artifact repository name"""
|
23
|
+
return os.getenv("CODEARTIFACT_REPOSITORY_NAME") # "python"
|
24
|
+
|
25
|
+
@property
|
26
|
+
def code_artifact_repository_region(self):
|
27
|
+
"""Gets the code artifact repository region"""
|
28
|
+
return os.getenv("CODEARTIFACT_REPOSITORY_REGION") # "us-east-1"
|
29
|
+
|
30
|
+
@property
|
31
|
+
def code_artifact_repository_profile(self):
|
32
|
+
"""Gets the code artifact repository profile"""
|
33
|
+
return os.getenv("CODEARTIFACT_REPOSITORY_PROFILE")
|
34
|
+
|
35
|
+
|
36
|
+
envrionment_vars = EnvironmentVars()
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
from typing import Optional
|
4
|
+
from pathlib import Path
|
5
|
+
from dotenv import load_dotenv
|
6
|
+
from aws_lambda_powertools import Logger
|
7
|
+
|
8
|
+
logger = Logger()
|
9
|
+
|
10
|
+
|
11
|
+
class LocalTestingSupport:
|
12
|
+
def __init__(self) -> None:
|
13
|
+
pass
|
14
|
+
|
15
|
+
def load_local_environment(
|
16
|
+
self, *, path: Optional[str] = None, file_name: str = ".env.development"
|
17
|
+
):
|
18
|
+
"""Loads the local environment"""
|
19
|
+
environment_file: str | None = None
|
20
|
+
if path:
|
21
|
+
environment_file = os.path.join(path, file_name)
|
22
|
+
else:
|
23
|
+
environment_file = self.__find_env_file(file_name)
|
24
|
+
|
25
|
+
if environment_file is None:
|
26
|
+
raise RuntimeError(f"Failed to locate evnrionment file: {file_name}")
|
27
|
+
|
28
|
+
if os.path.exists(environment_file):
|
29
|
+
logger.debug(f"loading profile: {environment_file}")
|
30
|
+
load_dotenv(environment_file, override=True)
|
31
|
+
|
32
|
+
aws_profile = os.getenv("AWS_PROFILE")
|
33
|
+
logger.debug(f"aws_profile: {aws_profile}")
|
34
|
+
else:
|
35
|
+
raise RuntimeError(
|
36
|
+
f"Failed to locate evnrionment file: {file_name} at {environment_file}"
|
37
|
+
)
|
38
|
+
|
39
|
+
def __find_env_file(self, file_name: str) -> Optional[str]:
|
40
|
+
"""Finds the environment file"""
|
41
|
+
for i in range(10):
|
42
|
+
path = str(Path(__file__).parents[i].absolute())
|
43
|
+
env_file = os.path.join(path, file_name)
|
44
|
+
if os.path.exists(env_file):
|
45
|
+
return env_file
|
46
|
+
|
47
|
+
return None
|
48
|
+
|
49
|
+
def load_event_file(self, full_path: str) -> dict:
|
50
|
+
"""Loads an event file"""
|
51
|
+
if not os.path.exists(full_path):
|
52
|
+
raise RuntimeError(f"Failed to locate event file: {full_path}")
|
53
|
+
|
54
|
+
event: dict = {}
|
55
|
+
with open(full_path, mode="r", encoding="utf-8") as json_file:
|
56
|
+
event = json.load(json_file)
|
57
|
+
|
58
|
+
if "message" in event:
|
59
|
+
tmp = event.get("message")
|
60
|
+
if isinstance(tmp, dict):
|
61
|
+
event = tmp
|
62
|
+
|
63
|
+
if "event" in event:
|
64
|
+
tmp = event.get("event")
|
65
|
+
if isinstance(tmp, dict):
|
66
|
+
event = tmp
|
67
|
+
|
68
|
+
return event
|
@@ -0,0 +1,224 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
import subprocess
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
import toml
|
9
|
+
from aws_lambda_powertools import Logger
|
10
|
+
from devops.code_artifacts.environment_vars import envrionment_vars
|
11
|
+
|
12
|
+
logger = Logger()
|
13
|
+
|
14
|
+
|
15
|
+
class AWSCodeArtifactsService:
|
16
|
+
"""AWS Code Artifactis"""
|
17
|
+
|
18
|
+
def __init__(self) -> None:
|
19
|
+
self.domain = envrionment_vars.code_artifact_domain
|
20
|
+
self.repository = envrionment_vars.code_artifact_repository_name
|
21
|
+
self.account = envrionment_vars.code_artifact_account_number
|
22
|
+
self.profile = envrionment_vars.code_artifact_repository_profile
|
23
|
+
|
24
|
+
def build(self):
|
25
|
+
"""Build the artifacts"""
|
26
|
+
|
27
|
+
project_root = Path(__file__).parents[2]
|
28
|
+
|
29
|
+
# extact the version
|
30
|
+
pyproject_toml = os.path.join(project_root, "pyproject.toml")
|
31
|
+
|
32
|
+
if not os.path.exists(pyproject_toml):
|
33
|
+
raise RuntimeError(
|
34
|
+
f"The pyproject.toml file ({pyproject_toml}) not found. "
|
35
|
+
"Please check the path and try again."
|
36
|
+
)
|
37
|
+
|
38
|
+
# get the "packages" from the toml file
|
39
|
+
packages_path: str | None = None
|
40
|
+
with open(pyproject_toml, "r", encoding="utf-8") as file:
|
41
|
+
pyproject_data = toml.load(file)
|
42
|
+
packages_path = pyproject_data.get("project", {}).get("source")
|
43
|
+
|
44
|
+
if not packages_path:
|
45
|
+
raise RuntimeError(
|
46
|
+
"The packages path is not defined in the pyproject.toml file."
|
47
|
+
)
|
48
|
+
version_file = os.path.join(project_root, packages_path, "version.py")
|
49
|
+
|
50
|
+
self.extract_version_and_write_to_file(pyproject_toml, version_file)
|
51
|
+
# do the build
|
52
|
+
self.__run_local_clean_up(project_root)
|
53
|
+
self.__run_login()
|
54
|
+
self.__run_build()
|
55
|
+
|
56
|
+
def publish(self):
|
57
|
+
"""Publish the artifacts"""
|
58
|
+
self.__run_publish()
|
59
|
+
|
60
|
+
def __run_local_clean_up(self, project_root: str):
|
61
|
+
"""run a local clean up and remove older items in the dist directory"""
|
62
|
+
|
63
|
+
dist_dir = os.path.join(project_root, "dist")
|
64
|
+
if os.path.exists(dist_dir):
|
65
|
+
# clear it out
|
66
|
+
shutil.rmtree(dist_dir)
|
67
|
+
|
68
|
+
def run_remote_clean_up(self):
|
69
|
+
"""
|
70
|
+
Clean out older versions
|
71
|
+
"""
|
72
|
+
logger.warning("warning/info: older versions are not being cleaned out.")
|
73
|
+
|
74
|
+
def extract_version_and_write_to_file(self, pyproject_toml: str, version_file: str):
|
75
|
+
"""
|
76
|
+
extract the version number from the pyproject.toml file and write it
|
77
|
+
to the version.py file
|
78
|
+
"""
|
79
|
+
if not os.path.exists(pyproject_toml):
|
80
|
+
raise FileNotFoundError(
|
81
|
+
f"The pyproject.toml file ({pyproject_toml}) not found. "
|
82
|
+
"Please check the path and try again."
|
83
|
+
)
|
84
|
+
|
85
|
+
with open(pyproject_toml, "r", encoding="utf-8") as file:
|
86
|
+
pyproject_data = toml.load(file)
|
87
|
+
version = pyproject_data["project"]["version"]
|
88
|
+
with open(version_file, "w", encoding="utf-8") as f:
|
89
|
+
f.write(f"__version__ = '{version}'\n")
|
90
|
+
|
91
|
+
def __run_login(self):
|
92
|
+
"""log into code artifact"""
|
93
|
+
profile_flag: str = ""
|
94
|
+
|
95
|
+
if self.profile:
|
96
|
+
profile_flag = f"--profile {self.profile}"
|
97
|
+
commands = f"aws codeartifact login --tool pip --domain {self.domain} --repository {self.repository} {profile_flag}".split()
|
98
|
+
self.run_commands(commands=commands)
|
99
|
+
|
100
|
+
def __run_build(self):
|
101
|
+
"""Run python build commands"""
|
102
|
+
self.run_commands(["python", "-m", "build"])
|
103
|
+
|
104
|
+
def __run_publish(self):
|
105
|
+
"""publish to code artifact"""
|
106
|
+
self.connect_artifact_to_twine()
|
107
|
+
repo_url = self.get_repo_url()
|
108
|
+
|
109
|
+
token = self.get_auth_token()
|
110
|
+
# Set up the environment variables for the upload command
|
111
|
+
env = os.environ.copy()
|
112
|
+
env["TWINE_USERNAME"] = "aws"
|
113
|
+
env["TWINE_PASSWORD"] = token
|
114
|
+
|
115
|
+
commands = [
|
116
|
+
"python",
|
117
|
+
"-m",
|
118
|
+
"twine",
|
119
|
+
"upload",
|
120
|
+
"--repository-url",
|
121
|
+
repo_url,
|
122
|
+
]
|
123
|
+
|
124
|
+
# if self.profile:
|
125
|
+
# commands.extend(["--profile", f"{self.profile}"])
|
126
|
+
|
127
|
+
commands.extend(["dist/*"])
|
128
|
+
|
129
|
+
self.run_commands(
|
130
|
+
commands=commands,
|
131
|
+
env=env,
|
132
|
+
)
|
133
|
+
|
134
|
+
def connect_artifact_to_twine(self) -> None:
|
135
|
+
"""Connect twine to codeartifact"""
|
136
|
+
profile_flag = ""
|
137
|
+
if self.profile:
|
138
|
+
profile_flag = f"--profile {self.profile}"
|
139
|
+
commands = f"aws codeartifact login {profile_flag} --tool twine --domain {self.domain} --repository {self.repository}".split()
|
140
|
+
self.run_commands(commands=commands, capture_output=True)
|
141
|
+
|
142
|
+
def get_repo_url(self) -> str:
|
143
|
+
"""get the artifact repo url"""
|
144
|
+
get_url_command = [
|
145
|
+
"aws",
|
146
|
+
"codeartifact",
|
147
|
+
"get-repository-endpoint",
|
148
|
+
"--domain",
|
149
|
+
self.domain,
|
150
|
+
"--domain-owner",
|
151
|
+
self.account,
|
152
|
+
"--repository",
|
153
|
+
self.repository,
|
154
|
+
"--format",
|
155
|
+
"pypi",
|
156
|
+
]
|
157
|
+
|
158
|
+
if self.profile:
|
159
|
+
get_url_command.extend(["--profile", self.profile])
|
160
|
+
|
161
|
+
repo_url = self.run_commands(get_url_command, capture_output=True)
|
162
|
+
|
163
|
+
if not repo_url:
|
164
|
+
raise RuntimeError("Unable to get the repository url")
|
165
|
+
repo_url = self.get_url(repo_url)
|
166
|
+
return repo_url
|
167
|
+
|
168
|
+
def get_auth_token(self) -> str:
|
169
|
+
"""get the auth token"""
|
170
|
+
commands = [
|
171
|
+
"aws",
|
172
|
+
"codeartifact",
|
173
|
+
"get-authorization-token",
|
174
|
+
"--domain",
|
175
|
+
self.domain,
|
176
|
+
"--domain-owner",
|
177
|
+
self.account,
|
178
|
+
"--query",
|
179
|
+
"authorizationToken",
|
180
|
+
"--output",
|
181
|
+
"text",
|
182
|
+
]
|
183
|
+
|
184
|
+
token = self.run_commands(commands=commands, capture_output=True)
|
185
|
+
|
186
|
+
return token
|
187
|
+
|
188
|
+
def get_url(self, payload: str):
|
189
|
+
"""get the url from the payload"""
|
190
|
+
value: dict = json.loads(payload)
|
191
|
+
url = value.get("repositoryEndpoint")
|
192
|
+
|
193
|
+
return url
|
194
|
+
|
195
|
+
def run_commands(
|
196
|
+
self, commands: List[str], capture_output: bool = False, env=None
|
197
|
+
) -> str | None:
|
198
|
+
"""centralized area for running process commands"""
|
199
|
+
try:
|
200
|
+
# Run the publish command
|
201
|
+
result = subprocess.run(
|
202
|
+
commands,
|
203
|
+
check=True,
|
204
|
+
capture_output=capture_output,
|
205
|
+
env=env, # pass any environment vars
|
206
|
+
)
|
207
|
+
|
208
|
+
if capture_output:
|
209
|
+
output = result.stdout.decode().strip()
|
210
|
+
return output
|
211
|
+
|
212
|
+
except subprocess.CalledProcessError as e:
|
213
|
+
logger.exception(f"An error occurred: {e}")
|
214
|
+
|
215
|
+
|
216
|
+
def main():
|
217
|
+
"""build the artifacts"""
|
218
|
+
artifacts: AWSCodeArtifactsService = AWSCodeArtifactsService()
|
219
|
+
artifacts.build()
|
220
|
+
artifacts.publish()
|
221
|
+
|
222
|
+
|
223
|
+
if __name__ == "__main__":
|
224
|
+
main()
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import os
|
2
|
+
import subprocess
|
3
|
+
from aws_lambda_powertools import Logger
|
4
|
+
from devops.code_artifacts.environment_vars import envrionment_vars
|
5
|
+
|
6
|
+
logger = Logger()
|
7
|
+
|
8
|
+
|
9
|
+
def setup_codeartifact_pip(
|
10
|
+
account_number: str,
|
11
|
+
domain: str,
|
12
|
+
repository: str,
|
13
|
+
region: str,
|
14
|
+
profile: str | None = None,
|
15
|
+
):
|
16
|
+
# Get the AWS account ID
|
17
|
+
# sts_client = boto3.client('sts')
|
18
|
+
# account_id = sts_client.get_caller_identity().get('Account')
|
19
|
+
|
20
|
+
# Authenticate pip with CodeArtifact
|
21
|
+
login_command = [
|
22
|
+
"aws",
|
23
|
+
"codeartifact",
|
24
|
+
"login",
|
25
|
+
"--tool",
|
26
|
+
"pip",
|
27
|
+
"--domain",
|
28
|
+
domain,
|
29
|
+
"--domain-owner",
|
30
|
+
account_number,
|
31
|
+
"--repository",
|
32
|
+
repository,
|
33
|
+
"--region",
|
34
|
+
region,
|
35
|
+
]
|
36
|
+
if profile:
|
37
|
+
login_command.extend(["--profile", profile])
|
38
|
+
|
39
|
+
result = subprocess.run(login_command, capture_output=True, text=True, check=True)
|
40
|
+
|
41
|
+
if result.returncode != 0:
|
42
|
+
logger.error("Failed to authenticate pip with CodeArtifact.")
|
43
|
+
logger.error("Command output:", result.stdout)
|
44
|
+
logger.error("Error output:", result.stderr)
|
45
|
+
raise RuntimeError("CodeArtifact login failed")
|
46
|
+
|
47
|
+
logger.info("Successfully authenticated pip with CodeArtifact.")
|
48
|
+
logger.info("Command output:", result.stdout)
|
49
|
+
|
50
|
+
logger.info(
|
51
|
+
f"Configured pip to use CodeArtifact repository {repository} in domain {domain}."
|
52
|
+
)
|
53
|
+
|
54
|
+
|
55
|
+
def main():
|
56
|
+
setup_codeartifact_pip(
|
57
|
+
account_number=envrionment_vars.code_artifact_account_number,
|
58
|
+
domain=envrionment_vars.code_artifact_domain,
|
59
|
+
repository=envrionment_vars.code_artifact_repository_name,
|
60
|
+
region=envrionment_vars.code_artifact_repository_region,
|
61
|
+
profile=envrionment_vars.code_artifact_repository_profile,
|
62
|
+
)
|
63
|
+
|
64
|
+
|
65
|
+
if __name__ == "__main__":
|
66
|
+
main()
|