diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34e1140..06fc46c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: build-release: uses: hatlabs/shared-workflows/.github/workflows/build-release.yml@main with: - package-name: halpi2-daemon + package-name: halpid package-description: 'HALPI2 hardware daemon (Python implementation)' apt-component: hatlabs runs-on: ubuntu-latest-arm64 diff --git a/docker/Dockerfile.debtools b/docker/Dockerfile.debtools deleted file mode 100644 index bc77db3..0000000 --- a/docker/Dockerfile.debtools +++ /dev/null @@ -1,32 +0,0 @@ -FROM debian:bookworm - -# Install all packaging tools -RUN apt-get update && apt-get install -y \ - debhelper \ - dh-make \ - devscripts \ - build-essential \ - python3-dev \ - python3-venv \ - git \ - vim \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Install uv for root user (needed during build) -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -ENV PATH="/root/.local/bin:$PATH" - -# Set up working directory -WORKDIR /workspace - -# Optional: Add a non-root user -RUN useradd -m -s /bin/bash builder && \ - chown builder:builder /workspace -USER builder - -# Install uv for builder user -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -ENV PATH="/home/builder/.local/bin:$PATH" - -CMD ["/bin/bash"] diff --git a/docker/Dockerfile.devtools b/docker/Dockerfile.devtools new file mode 100644 index 0000000..82e6fb7 --- /dev/null +++ b/docker/Dockerfile.devtools @@ -0,0 +1,53 @@ +# Development environment for HALPI2-daemon +# +# Provides a Debian Bookworm environment with all development and build tools. +# Used for testing, linting, and Debian package building. + +FROM debian:bookworm + +# Install system dependencies including Debian packaging tools +RUN apt-get update && apt-get install -y \ + python3 \ + python3-pip \ + python3-venv \ + python3-dev \ + debhelper \ + dh-python \ + pybuild-plugin-pyproject \ + build-essential \ + fakeroot \ + devscripts \ + dpkg-dev \ + python3-all \ + python3-setuptools \ + curl \ + git \ + sudo \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user for development +ARG USERNAME=builder +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ + && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +# Install uv system-wide +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && mv /root/.local/bin/uv /usr/local/bin/uv \ + && mv /root/.local/bin/uvx /usr/local/bin/uvx + +# Set PATH for all users +ENV PATH="/usr/local/bin:${PATH}" + +# Switch to non-root user +USER $USERNAME + +# Set working directory +WORKDIR /workspace + +# Default command +CMD ["/bin/bash"] diff --git a/docker/docker-compose.debtools.yml b/docker/docker-compose.debtools.yml deleted file mode 100644 index 2465a23..0000000 --- a/docker/docker-compose.debtools.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - debtools: - build: - context: . - dockerfile: Dockerfile.debtools - env_file: ../.env - environment: - - DIR_NAME=${DIR_NAME} - volumes: - - ../../:/workspace - - debian-build-cache:/var/cache/apt - working_dir: /workspace/${DIR_NAME} - stdin_open: true - tty: true - -volumes: - debian-build-cache: diff --git a/docker/docker-compose.devtools.yml b/docker/docker-compose.devtools.yml new file mode 100644 index 0000000..7f43e6b --- /dev/null +++ b/docker/docker-compose.devtools.yml @@ -0,0 +1,23 @@ +services: + devtools: + build: + context: . + dockerfile: Dockerfile.devtools + image: halpi2-daemon-devtools:latest + + # Mount the project directory + volumes: + - ..:/workspace + + # Working directory + working_dir: /workspace + + # Keep container running for interactive use + stdin_open: true + tty: true + + # Run as non-root user + user: builder + + # Override command for interactive use + command: /bin/bash diff --git a/run b/run index c249acc..4d68291 100755 --- a/run +++ b/run @@ -8,6 +8,26 @@ # See https://death.andgravity.com/run-sh # for an explanation of how it works and why it's useful. +# First, set up the environment. +# (Check the notes at the end when changing this.) + +set -o nounset +set -o pipefail +set -o errexit + +# Enable this to echo commands as they are executed. +#set -o xtrace + +# Change the current directory to the project root. +PROJECT_ROOT=${0%/*} +if [[ $0 != $PROJECT_ROOT && $PROJECT_ROOT != "" ]]; then + cd "$PROJECT_ROOT" +fi +readonly PROJECT_ROOT=$(pwd) + +# Store the absolute path to this script (useful for recursion). +readonly SCRIPT="$PROJECT_ROOT/$(basename "$0")" + ################################################################################ # Core Development Commands @@ -85,59 +105,56 @@ function bumpversion { # Package Management Commands function build-deb { - #@ Build Debian package (native) + #@ Build Debian package (native, run inside devtools container) #@ Category: Package Management echo "๐Ÿ—๏ธ Building Debian package..." - - # Set up environment - dev-env - - # Check if changelog was already generated by CI (shared-workflows) - # If debian/changelog exists and was modified in the last 5 minutes, skip regeneration - if [ -f "debian/changelog" ]; then - # Use stat -c for Linux, stat -f for macOS, handle errors gracefully - CHANGELOG_MTIME=$(stat -c %Y debian/changelog 2>/dev/null) || CHANGELOG_MTIME=$(stat -f %m debian/changelog 2>/dev/null) || CHANGELOG_MTIME=0 - CURRENT_TIME=$(date +%s) - CHANGELOG_AGE=$((CURRENT_TIME - CHANGELOG_MTIME)) - if [ "$CHANGELOG_AGE" -lt 300 ]; then - echo "๐Ÿ“‹ Using existing debian/changelog (generated by CI)" - else - # Generate changelog for local builds - export DEBEMAIL="info@hatlabs.fi" - export DEBFULLNAME="Hat Labs CI" - export PACKAGE_VERSION=$(cat VERSION) - DEB_VERSION=$(echo "$PACKAGE_VERSION" | sed 's/-\([a-zA-Z]\)/~\1/g') - - dch --newversion "$DEB_VERSION" \ - --distribution stable \ - --force-distribution \ - "Automated release $PACKAGE_VERSION. See GitHub for details." - fi - fi - + uv sync dpkg-buildpackage -us -uc -b - echo "โœ… Debian package built successfully" } -function docker-build-deb { - #@ Build Debian package using Docker container +function build { + #@ Build Debian package in Docker container #@ Category: Package Management echo "๐Ÿณ Building Debian package using Docker..." + devtools bash -c "uv sync && dpkg-buildpackage -us -uc -b" + echo "โœ… Build complete" +} + +function build-docker { + #@ Build development Docker image + #@ Category: Package Management + echo "๐Ÿณ Building development Docker image..." + docker compose -f docker/docker-compose.devtools.yml build devtools + echo "โœ… Docker image built successfully" +} - # Use Docker container for Debian packaging - echo "๐Ÿณ Running Debian packaging in Docker container..." - export DIR_NAME=$(basename "$PWD") - debtools ./run build-deb +################################################################################ +# Docker Commands - echo "โœ… Docker-based Debian package built successfully" +function devtools { + #@ Run command in devtools container + #@ Usage: devtools [args] + #@ Category: Docker + docker compose -f docker/docker-compose.devtools.yml run --rm devtools "$@" } -function build-docker-tools { - #@ Build Docker tools image - #@ Category: Package Management - export DIR_NAME=$(basename "$PWD") - docker compose -f docker/docker-compose.debtools.yml build debtools "$@" +function docker-shell { + #@ Open interactive shell in development container + #@ Category: Docker + echo "๐Ÿš Opening shell in development container..." + echo " Working directory: /workspace" + echo " Exit with: exit or Ctrl+D" + echo "" + devtools bash +} + +function docker-clean { + #@ Remove development Docker containers and images + #@ Category: Docker + echo "๐Ÿงน Cleaning Docker containers and images..." + docker compose -f docker/docker-compose.devtools.yml down --rmi all --volumes + echo "โœ… Docker cleanup complete" } ################################################################################ @@ -158,7 +175,7 @@ function ci-build { echo "๐Ÿš€ Running full CI build pipeline..." clean ci-check - docker-build-deb + build echo "โœ… CI build pipeline completed successfully" } @@ -195,20 +212,6 @@ function pull { --exclude='.venv' halpi2:src/halpid/ . } -function dev-env { - #@ Show/check development environment - #@ Category: Development Utilities - _env -} - -function debtools { - #@ Run a command inside the debtools Docker container - #@ Usage: debtools [args] - #@ Category: Development Utilities - export DIR_NAME=$(basename "$PWD") - docker compose -f docker/docker-compose.debtools.yml run --rm debtools "$@" -} - function bumpversion-dry-run { #@ Preview version change without applying #@ Usage: bumpversion-dry-run @@ -227,24 +230,6 @@ function bumpversion-dry-run { bump2version --new-version "$new_version" patch --dry-run --verbose } -################################################################################ -# Docker Commands - -function build-docker { - #@ Build the Docker image - #@ Category: Docker - echo Building docker ${DOCKER_IMAGE}:${DOCKER_IMAGE_VERSION} ... - docker build -t ${DOCKER_IMAGE}:${DOCKER_IMAGE_VERSION} . \ - -f ./docker/Dockerfile --no-cache -} - -function remove-docker { - #@ Remove the Docker image - #@ Category: Docker - echo Removing docker ${DOCKER_IMAGE}:${DOCKER_IMAGE_VERSION} ... - docker rmi -f ${DOCKER_IMAGE}:${DOCKER_IMAGE_VERSION} -} - ################################################################################ # Setup/Installation Commands @@ -281,34 +266,6 @@ function build-remove { rm -rf build/ 2>/dev/null || true } - -################################################################################ -# Project-specific commands end. - -# First, set up the environment. -# (Check the notes at the end when changing this.) - -set -o nounset -set -o pipefail -set -o errexit - -# Enable this to echo commands as they are executed. -#set -o xtrace - -# Change the current directory to the project root. -PROJECT_ROOT=${0%/*} -if [[ $0 != $PROJECT_ROOT && $PROJECT_ROOT != "" ]]; then - cd "$PROJECT_ROOT" -fi -readonly PROJECT_ROOT=$(pwd) - -# Some useful variables. -DOCKER_IMAGE="halpid" -DOCKER_IMAGE_VERSION="latest" - -# Store the absolute path to this script (useful for recursion). -readonly SCRIPT="$PROJECT_ROOT/$(basename "$0")" - ################################################################################ # Meta-commands and utilities follow. @@ -344,7 +301,7 @@ function help { done # Display in order of preference - local ordered_categories=("Core Development" "Testing/CI" "Package Management" "Development Utilities" "Docker" "Setup/Installation" "Meta") + local ordered_categories=("Core Development" "Testing/CI" "Package Management" "Docker" "Development Utilities" "Setup/Installation" "Meta") for category in "${ordered_categories[@]}"; do if [[ -n "${category_funcs[$category]:-}" ]]; then @@ -368,67 +325,8 @@ function help { done } -once_hash_array=() -function _once { - # Run a command only once during the execution of this script, even if it's - # called multiple times. - # - # Usage: - # _once [argument ...] - # - # Example: - # _once echo "Hello" - # _once echo "Hello" # won't be executed - - local command="$*" - local hash=$(echo "$command" | shasum | cut -d' ' -f1) - if [[ ! " ${once_hash_array[@]} " =~ " ${hash} " ]]; then - once_hash_array+=("$hash") - eval "$command" - fi -} - -compose_flags="" -function _dc { - docker compose $compose_flags "$@" -} - -function _env { - echo "๐Ÿ—๏ธ Setting environment from .env and .env.defaults" - # Go through the files and export all variables not already present in - # the environment. First file has precedence! - if [ -f .env ]; then - _export_unset .env - elif [ -f .env.defaults ]; then - # Make sure a .env file exists, otherwise docker-compose will complain - cp .env.defaults .env - _export_unset .env - fi - # If neither file exists, continue without env setup (CI environment) - if [ -f .env.defaults ]; then - _export_unset .env.defaults - fi -} - -function _export_unset { - local file="$1" - - # Need to use a temp file to avoid a subshell - local tmpfile=$(mktemp) - # Use || true to handle files with only comments (grep returns 1 when no matches) - grep -v '^#' "$file" >"$tmpfile" || true - - while read -r line; do - if [[ ! "$line" =~ ^[[:space:]]*$ ]]; then - varname=$(echo "$line" | cut -d= -f1) - if [[ -z "${!varname:-}" ]]; then - eval $line - export $varname - fi - fi - done <$tmpfile - rm $tmpfile -} +################################################################################ +# Commands end. # Dispatch to command. A simpler version would be just "$@" (with the quotes!).