dxlabs 0.1.0__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.
- dxlabs-0.1.0/.gitignore +13 -0
- dxlabs-0.1.0/PKG-INFO +47 -0
- dxlabs-0.1.0/README.md +35 -0
- dxlabs-0.1.0/pyproject.toml +25 -0
- dxlabs-0.1.0/src/dxlabs/cli.py +143 -0
- dxlabs-0.1.0/src/dxlabs/config.py +67 -0
- dxlabs-0.1.0/src/dxlabs/core/env_manager.py +72 -0
- dxlabs-0.1.0/src/dxlabs/providers/s3.py +68 -0
dxlabs-0.1.0/.gitignore
ADDED
dxlabs-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dxlabs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool by DX Labs to manage project assets, starting with .env on S3
|
|
5
|
+
Author-email: DX Labs <contact@dxlabs.io>
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: boto3>=1.34.0
|
|
8
|
+
Requires-Dist: click>=8.1.0
|
|
9
|
+
Requires-Dist: pydantic-settings>=2.2.0
|
|
10
|
+
Requires-Dist: rich>=13.7.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# dxlabs
|
|
14
|
+
|
|
15
|
+
A Python CLI tool by DX Labs to manage project assets. Currently supporting `.env` file management on AWS S3.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install dxlabs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Initialize configuration
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
dxlabs init --bucket my-env-bucket --project my-project-name --profile my-aws-profile
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Push local .env to S3
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
dxlabs env push --env development
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Pull from S3 to local .env
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
dxlabs env pull --env staging
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- **Multi-Environment Support**: Specify environments like `dev`, `staging`, `prod`.
|
|
46
|
+
- **S3 Backend**: Secure storage in your own AWS S3 bucket.
|
|
47
|
+
- **CI/CD Integration**: Supports AWS environment variables for credentials.
|
dxlabs-0.1.0/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# dxlabs
|
|
2
|
+
|
|
3
|
+
A Python CLI tool by DX Labs to manage project assets. Currently supporting `.env` file management on AWS S3.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install dxlabs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Initialize configuration
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
dxlabs init --bucket my-env-bucket --project my-project-name --profile my-aws-profile
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Push local .env to S3
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
dxlabs env push --env development
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Pull from S3 to local .env
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
dxlabs env pull --env staging
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Multi-Environment Support**: Specify environments like `dev`, `staging`, `prod`.
|
|
34
|
+
- **S3 Backend**: Secure storage in your own AWS S3 bucket.
|
|
35
|
+
- **CI/CD Integration**: Supports AWS environment variables for credentials.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dxlabs"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A CLI tool by DX Labs to manage project assets, starting with .env on S3"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "DX Labs", email = "contact@dxlabs.io" }
|
|
7
|
+
]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"boto3>=1.34.0",
|
|
10
|
+
"click>=8.1.0",
|
|
11
|
+
"rich>=13.7.0",
|
|
12
|
+
"pydantic-settings>=2.2.0"
|
|
13
|
+
]
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
requires-python = ">=3.9"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
dxlabs = "dxlabs.cli:main"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/dxlabs"]
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["hatchling"]
|
|
25
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for dxlabs.
|
|
3
|
+
Defines the command structure and handles user interaction for asset management.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from .config import load_config, save_config
|
|
13
|
+
from .core.env_manager import EnvManager
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group()
|
|
19
|
+
def main():
|
|
20
|
+
"""
|
|
21
|
+
dxlabs: DX Labs CLI for project asset management.
|
|
22
|
+
A tool to manage environment files and other project resources efficiently.
|
|
23
|
+
"""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@main.command()
|
|
28
|
+
@click.option(
|
|
29
|
+
"--bucket", required=True, help="The AWS S3 bucket name to use for storage."
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
"--project", required=True, help="A unique project name (used as a key prefix)."
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--region",
|
|
36
|
+
default="ap-northeast-1",
|
|
37
|
+
help="The AWS region to use. Defaults to ap-northeast-1.",
|
|
38
|
+
)
|
|
39
|
+
@click.option(
|
|
40
|
+
"--profile", default=None, help="The AWS profile name to use for credentials."
|
|
41
|
+
)
|
|
42
|
+
def init(bucket, project, region, profile):
|
|
43
|
+
"""
|
|
44
|
+
Initialize dxlabs configuration for the current project.
|
|
45
|
+
Saves settings to a local .dxlabs.json file.
|
|
46
|
+
"""
|
|
47
|
+
save_config(bucket, project, region, profile)
|
|
48
|
+
profile_display = profile if profile else "default/env-var"
|
|
49
|
+
console.print(
|
|
50
|
+
Panel(
|
|
51
|
+
f"Configuration saved to [bold cyan].dxlabs.json[/bold cyan]\n"
|
|
52
|
+
f"Bucket: [green]{bucket}[/green]\n"
|
|
53
|
+
f"Project: [green]{project}[/green]\n"
|
|
54
|
+
f"Region: [green]{region}[/green]\n"
|
|
55
|
+
f"Profile: [green]{profile_display}[/green]",
|
|
56
|
+
title="Success",
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@main.group()
|
|
62
|
+
def env():
|
|
63
|
+
"""
|
|
64
|
+
Manage project environment (.env) files.
|
|
65
|
+
Sub-commands for pushing to and pulling from the cloud.
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@env.command(name="push")
|
|
71
|
+
@click.option(
|
|
72
|
+
"--env",
|
|
73
|
+
"env_name",
|
|
74
|
+
default="development",
|
|
75
|
+
help="The environment name (e.g., 'dev', 'prod').",
|
|
76
|
+
)
|
|
77
|
+
@click.option(
|
|
78
|
+
"--file",
|
|
79
|
+
default=".env",
|
|
80
|
+
help="Path to the local file to upload. Defaults to '.env'.",
|
|
81
|
+
)
|
|
82
|
+
def env_push(env_name, file):
|
|
83
|
+
"""
|
|
84
|
+
Upload a local .env file to S3.
|
|
85
|
+
"""
|
|
86
|
+
config = load_config()
|
|
87
|
+
if not config:
|
|
88
|
+
console.print(
|
|
89
|
+
"[red]Error: Project not initialized. Run 'dxlabs init' first.[/red]"
|
|
90
|
+
)
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
if not os.path.exists(file):
|
|
94
|
+
console.print(f"[red]Error: Local file '{file}' not found.[/red]")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
manager = EnvManager(config)
|
|
98
|
+
remote_key = manager.get_remote_key(env_name)
|
|
99
|
+
|
|
100
|
+
console.print(
|
|
101
|
+
f"Uploading [cyan]{file}[/cyan] to [bold]s3://{config.bucket}/{remote_key}[/bold]..."
|
|
102
|
+
)
|
|
103
|
+
if manager.push(file, env_name):
|
|
104
|
+
console.print("[bold green]Successfully uploaded![/bold green]")
|
|
105
|
+
else:
|
|
106
|
+
console.print("[bold red]Upload failed.[/bold red]")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@env.command(name="pull")
|
|
110
|
+
@click.option(
|
|
111
|
+
"--env",
|
|
112
|
+
"env_name",
|
|
113
|
+
default="development",
|
|
114
|
+
help="The environment name to download from.",
|
|
115
|
+
)
|
|
116
|
+
@click.option(
|
|
117
|
+
"--file", default=".env", help="The local destination path. Defaults to '.env'."
|
|
118
|
+
)
|
|
119
|
+
def env_pull(env_name, file):
|
|
120
|
+
"""
|
|
121
|
+
Download a .env file from S3 to the local filesystem.
|
|
122
|
+
"""
|
|
123
|
+
config = load_config()
|
|
124
|
+
if not config:
|
|
125
|
+
console.print(
|
|
126
|
+
"[red]Error: Project not initialized. Run 'dxlabs init' first.[/red]"
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
manager = EnvManager(config)
|
|
131
|
+
remote_key = manager.get_remote_key(env_name)
|
|
132
|
+
|
|
133
|
+
console.print(
|
|
134
|
+
f"Downloading from [bold]s3://{config.bucket}/{remote_key}[/bold] to [cyan]{file}[/cyan]..."
|
|
135
|
+
)
|
|
136
|
+
if manager.pull(env_name, file):
|
|
137
|
+
console.print("[bold green]Successfully downloaded![/bold green]")
|
|
138
|
+
else:
|
|
139
|
+
console.print("[bold red]Download failed.[/bold red]")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management for the dxlabs CLI.
|
|
3
|
+
Handles loading and saving of the .dxlabs.json configuration file.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
CONFIG_FILE = ".dxlabs.json"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Config(BaseModel):
|
|
16
|
+
"""
|
|
17
|
+
Configuration model for dxlabs project settings.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
bucket (str): The AWS S3 bucket name.
|
|
21
|
+
project (str): The unique project identifier used as a prefix in S3.
|
|
22
|
+
region (str): The AWS region for S3 operations.
|
|
23
|
+
profile (Optional[str]): The AWS profile name to use for credentials.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
bucket: str
|
|
27
|
+
project: str
|
|
28
|
+
region: Optional[str] = "ap-northeast-1"
|
|
29
|
+
profile: Optional[str] = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_config() -> Optional[Config]:
|
|
33
|
+
"""
|
|
34
|
+
Loads the project configuration from the local .dxlabs.json file.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Optional[Config]: The configuration object if found and valid, else None.
|
|
38
|
+
"""
|
|
39
|
+
config_path = Path(CONFIG_FILE)
|
|
40
|
+
if not config_path.exists():
|
|
41
|
+
return None
|
|
42
|
+
try:
|
|
43
|
+
with open(config_path, "r") as f:
|
|
44
|
+
data = json.load(f)
|
|
45
|
+
return Config(**data)
|
|
46
|
+
except Exception:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def save_config(
|
|
51
|
+
bucket: str,
|
|
52
|
+
project: str,
|
|
53
|
+
region: str = "ap-northeast-1",
|
|
54
|
+
profile: Optional[str] = None,
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Saves the project configuration to a local .dxlabs.json file.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
bucket (str): The S3 bucket name to use.
|
|
61
|
+
project (str): The project name/prefix for storage.
|
|
62
|
+
region (str): The AWS region (defaults to ap-northeast-1).
|
|
63
|
+
profile (Optional[str]): The AWS profile name to use.
|
|
64
|
+
"""
|
|
65
|
+
config = Config(bucket=bucket, project=project, region=region, profile=profile)
|
|
66
|
+
with open(CONFIG_FILE, "w") as f:
|
|
67
|
+
json.dump(config.model_dump(), f, indent=4)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core environment management logic for dxlabs.
|
|
3
|
+
Handles the high-level business logic for pushing and pulling .env files.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ..config import Config
|
|
7
|
+
from ..providers.s3 import S3Provider
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EnvManager:
|
|
11
|
+
"""
|
|
12
|
+
Manager for project environment (.env) files.
|
|
13
|
+
Coordinates between the configuration and the storage provider.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
config (Config): The project configuration.
|
|
17
|
+
provider (S3Provider): The storage provider (currently S3).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config: Config):
|
|
21
|
+
"""
|
|
22
|
+
Initializes the EnvManager with a project configuration.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
config (Config): The project settings (bucket, project ID, region, profile).
|
|
26
|
+
"""
|
|
27
|
+
self.config = config
|
|
28
|
+
self.provider = S3Provider(
|
|
29
|
+
bucket=config.bucket, region=config.region, profile=config.profile
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def get_remote_key(self, env_name: str) -> str:
|
|
33
|
+
"""
|
|
34
|
+
Constructs the standard S3 key for an environment file.
|
|
35
|
+
|
|
36
|
+
Format: {project_id}/{environment_name}/.env
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
env_name (str): The name of the environment (e.g., 'staging', 'dev').
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str: The fully qualified remote path/key.
|
|
43
|
+
"""
|
|
44
|
+
return f"{self.config.project}/{env_name}/.env"
|
|
45
|
+
|
|
46
|
+
def push(self, local_path: str, env_name: str) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Pushes a local file to the remote storage for a given environment.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
local_path (str): Path to the local file to upload.
|
|
52
|
+
env_name (str): The environment name to target in the cloud.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
bool: True if the operation succeeded, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
remote_key = self.get_remote_key(env_name)
|
|
58
|
+
return self.provider.upload(local_path, remote_key)
|
|
59
|
+
|
|
60
|
+
def pull(self, env_name: str, local_path: str) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Pulls a file from remote storage to a local path for a given environment.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
env_name (str): The environment name to pull from.
|
|
66
|
+
local_path (str): The local destination where the file should be saved.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
bool: True if the operation succeeded, False otherwise.
|
|
70
|
+
"""
|
|
71
|
+
remote_key = self.get_remote_key(env_name)
|
|
72
|
+
return self.provider.download(remote_key, local_path)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AWS S3 storage provider implementation.
|
|
3
|
+
Handles low-level operations with AWS S3 using boto3.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import boto3
|
|
9
|
+
from botocore.exceptions import ClientError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class S3Provider:
|
|
13
|
+
"""
|
|
14
|
+
Provider class for interacting with AWS S3.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
bucket (str): The name of the S3 bucket.
|
|
18
|
+
s3 (boto3.client): The initialized boto3 S3 client.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, bucket: str, region: Optional[str] = "ap-northeast-1", profile: Optional[str] = None):
|
|
22
|
+
"""
|
|
23
|
+
Initializes the S3 client.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
bucket (str): The name of the S3 bucket to use.
|
|
27
|
+
region (str, optional): The AWS region. Defaults to "ap-northeast-1".
|
|
28
|
+
profile (str, optional): The AWS profile name to use.
|
|
29
|
+
"""
|
|
30
|
+
self.bucket = bucket
|
|
31
|
+
# Use session to support profile-based credentials
|
|
32
|
+
session = boto3.Session(profile_name=profile)
|
|
33
|
+
self.s3 = session.client("s3", region_name=region)
|
|
34
|
+
|
|
35
|
+
def upload(self, local_path: str, remote_key: str) -> bool:
|
|
36
|
+
"""
|
|
37
|
+
Uploads a local file to a specific key in S3.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
local_path (str): The path to the file on the local filesystem.
|
|
41
|
+
remote_key (str): The key (path) where the file will be stored in S3.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
bool: True if the upload was successful, False otherwise.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
self.s3.upload_file(local_path, self.bucket, remote_key)
|
|
48
|
+
return True
|
|
49
|
+
except ClientError:
|
|
50
|
+
# Error details can be logged here if needed
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def download(self, remote_key: str, local_path: str) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Downloads a file from S3 to the local filesystem.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
remote_key (str): The key of the file to download from S3.
|
|
59
|
+
local_path (str): The local path where the file should be saved.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
bool: True if the download was successful, False otherwise.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
self.s3.download_file(self.bucket, remote_key, local_path)
|
|
66
|
+
return True
|
|
67
|
+
except ClientError:
|
|
68
|
+
return False
|