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.
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .venv/
8
+ env/
9
+ venv/
10
+ .dxlabs.json
11
+ .pytest_cache/
12
+ .env
13
+ **/__init__.py
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