diff --git a/deployment.example.lua b/deployment.example.lua new file mode 100644 index 0000000..20bc7c1 --- /dev/null +++ b/deployment.example.lua @@ -0,0 +1,39 @@ +local app_name = "deployment-pipeline" +local git_user = "example" + +local repo = "ssh://git@github.com/" .. git_user .. "/" .. app_name .. ".git" +local config_dir = "/etc/" .. app_name +-- 1. Static Lookups +local base = "/var/lib/" .. app_name +local deployments = base .. "/deployments" + +-- Pipeline testing +local config_file = "/etc/" .. app_name .. "/config.toml" + +function get_config(env_key) + -- Specific folder name for this environment + local instance_name = app_name .. "-" .. env_key + local deploy_link = deployments .. "/" .. instance_name + + return { + deploy_link = deploy_link, + -- Pipeline testing + config_file = config_file, + -- config_file = config_dir .. env_key .. ".toml", + service_name = app_name .. "@" .. env_key .. ".service", + -- Tracking which deployments were successful + get_release_dir = function(timestamp) + return deploy_link .. "-" .. timestamp + end, + count = (env_key == "release") and 5 or 1, + } +end + +return { + app_name = app_name, + timestamp_format = "%Y%m%d-%H%M%S", + repo = repo, + base = base, + release = get_config("release"), + testing = get_config("testing"), +} diff --git a/deployment.lua b/deployment.lua deleted file mode 100644 index c7330e9..0000000 --- a/deployment.lua +++ /dev/null @@ -1,32 +0,0 @@ -local app_name = "Express Blog" -local repo = "ssh://git@git.jasonpoage.vpn:29418/jason/express-blog.git" -local config_dir = "/srv/jasonpoage.com/env/" --- 1. Static Lookups -local base = "/srv/jasonpoage.com" -local deployments = base .. "/deployments" - -function get_config(env_key) - -- Specific folder name for this environment - local instance_name = "blog-" .. env_key - local deploy_link = deployments .. "/" .. instance_name - - return { - deploy_link = deploy_link, - config_file = config_dir .. env_key .. ".toml", - service_name = "expressjs-blog@" .. env_key .. ".service", - -- Tracking which deployments were successful - get_release_dir = function(timestamp) - return deploy_link .. "-" .. timestamp - end, - count = (env_key == "release") and 5 or 1, - } -end - -return { - app_name = app_name, - timestamp_format = "%Y%m%d-%H%M%S", - repo = repo, - base = base, - release = get_config("release"), - testing = get_config("testing"), -} diff --git a/deployment/README b/deployment/README deleted file mode 100644 index 32a7bf0..0000000 --- a/deployment/README +++ /dev/null @@ -1,2 +0,0 @@ -#Entry point: -python . --config ../deployment.lua --dry-run --branch refs/heads/dev diff --git a/deployment/__main__.py b/deployment/__main__.py index 421d58f..0d3ea05 100644 --- a/deployment/__main__.py +++ b/deployment/__main__.py @@ -1,4 +1,4 @@ if __name__ == "__main__": - from main import main + from deployment_pipeline.main import main main() diff --git a/deployment/core/bootstrap.py b/deployment/core/bootstrap.py index 19abe76..ef3ca0b 100644 --- a/deployment/core/bootstrap.py +++ b/deployment/core/bootstrap.py @@ -1,45 +1,7 @@ -import os import shutil -from core.tasks import GetDeploymentConfig -from lib.printer import clear_screen -from lib.types import Stage -from lib.task_types import SuiteTask, SuiteSubTask - - -class CheckNix(SuiteTask): - _stage = Stage.BOOTSTRAP - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.name = "Checking if we are in a nix shell..." - - def _run(self): - if not shutil.which("nix"): - self.print("⬡ Nix tools not found in PATH.") - return - - # 2. Check if already in a shell - shell_type = os.environ.get("IN_NIX_SHELL") - if shell_type: - self._in_nix_shell = shell_type - return True - - -class EnsureBuildPaths(SuiteTask): - _deps = [GetDeploymentConfig] - _can_skip = False - """Task 1: Ensure build paths exist""" - - _stage = Stage.BOOTSTRAP - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.name = "Setting up bulid path" - - def _run(self): - """Ensure build directory exists""" - self.env.build_dir.mkdir(parents=True, exist_ok=True) +from pipeline_runner.lib.types import Stage +from pipeline_runner.lib.task_types import SuiteTask class VerifySystemDependencies(SuiteTask): diff --git a/deployment/core/suite.py b/deployment/core/suite.py index 0809d25..046afa0 100644 --- a/deployment/core/suite.py +++ b/deployment/core/suite.py @@ -1,13 +1,15 @@ import argparse from pathlib import Path -from lib.errors import SuiteError -from lib.task_types import SuiteTask, Task -from core.task_runner import TaskRunner -from lib.types import BuildEnv -from core.bootstrap import * -from core.task_runner import * -from core.suite import * +from pipeline_runner.lib.exceptions import SuiteError +from pipeline_runner.lib.task_types import SuiteTask, Task +from pipeline_runner.core.bootstrap import * + +from deployment_pipeline.core.task_runner import * + +from deployment_pipeline.lib.types import BuildEnv + +# from core.suite import * def load_parser(): @@ -55,7 +57,7 @@ Replaces tdd_loop.sh with zero subprocess overhead for Python logic. """ - name = "Hexa Core Test Script Runner" + name = "Deployment Test Runner" root_dir: Path | None _in_nix_shell: bool _owner: "DeploymentSuite" @@ -123,8 +125,3 @@ task = self.get_arg("task") Task.run(task) return self - runner = TaskRunner( - self, all_tasks, base_class_name="SuiteTask", owner=self._owner - ) - runner.queue_tasks(all_tasks) - runner.run() diff --git a/deployment/core/task_runner.py b/deployment/core/task_runner.py index d7b86c7..ed0bda4 100755 --- a/deployment/core/task_runner.py +++ b/deployment/core/task_runner.py @@ -1,24 +1,16 @@ from os import wait from typing import List -from lib.types import Stage, typename -from lib.errors import TaskError -from lib.task_types import SuiteTask +from pipeline_runner.lib.types import Stage, typename +from pipeline_runner.lib.exceptions import TaskError +from pipeline_runner.lib.task_types import SuiteTask -from core.bootstrap import * -from core.task_runner import * -from core.suite import * +from pipeline_runner.core.bootstrap import * +from deployment_pipeline.core.task_runner import * +from pipeline_runner.core.suite import * -from core.tasks import ( - GetDeploymentConfig, - LoadServerConfig, - HotFix, - YarnBuild, - AtomicDeploy, - HealthCheck, - PipelineSuccess, -) +from deployment_pipeline.core.tasks import PipelineSuccess class TaskRunner(SuiteTask): diff --git a/deployment/core/tasks.py b/deployment/core/tasks.py index aa5a9cb..b31dbff 100644 --- a/deployment/core/tasks.py +++ b/deployment/core/tasks.py @@ -3,11 +3,11 @@ import tomllib from lupa import LuaRuntime from pathlib import Path -from lib.task_types import SuiteTask -from lib.types import Stage +from deployment_pipeline.lib.task_types import DeploymentTask +from pipeline_runner.lib.types import Stage -class GetDeploymentConfig(SuiteTask): +class GetDeploymentConfig(DeploymentTask): _stage = Stage.BOOTSTRAP _deps = [] @@ -30,6 +30,7 @@ self.fail("Failed to load deployment config: ", e) # 4. Hydrate self.env + self.cfg = cfg # Store the lua object for functional calls later self.env.lua_cfg = cfg # Store the lua object for functional calls later self.env.app_name = cfg.app_name self.env.repo = cfg.repo @@ -47,7 +48,7 @@ return True -class LoadServerConfig(SuiteTask): +class LoadServerConfig(DeploymentTask): """Verifies TOML existence and hydrates the environment with health check URI components""" _stage = Stage.BOOTSTRAP @@ -120,7 +121,7 @@ pass -class HotFix(SuiteTask): +class HotFix(DeploymentTask): """Bypasses the full build to update the current live deployment""" _stage = Stage.DEPLOY @@ -159,7 +160,7 @@ raise PipelineSuccess("Hot fix applied successfully") -class YarnBuild(SuiteTask): +class YarnBuild(DeploymentTask): """Executes dependency installation and asset compilation""" _stage = Stage.BUILD @@ -206,7 +207,7 @@ return True -class AtomicDeploy(SuiteTask): +class AtomicDeploy(DeploymentTask): """Performs rsync to release directory and updates environment symlink""" _stage = Stage.DEPLOY @@ -257,7 +258,7 @@ return True -class HealthCheck(SuiteTask): +class HealthCheck(DeploymentTask): """Polls the local production service endpoint""" _stage = Stage.DEPLOY diff --git a/deployment/core/tests.py b/deployment/core/tests.py index ce5eb60..7297e6a 100644 --- a/deployment/core/tests.py +++ b/deployment/core/tests.py @@ -1,10 +1,10 @@ import time import shlex -from lib.task_types import SuiteTask, SuiteSubTask -from lib.types import Stage -from core.tasks import YarnBuild -from core.task_runner import TaskRunner +from pipeline_runner.lib.task_types import SuiteTask, SuiteSubTask +from pipeline_runner.lib.types import Stage +from pipeline_runner.core.tasks import YarnBuild +from pipeline_runner.core.task_runner import TaskRunner class StartTestApp(SuiteSubTask): diff --git a/deployment/lib/errors.py b/deployment/lib/errors.py deleted file mode 100644 index 72a1bbe..0000000 --- a/deployment/lib/errors.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys -import traceback - - -class SuiteError(Exception): - def __init__( - self, parent, *args, critical: bool = False, code: int | None = None, **kwargs - ): - super().__init__(*args, **kwargs) - try: - if type(parent).__name__ == "str": - print(parent) - raise Exception("Um... this is embarrassing. Parent? ", parent) - parent.dump_print_queue() - traceback.print_stack() - print(*args, **kwargs) - - if code is not None: - sys.exit(code) - if critical: - raise RuntimeError(*args, **kwargs) - except Exception as e: - raise Exception( - f"There was an error while handling an exception: {e}" - ) from e - sys.exit(1) - - -class TaskError(SuiteError): - def __init__( - self, parent, *args, critical: bool = False, code: int | None = None, **kwargs - ): - super().__init__(parent, *args, critical=critical, code=code, **kwargs) - # I dont think this code is reachable - if critical: - raise RuntimeError(*args, **kwargs) - else: - print(*args, **kwargs) - traceback.print_stack() diff --git a/deployment/lib/printer.py b/deployment/lib/printer.py deleted file mode 100755 index 49294e4..0000000 --- a/deployment/lib/printer.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -import json -from pathlib import Path - - -def clear_screen(): - """Clear the screen on both NT and *nix systems.""" - os.system("cls" if os.name == "nt" else "clear") - - -class Printer: - _queue: list = [] - _cache: list = [] - _use_queue = False - _parent = None - _instance = None - - def __init__( - self, - parent, - instance, - *args, - **kwargs, - ): - self._parent = parent - self._instance = instance - self._parent_id = type(parent) - self._instance_id = instance.get_id() - - def dump(self): - for args, kwargs in Printer._queue: - print(*args, **kwargs) - Printer._queue = [] - - def print(self, *args, **kwargs): - payload = [args, kwargs] - Printer._cache.append(payload) - if Printer._use_queue: - Printer._queue.append(payload) - - else: - print(*args, flush=True, **kwargs) - - def flush(self): - for args, kwargs in Printer._queue: - print(*args, **kwargs) - Printer._queue = [] - - def save_stdout(self, _file_path: Path | str): - file_path = Path(_file_path).resolve() - with open(file_path, "w") as f: - try: - for line in Printer._cache: - try: - f.write(line) - except: - f.write(json.dumps(line)) - except Exception as e: - print(e.with_traceback) - raise e - - def _msg_prefix(self): - # Format: [ID] for main tasks, [ID.Sub] for subtasks - from lib.task_types import SuiteSubTask - - if isinstance(self._instance, SuiteSubTask): - return f"\n[{self._instance.parent_id}.{self._instance.sub_id}] " - return f"\n[{self._instance._id}] " - - def msg(self, *args, **kwargs): - """Standardized message logger.""" - - self.print(self._msg_prefix(), *args, **kwargs) - - def enable_queue(self): - Printer._use_queue = True - - def disable_queue(self): - Printer._use_queue = False - self.dump() diff --git a/deployment/lib/task_types.py b/deployment/lib/task_types.py index cc9886a..5e4ff56 100755 --- a/deployment/lib/task_types.py +++ b/deployment/lib/task_types.py @@ -1,199 +1,22 @@ import time -import threading -import os -import sys -import subprocess -from pathlib import Path -from abc import ABC, abstractmethod -from typing import List, TYPE_CHECKING, Optional -from shlex import split as shlex_split +from typing import TYPE_CHECKING -from lib.printer import Printer -from lib.errors import TaskError -from lib.types import typename +from pipeline_runner.lib.types import typename +from pipeline_runner.lib.task_types import SuiteTask, Task as BaseTask, SuiteSubTask if TYPE_CHECKING: - from types import Stage, BuildEnv - from task_types import BlogDeploySuite - from task_runner import TaskRunner + from deployment_pipeline.core.suite import DeploymentSuite + from deployment_pipeline.lib.task_types import BlogDeploySuite -class Task: - _initialized: bool = False - _registry: dict = {} - _loaded: dict = {} - _completed: dict = {} - _owner = None - - # 1. The owner initializes the class - @staticmethod - def __init__(owner, task_list): - if Task._initialized: - return - Task._owner = owner - for task in task_list: - task_name = Task.get_key(task) - print(f"Registring {task_name}") - Task._registry[task_name] = task - return - - # 2. Add a dependency from the registry - @staticmethod - def add(key): - """Adds a depndency only if it exists in the registry""" - key = Task.get_key(key) - - # 1. Determine if the key exists in the registry - if not Task.exists(key): - raise ValueError(f"Dependency {key} does not exist in the registry") - - # 2. Determine if the key has been initialized - if Task.initialized(key): - print(f"Fetching task: {key}") - # Return the key if already initialized - return Task._loaded[key] - - # 3. Initialize the key - print(f"Initializing {key}") - dep = Task._registry[key] - task = dep(Task._owner, owner=Task._owner) - Task._loaded[key] = task - return task - - @staticmethod - def exists(key): - key = Task.get_key(key) - return key in Task._registry.keys() - - @staticmethod - def initialized(key): - """Returns the initialized object""" - key = Task.get_key(key) - return key in Task._completed.keys() - - # 3. Run the task - @staticmethod - def run(key): - """Runs a task from the registry""" - key = Task.get_key(key) - - # 1. Determine if the task has already ran - if Task.completed(key): - print(f"fetching result for {key}") - return Task._completed[key] - - # 2. Initialize the dependency - # This is harmless if already initialized - # due to internal checks - task = Task.add(key) # Also provides the object - - # 3. Run the task and store its result - print(f"Running task: {key}") - result = task.run() - Task._completed[key] = result - - return result - - @staticmethod - def get_key(key): - """Convert a raw class to a key""" - if type(key) is not str: - key = key.__name__ - return key - - @staticmethod - def completed(key): - """Returns a bool if the task has been completed or not""" - key = Task.get_key(key) - return key in Task._completed.keys() - - @staticmethod - def get_owner(): - return Task._owner +class DeploymentTask(BaseTask): + pass -class SuiteTask(ABC): +class DeploymentSuiteTask(SuiteTask): _owner: "BlogDeploySuite" - _parent: "SuiteTask" - _global_counter: int = 0 - _id: int - _cwd: Path | None - message: str - name: str - printer: Printer - skip: bool = False - _can_skip: bool = True - _stage: "Stage" - _initialized = False - _deps = [] - env: "BuildEnv" - complete: bool = False - skip_list: List = [] - - def __init__( - self, - parent, - *args, - owner: "TDDSuite", - cwd: Path | str | None = None, - attach_printer: bool = True, - **kwargs, - ): - self.add_deps() - - from lib.task_types import SuiteTask - - if owner is None and not SuiteTask._initialized: - raise ValueError("Owner is not set") - if parent is None: - raise ValueError("Parent is not set") - # print(kwargs, self.__class__.__name__) - # if kwargs and self.__class__.__name__ in self.get_arg("skip_list"): - # self.skip = True - # return - SuiteTask._initialized = True - - if cwd is not None: - cwd = Path(cwd) - if cwd is None and parent is not None: - try: - cwd = parent.get_cwd() - except: - pass - if cwd is None: - cwd = os.getcwd() - self._cwd = cwd - - self._owner = owner - self._parent = parent - self.env = owner.env - self.args = self._owner.args - - from lib.task_types import SuiteTask - - if not isinstance(self, SuiteSubTask): - self._id = SuiteTask._global_counter - SuiteTask._global_counter += 1 - if attach_printer: - self.attach_printer(parent) - - def initialize_deps(): - for dep in self._deps: - dep_name = dep.__name__ - if not Task.initalized(dep_name): - Task.load(dep) - - def add_deps(self): - for dep in self._deps: - Task.add(dep) - - def run_deps(self): - for dep in self._deps: - Task.run(dep) - - def get_arg(self, arg): - return self._owner.args.get(arg) + _parent: "DeploymentTask" def skip_task(self): if self._deps and not self.deps_loaded(): @@ -204,62 +27,12 @@ return False - def get_path(self, component: str, path: Path | str | None = None) -> Path: - if path is not None: - return self._owner.paths.get(component) / Path(path) - return self._owner.paths.get(component) - - def do_dry_run(self): - do_dry_run = self.args.get("dry_run", False) or self.skip_task() - return do_dry_run - - def attach_printer(self, parent): - self.printer = Printer(parent, self) - - @staticmethod - def inc_count(): - SuiteSubTask._global_counter += 1 - - @staticmethod - def get_count(): - return SuiteTask._global_counter - - def dump_print_queue(self): - """Standardized message logger.""" - self.printer.dump() - - def print(self, *args, **kwargs): - """Standardized message logger.""" - self.printer.print(*args, **kwargs) - - def msg(self, *args, **kwargs): - """Standardized message logger.""" - self.printer.msg(*args, **kwargs) - - @abstractmethod - def _run(self): - pass - - def dry_run(self): - self.msg(self.name) - if self.skip_task(): - self.print("Skipping") - return True - return self.do_dry_run() - - def disable_dry_run(self): - def func(): - print("Dry run disabled") - return False - - print("Disabling dry run") - self.do_dry_run = func - def run(self): self.print("Running ", typename(self)) try: if len(self._deps) > 0: - from core.task_runner import TaskRunner + + from deployment_pipeline.core.task_runner import TaskRunner TaskRunner.add_deps(self) TaskRunner.run_deps(self) @@ -267,112 +40,11 @@ raise Exception(f"Failed to load dependency for {typename(self)}: {e}") return self._run() - def fail(self, *args, critical: bool = False, **kwargs): - """Helper to raise the state-aware exception.""" - - raise TaskError(self, critical=critical, *args, **kwargs) - - def sh( - self, - cmd: str, - cwd: Path | None = None, - handle_exception=True, - dry_run=None, - check=True, - shell=True, - shlex=False, - disabled=False, - ): - """Helper to run shell commands within the project context.""" - if shlex: - cmd = shlex_split(cmd) - shell = False - - if cwd is not None: - self.print(f" [CWD] {cwd}") - if disabled: - self.msg(f"[DISABLED] [EXEC] {cmd}") - return - - cwd = str(cwd or os.getcwd()) - self.msg(f" [EXEC] {cmd}") - if self.do_dry_run() and dry_run is not False: - return - - try: - return subprocess.run(cmd, shell=shell, check=check, cwd=cwd) - except subprocess.CalledProcessError as e: - if handle_exception: - self.fail(e) - raise Exception(e) - - def sh_thread(self, cmd: str, cwd: Path | None = None): - """ - Runs shell commands, streams output to CLI in real-time, - and captures it for later analysis. - """ - self.msg(f" [EXEC] {cmd}") - if self.do_dry_run: - return - - # Store captured output - self.last_stdout = [] - self.last_stderr = [] - - # Start the process with piped outputs - process = subprocess.Popen( - cmd, - shell=True, - cwd=str(cwd or self.get_path("root")), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, # Line buffered - ) - - def stream_pipe(pipe, relay, accumulator): - """Reads from pipe, writes to relay (stdout/err), and saves to list.""" - for line in iter(pipe.readline, ""): - if line: - accumulator.append(line) - relay.write(line) - relay.flush() - pipe.close() - - # Use threads to prevent the pipes from clogging (which causes deadlocks) - t1 = threading.Thread( - target=stream_pipe, args=(process.stdout, sys.stdout, self.last_stdout) - ) - t2 = threading.Thread( - target=stream_pipe, args=(process.stderr, sys.stderr, self.last_stderr) - ) - - t1.start() - t2.start() - - # Wait for completion - exit_code = process.wait() - t1.join() - t2.join() - - if exit_code != 0: - self.fail(f"\n[ERROR] Command failed with code {exit_code}", code=exit_code) - - return ["".join(self.last_stdout), "".join(self.last_stdout)] - - def get_cwd(self): - return self._cwd - - def get_id(self): - return self._id - - def get_stage(self): - return self._stage - def deps_loaded(self): if isinstance(self, SuiteSubTask): return True - from core.task_runner import TaskRunner + + from deployment_pipeline.core.task_runner import TaskRunner return TaskRunner.is_loaded(self._deps) @@ -406,43 +78,16 @@ return False -class SuiteSubTask(SuiteTask): - _owner: "TDDSuite" - _parent: SuiteTask - - _sub_counter: dict[int] = {} - - def __init__(self, *args, **kwargs): - super().__init__(*args, attach_printer=False, **kwargs) - - if SuiteTask._global_counter not in SuiteSubTask._sub_counter.keys(): - SuiteSubTask._sub_counter[SuiteTask._global_counter] = 0 - - self._id = (SuiteTask._global_counter, SuiteSubTask._sub_counter) - - self.attach_printer(self._owner) - - def msg(self, *args, **kwargs): - """Standardized message logger.""" - SuiteSubTask.inc_count() - - self._parent.msg(*args, **kwargs) - - @staticmethod - def inc_count(): - - print(SuiteSubTask._sub_counter) - SuiteSubTask._sub_counter[SuiteTask._global_counter] += 1 - - @staticmethod - def get_count(): - return SuiteSubTask._sub_counter +class DeploymentSubTask(SuiteSubTask): + _owner: "DeploymentSuite" + _parent: DeploymentSuiteTask def run(self): self.print("Running ", typename(self)) try: if len(self._deps) > 0: - from core.task_runner import TaskRunner + + from deployment_pipeline.core.task_runner import TaskRunner TaskRunner.load_deps( self, diff --git a/deployment/lib/types.py b/deployment/lib/types.py index 908a929..be5cfba 100644 --- a/deployment/lib/types.py +++ b/deployment/lib/types.py @@ -1,20 +1,7 @@ import os -from enum import Enum from pathlib import Path -def typename(t): - return type(t).__name__ - - -class Stage(Enum): - ANY = "any" - BOOTSTRAP = "bootstrap" - BUILD = "build" - TEST = "test" - DEPLOY = "deploy" - - class BuildEnv: timestamp_format: str = "%Y%m%d-%H%M%S" workspace: Path diff --git a/deployment/main.py b/deployment/main.py index 4ff19d4..37b1b78 100644 --- a/deployment/main.py +++ b/deployment/main.py @@ -1,7 +1,7 @@ import sys -from core.suite import DeploymentSuite +from deployment_pipeline.core.suite import DeploymentSuite def main(): diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..e4e8b26 --- /dev/null +++ b/package.nix @@ -0,0 +1,72 @@ +{ + lib, + python3Packages, + fetchgit, + nodejs_latest, + nodePackages, + chromium, + imagemagick, + makeWrapper, + which, +}: let + deployment_pipeline = python3Packages.buildPythonPackage { + pname = "deployment_pipeline"; + version = "0.1.0"; + pyproject = true; + src = fetchgit { + url = "ssh://git@git.jasonpoage.vpn:29418/jason/deployment_pipeline.git"; + rev = "main"; + hash = "sha256-2yapZOSOop/ng8MNjZcuJIr7Qu9rZfeHlH8h0ljN4aE="; + }; + # src = fetchFromGitHub { + # owner = "jpoage1"; + # repo = "deployment_pipeline"; + # rev = "main"; + # hash = "sha256-2yapZOSOop/ng8MNjZcuJIr7Qu9rZfeHlH8h0ljN4aE="; + # }; + nativeBuildInputs = with python3Packages; [ + setuptools + wheel + ]; + doCheck = false; + }; +in + python3Packages.buildPythonApplication { + pname = "deployment_pipeline"; + version = "0.1.0"; + pyproject = true; + + src = ./.; + + nativeBuildInputs = [makeWrapper]; + + # Combine Python and System dependencies + propagatedBuildInputs = + [ + deployment_pipeline + nodejs_latest + nodePackages.pnpm + chromium + imagemagick + which + ] + ++ (with python3Packages; [ + tomli + lupa + pip + setuptools + ]); + + # Inject environment variables into the final binary + postFixup = '' + wrapProgram $out/bin/deployment_pipeline \ + --set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true \ + --set PUPPETEER_EXECUTABLE_PATH ${chromium}/bin/chromium + ''; + + meta = with lib; { + description = "Deployment Pipeline with Node.js and Chromium integration"; + license = licenses.mit; + maintainers = ["Jason Poage"]; + }; + } diff --git a/shell.nix b/shell.nix index 13c53af..2e75e9c 100644 --- a/shell.nix +++ b/shell.nix @@ -1,25 +1,50 @@ # NodeJS PM2 # shell.nix -{pkgs ? import {}}: -pkgs.mkShell { - packages = with pkgs; - [ - which - nodejs_latest - nodePackages.pnpm - chromium - # nodePackages.pm2mk - imagemagick - ] - ++ (with python313Packages; [tomli lupa pip]); - shellHook = '' - export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true - export PUPPETEER_EXECUTABLE_PATH=${pkgs.chromium}/bin/chromium - alias mkicon="node src/render-favicon.js" - alias mkfavicons="node src/generate-favicon.js" - alias mkfavicon="magick static/favicons/favicon-512.png -define icon:auto-resize=64,48,32,16 static/favicons/favicon.ico" - echo "Type 'mkicon' to render the favicon-512.png" - echo "Type 'mkfavicon' to generate favicon.ico from favicon-512.png" +{pkgs ? import {}}: let + # deployment_pipeline = python3Packages.buildPythonPackage { + # pname = "deployment_pipeline"; + # version = "0.1.0"; + # pyproject = true; + # src = fetchgit { + # url = "ssh://git@git.jasonpoage.vpn:29418/jason/pipeline_runner.git"; + # rev = "main"; + # hash = "sha256-2yapZOSOop/ng8MNjZcuJIr7Qu9rZfeHlH8h0ljN4aE="; + # }; + # # src = fetchFromGitHub { + # # owner = "jpoage1"; + # # repo = "deployment_pipeline"; + # # rev = "main"; + # # hash = "sha256-2yapZOSOop/ng8MNjZcuJIr7Qu9rZfeHlH8h0ljN4aE="; + # # }; + # nativeBuildInputs = with python3Packages; [ + # setuptools + # wheel + # ]; + # doCheck = false; + # }; + deployment_pipeline = pkgs.callPackage ./package.nix {}; +in + pkgs.mkShell { + inputsFrom = [deployment_pipeline]; + # packages = with pkgs; + # [ + # which + # nodejs_latest + # nodePackages.pnpm + # chromium + # # nodePackages.pm2mk + # imagemagick + # deployment_pipeline + # ] + # ++ (with python313Packages; [tomli lupa pip]); + shellHook = '' + export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + export PUPPETEER_EXECUTABLE_PATH=${pkgs.chromium}/bin/chromium + alias mkicon="node src/render-favicon.js" + alias mkfavicons="node src/generate-favicon.js" + alias mkfavicon="magick static/favicons/favicon-512.png -define icon:auto-resize=64,48,32,16 static/favicons/favicon.ico" + echo "Type 'mkicon' to render the favicon-512.png" + echo "Type 'mkfavicon' to generate favicon.ico from favicon-512.png" - ''; -} + ''; + }