diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index eb7f3b3..5f900b0 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,6 +8,7 @@ SHELL ["/bin/bash", "-c"] # setup environment ENV LANG=C.UTF-8 ENV LC_ALL=C.UTF-8 +ENV PIP_BREAK_SYSTEM_PACKAGES=1 SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN DEBIAN_FRONTEND=noninteractive apt-get update -q && \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20d1112..def70d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,10 +6,6 @@ on: branches: - main -env: - REGISTRY: ghcr.io - IMAGE_NAME: bdaiinstitute/proto2ros_humble - defaults: run: shell: bash @@ -29,17 +25,12 @@ jobs: - name: Lint sources uses: pre-commit/action@v3.0.0 prepare_container: - name: Prepare Humble container for tests + name: Prepare proto2ros containers runs-on: ubuntu-22.04 needs: lint - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write - outputs: - image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} + strategy: + matrix: + rosdistro: [humble, jazzy] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -47,43 +38,65 @@ jobs: - name: Setup Docker buildx # to workaround: https://github.com/docker/build-push-action/issues/461 uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 # https://github.com/docker/login-action - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker uses: docker/metadata-action@v5 # https://github.com/docker/metadata-action with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ github.repository }}_on_${{ matrix.rosdistro }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=sha,format=long id: meta - - name: Build and push Docker image (may be cached) + - name: Build Docker image (may be cached) uses: docker/build-push-action@v5 # https://github.com/docker/build-push-action with: context: . file: .devcontainer/Dockerfile - push: true + build-args: | + ROS_DISTRO=${{ matrix.rosdistro }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + outputs: type=docker,dest=/tmp/${{ matrix.rosdistro }}_docker_image.tar + push: false + + - name: Upload Docker image as artifact + uses: actions/upload-artifact@v4 # https://github.com/actions/upload-artifact + with: + name: ${{ matrix.rosdistro }}_docker_image + path: /tmp/${{ matrix.rosdistro }}_docker_image.tar + retention-days: 7 build_and_test: name: Build and test proto2ros packages runs-on: ubuntu-22.04 needs: prepare_container - container: - image: ${{ needs.prepare_container.outputs.image }} + strategy: + matrix: + rosdistro: [humble, jazzy] steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Download Docker image artifact + uses: actions/download-artifact@v5 # https://github.com/actions/download-artifact + with: + name: ${{ matrix.rosdistro }}_docker_image + path: /tmp + + - name: Load Docker image + run: | + docker load -i /tmp/${{ matrix.rosdistro }}_docker_image.tar + - name: Build and run tests run: | - source /opt/ros/humble/setup.bash - colcon build --symlink-install - source install/setup.bash - colcon test --event-handlers console_direct+ - colcon test-result --all --verbose + docker run --rm -v ${{ github.workspace }}:/workspace -w /workspace \ + ${{ github.repository }}_on_${{ matrix.rosdistro }}:sha-${{ github.sha }} bash -c " \ + source /opt/ros/${{ matrix.rosdistro }}/setup.bash; \ + colcon build --symlink-install; \ + source install/setup.bash; \ + colcon test --event-handlers console_direct+; \ + colcon test-result --all --verbose" diff --git a/.github/workflows/maintenance.yaml b/.github/workflows/maintenance.yaml deleted file mode 100644 index e09447c..0000000 --- a/.github/workflows/maintenance.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: proto2ros CI maintenance - -on: - schedule: - - cron: "0 0 * * 0" # once a week - -env: - ORG_NAME: bdaiinstitute - IMAGE_NAME: proto2ros_humble - -jobs: - clean-ghcr: - name: Prune old images from Github Container Registry - runs-on: ubuntu-22.04 - steps: - - name: Delete old pull request images - uses: snok/container-retention-policy@v2 - with: - image-names: ${{ env.IMAGE_NAME }} - skip-tags: main - cut-off: One week ago UTC - org-name: ${{ env.ORG_NAME }} - account-type: org - token: ${{ secrets.GITHUB_TOKEN }} - token-type: github-token diff --git a/proto2ros/proto2ros/cli/generate.py b/proto2ros/proto2ros/cli/generate.py index bd351e8..c44d7e4 100644 --- a/proto2ros/proto2ros/cli/generate.py +++ b/proto2ros/proto2ros/cli/generate.py @@ -1,13 +1,14 @@ # Copyright (c) 2023 Robotics and AI Institute LLC dba RAI Institute. All rights reserved. import argparse import collections +import contextlib import importlib import importlib.resources import os import pathlib import sys import textwrap -from typing import List +from typing import Generator, List from google.protobuf.descriptor_pb2 import FileDescriptorProto @@ -143,8 +144,21 @@ def do_generate(args: argparse.Namespace) -> int: return 0 -def main() -> int: - """Entrypoint for proto2ros""" +@contextlib.contextmanager +def package_defaults() -> Generator[argparse.Namespace, None, None]: + """Returns package defaults.""" + if sys.version_info >= (3, 9): + files = importlib.resources.files("proto2ros.configuration") + managed_path = importlib.resources.as_file(files.joinpath("default.yaml")) + with managed_path as path: + yield argparse.Namespace(config_file=path) + else: + with importlib.resources.path("proto2ros.configuration", "default.yaml") as path: + yield argparse.Namespace(config_file=path) + + +def parse_args(defaults: argparse.Namespace) -> argparse.Namespace: + """Parses command line arguments.""" parser = argparse.ArgumentParser(description="Generate Protobuf <-> ROS 2 interoperability interfaces") parser.add_argument( "-O", @@ -157,7 +171,7 @@ def main() -> int: "-c", "--config-file", type=pathlib.Path, - default=importlib.resources.path("proto2ros.configuration", "default.yaml"), + default=defaults.config_file, help="Base configuration file for the generation procedure.", ) parser.add_argument( @@ -196,8 +210,14 @@ def main() -> int: nargs="+", help="Protobuf descriptor files to process, as generated by protoc.", ) - args = parser.parse_args() - return do_generate(args) + return parser.parse_args() + + +def main() -> int: + """Entrypoint for proto2ros""" + with package_defaults() as defaults: + args = parse_args(defaults) + return do_generate(args) if __name__ == "__main__":