diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..0bea168ed --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: Build + +on: + workflow_call: + +env: + CMAKE_BUILD_OPTS: --parallel 16 + CMAKE_EXTRA_OPTS: -DCMAKE_BUILD_TYPE=Release + CACHIX_CACHE_NAME: villas + +permissions: + contents: read + packages: write + +jobs: + build-source: + name: "build:source [${{ matrix.distro }}]" + runs-on: ubuntu-latest + container: + image: ghcr.io/villasframework/villas/node/dev-${{ matrix.image_name }}:${{ github.sha }} + options: --user root + strategy: + fail-fast: false + matrix: + include: + - distro: fedora + image_name: fedora + cmake_extra_opts: -DVILLAS_COMPILE_WARNING_AS_ERROR=ON + - distro: fedora-minimal + image_name: fedora-minimal + cmake_extra_opts: >- + -DVILLAS_COMPILE_WARNING_AS_ERROR=ON + -DWITH_API=OFF + -DWITH_CLIENTS=OFF + -DWITH_CONFIG=OFF + -DWITH_DOC=OFF + -DWITH_FPGA=OFF + -DWITH_GRAPHVIZ=OFF + -DWITH_HOOKS=OFF + -DWITH_LUA=OFF + -DWITH_OPENMP=OFF + -DWITH_PLUGINS=OFF + -DWITH_SRC=OFF + -DWITH_TESTS=OFF + -DWITH_TOOLS=OFF + -DWITH_WEB=OFF + -DCMAKE_MODULE_PATH=/usr/local/lib64/cmake + -DCMAKE_PREFIX_PATH=/usr/local + - distro: debian + image_name: debian + - distro: rocky + image_name: rocky + - distro: rocky9 + image_name: rocky9 + - distro: ubuntu + image_name: ubuntu + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure + run: cmake -S . -B build ${{ matrix.cmake_extra_opts || env.CMAKE_EXTRA_OPTS }} + + - name: Build + run: cmake --build build ${{ env.CMAKE_BUILD_OPTS }} + + - uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.distro }} + path: build/ + retention-days: 7 + + # build-nix: + # name: "build:nix [${{ matrix.system }}]" + # runs-on: ubuntu-latest + # container: + # image: docker.nix-community.org/nixpkgs/cachix-flakes + # strategy: + # fail-fast: false + # matrix: + # system: [x86_64-linux, aarch64-linux] + # steps: + # - uses: actions/checkout@v4 + # with: + # fetch-depth: 0 + # submodules: recursive + # + # - name: Configure Cachix + # run: cachix use "${{ env.CACHIX_CACHE_NAME }}" + # env: + # CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + # + # - name: Build + # run: | + # cachix watch-exec "${{ env.CACHIX_CACHE_NAME }}" -- \ + # nix build --print-build-logs ".#villas-node-${{ matrix.system }}" + + build-openapi: + name: build:openapi + runs-on: ubuntu-latest + container: + image: node:24-alpine + steps: + - uses: actions/checkout@v4 + + - name: Lint OpenAPI spec + run: npx -y @redocly/cli lint --config doc/redocly.yaml doc/openapi/openapi.yaml + + - name: Build OpenAPI docs + run: npx -y @redocly/cli build-docs --config doc/redocly.yaml doc/openapi/openapi.yaml --output openapi.html + + - uses: actions/upload-artifact@v4 + with: + name: openapi + path: openapi.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..3f0adc93f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: CI + +on: + push: + branches: + tags: + pull_request: + branches: + workflow_dispatch: + inputs: + publish_latest: + description: Publish dev and app images as latest + type: boolean + default: false + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + prepare: + uses: ./.github/workflows/prepare.yml + secrets: inherit + + build: + needs: [prepare] + uses: ./.github/workflows/build.yml + secrets: inherit + + test: + needs: [build] + uses: ./.github/workflows/test.yml + secrets: inherit + + packaging: + if: github.event_name != 'pull_request' + needs: [test] + uses: ./.github/workflows/packaging.yml + secrets: inherit + + deploy: + if: github.event_name != 'pull_request' + needs: [packaging] + uses: ./.github/workflows/deploy.yml + with: + publish_latest: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_latest || false }} + secrets: inherit diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..d72d8bb42 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,117 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: Deploy + +on: + workflow_call: + inputs: + publish_latest: + required: false + type: boolean + default: false + +env: + REGISTRY: ghcr.io + DOCKER_IMAGE: ghcr.io/villasframework/villas/node + +permissions: + contents: read + packages: write + +jobs: + deploy-docker: + name: deploy:docker + runs-on: ubuntu-latest + steps: + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push multi-arch manifest + run: | + docker manifest rm "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}" || true + docker manifest create "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-x86_64" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-arm64" + docker manifest push "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}" + + latest-docker: + name: latest:docker + if: startsWith(github.ref, 'refs/tags/v') + needs: [deploy-docker] + runs-on: ubuntu-latest + steps: + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag and push app image as latest + run: | + docker manifest rm "${{ env.DOCKER_IMAGE }}:latest" || true + docker manifest create "${{ env.DOCKER_IMAGE }}:latest" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-x86_64" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-arm64" + docker manifest push "${{ env.DOCKER_IMAGE }}:latest" + + latest-docker-dev: + name: latest:docker-dev + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag and push dev image as latest + run: | + docker buildx imagetools create \ + -t "${{ env.DOCKER_IMAGE }}/dev:latest" \ + "${{ env.DOCKER_IMAGE }}/dev:${{ github.ref_name }}" + + latest-docker-manual: + name: latest:docker (manual) + if: inputs.publish_latest + needs: [deploy-docker] + runs-on: ubuntu-latest + steps: + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag and push app image as latest (manual) + run: | + docker manifest rm "${{ env.DOCKER_IMAGE }}:latest" || true + docker manifest create "${{ env.DOCKER_IMAGE }}:latest" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-x86_64" \ + "${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-arm64" + docker manifest push "${{ env.DOCKER_IMAGE }}:latest" + + latest-docker-dev-manual: + name: latest:docker-dev (manual) + if: inputs.publish_latest + runs-on: ubuntu-latest + steps: + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag and push dev image as latest (manual) + run: | + docker buildx imagetools create \ + -t "${{ env.DOCKER_IMAGE }}/dev:latest" \ + "${{ env.DOCKER_IMAGE }}/dev:${{ github.ref_name }}" diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml deleted file mode 100644 index 7762b1a80..000000000 --- a/.github/workflows/mirror.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2025 OPAL-RT Germany GmbH -# SPDX-License-Identifier: Apache-2.0 - ---- -name: Mirror to RWTH GitLab - -on: - push: - delete: - -jobs: - mirror: - name: Mirror - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: yesolutions/mirror-action@master - with: - REMOTE: https://git.rwth-aachen.de/acs/public/villas/node.git - GIT_USERNAME: github - GIT_PASSWORD: ${{ secrets.RWTH_GITLAB_TOKEN }} - PUSH_ALL_REFS: "false" diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml new file mode 100644 index 000000000..d58847fa5 --- /dev/null +++ b/.github/workflows/packaging.yml @@ -0,0 +1,174 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: Packaging + +on: + workflow_call: + +env: + REGISTRY: ghcr.io + DOCKER_IMAGE: ghcr.io/villasframework/villas/node + CMAKE_EXTRA_OPTS: -DCMAKE_BUILD_TYPE=Release + CACHIX_CACHE_NAME: villas + +permissions: + contents: read + packages: write + +jobs: + pkg-docker: + name: "pkg:docker [${{ matrix.arch }}]" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - distro: debian + platform: linux/amd64 + arch: x86_64 + triplet: x86_64-linux-gnu + - distro: debian + platform: linux/arm64 + arch: arm64 + triplet: aarch64-linux-gnu + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - uses: docker/setup-qemu-action@v3 + if: matrix.arch != 'x86_64' + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/build-push-action@v6 + with: + context: . + file: packaging/docker/Dockerfile.${{ matrix.distro }} + target: app + push: true + pull: true + platforms: ${{ matrix.platform }} + build-args: | + ARCH=${{ matrix.arch }} + TRIPLET=${{ matrix.triplet }} + CMAKE_OPTS=${{ env.CMAKE_EXTRA_OPTS }} + tags: ${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-${{ matrix.arch }} + + pkg-nix-arx: + name: "pkg:nix:arx [${{ matrix.system }}]" + runs-on: ubuntu-latest + container: + image: docker.nix-community.org/nixpkgs/cachix-flakes + strategy: + fail-fast: false + matrix: + system: [x86_64-linux, aarch64-linux] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure Cachix + run: cachix use "${{ env.CACHIX_CACHE_NAME }}" + env: + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + + - name: Bundle as arx + run: | + cachix watch-exec "${{ env.CACHIX_CACHE_NAME }}" -- \ + nix bundle --print-build-logs --out-link bundle-arx \ + --bundler github:nix-community/nix-bundle \ + ".#villas-node-${{ matrix.system }}" + mkdir -p artifacts + cp -L bundle-arx artifacts/villas-${{ matrix.system }} + + - uses: actions/upload-artifact@v4 + with: + name: villas-arx-${{ matrix.system }} + path: artifacts/ + retention-days: 365 + + pkg-nix-rpm: + name: "pkg:nix:rpm [${{ matrix.system }}]" + runs-on: ubuntu-latest + container: + image: docker.nix-community.org/nixpkgs/cachix-flakes + strategy: + fail-fast: false + matrix: + system: [x86_64-linux, aarch64-linux] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure Cachix + run: cachix use "${{ env.CACHIX_CACHE_NAME }}" + env: + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + + - name: Bundle as RPM + run: | + cachix watch-exec "${{ env.CACHIX_CACHE_NAME }}" -- \ + nix bundle --print-build-logs --out-link bundle-rpm \ + --bundler github:NixOS/bundlers#toRPM \ + ".#villas-node-${{ matrix.system }}" + mkdir -p artifacts + cp -L bundle-rpm/*.rpm artifacts/villas-${{ matrix.system }}.rpm + + - uses: actions/upload-artifact@v4 + with: + name: villas-rpm-${{ matrix.system }} + path: artifacts/ + retention-days: 365 + + pkg-nix-docker: + name: "pkg:nix:docker [${{ matrix.system }}]" + runs-on: ubuntu-latest + container: + image: docker.nix-community.org/nixpkgs/cachix-flakes + strategy: + fail-fast: false + matrix: + system: [x86_64-linux, aarch64-linux] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure Cachix + run: cachix use "${{ env.CACHIX_CACHE_NAME }}" + env: + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + + - name: Bundle as Docker image + run: | + mkdir -p /var/tmp/ + cachix watch-exec "${{ env.CACHIX_CACHE_NAME }}" -- \ + nix bundle --print-build-logs --out-link bundle-docker \ + --bundler github:NixOS/bundlers#toDockerImage \ + ".#villas-node-${{ matrix.system }}" + + - name: Push to registry + run: | + nix run nixpkgs#skopeo -- login \ + --username "${{ github.actor }}" \ + --password "${{ secrets.GITHUB_TOKEN }}" \ + "${{ env.REGISTRY }}" + nix run nixpkgs#skopeo -- \ + --tmpdir="${{ runner.temp }}" \ + --insecure-policy \ + copy "docker-archive:./bundle-docker" \ + "docker://${{ env.DOCKER_IMAGE }}:${{ github.ref_name }}-nix-${{ matrix.system }}" diff --git a/.github/workflows/prepare.yml b/.github/workflows/prepare.yml new file mode 100644 index 000000000..dad9edc0a --- /dev/null +++ b/.github/workflows/prepare.yml @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: Prepare + +on: + workflow_call: + +env: + REGISTRY: ghcr.io + DOCKER_IMAGE: ghcr.io/villasframework/villas/node + DOCKER_TAG: ${{ github.sha }} + +permissions: + contents: read + packages: write + +jobs: + prepare-docker: + name: "prepare:docker [${{ matrix.name }}]" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: ubuntu + docker_file: packaging/docker/Dockerfile.ubuntu + image_name: ubuntu + - name: debian + docker_file: packaging/docker/Dockerfile.debian + image_name: debian + - name: rocky + docker_file: packaging/docker/Dockerfile.rocky + image_name: rocky + - name: rocky9 + docker_file: packaging/docker/Dockerfile.rocky9 + image_name: rocky9 + - name: fedora + docker_file: packaging/docker/Dockerfile.fedora + image_name: fedora + - name: fedora-minimal + docker_file: packaging/docker/Dockerfile.fedora-minimal + image_name: fedora-minimal + - name: fedora-vscode + docker_file: packaging/docker/Dockerfile.fedora + image_name: vscode + target: dev-vscode + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/setup-buildx-action@v3 + + - name: Build and push dev image + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.docker_file }} + target: ${{ matrix.target || 'dev' }} + push: true + tags: | + ${{ env.DOCKER_IMAGE }}/dev-${{ matrix.image_name }}:${{ env.DOCKER_TAG }} + ${{ env.DOCKER_IMAGE }}/dev-${{ matrix.image_name }}:${{ github.ref_name }} + + - name: Publish fedora dev alias + if: matrix.name == 'fedora' + run: | + docker buildx imagetools create \ + -t "${{ env.DOCKER_IMAGE }}/dev:${{ env.DOCKER_TAG }}" \ + -t "${{ env.DOCKER_IMAGE }}/dev:${{ github.ref_name }}" \ + "${{ env.DOCKER_IMAGE }}/dev-fedora:${{ env.DOCKER_TAG }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..5cb4a9e9d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,172 @@ +# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +name: Test + +on: + workflow_call: + +env: + CMAKE_BUILD_OPTS: --parallel 16 + CMAKE_OPTS: -Wno-dev -G=Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} + +permissions: + contents: read + packages: read + +jobs: + # test-pre-commit: + # name: test:pre-commit + # runs-on: ubuntu-latest + # container: + # image: python:3.12.10-slim-bookworm + # options: --user root + # steps: + # - name: Install git + # run: apt-get update && apt-get -y install git ruby + # + # - uses: actions/checkout@v4 + # + # - name: Install dependencies + # run: + # # apt-get update && apt-get -y install git ruby + # pip install --no-cache-dir pre-commit + # + # # - name: Add Git safe directory + # # run: git config --global --add safe.directory '*' + # + # - name: Run pre-commit + # run: | + # git version + # pre-commit run --all-files + # cat /github/home/.cache/pre-commit/pre-commit.log + # + # + # - name: Upload log + # if: failure() + # uses: actions/upload-artifact@v4 + # with: + # name: pre-commit-log + # path: /github/home/.cache/pre-commit/pre-commit.log + + test-python: + name: test:python + runs-on: ubuntu-latest + container: + image: ghcr.io/villasframework/villas/node/dev-fedora:${{ github.sha }} + options: --user root + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Run Python tests + run: | + cd python + pytest --verbose . + + - name: Check formatting + run: | + cd python + black --line-length=90 --extend-exclude=".*(\.pyi|_pb2.py)$" --check . + + - name: Lint with flake8 + run: | + cd python + flake8 --max-line-length=90 --extend-exclude="*.pyi,*_pb2.py" . + + - name: Type check with mypy + run: | + cd python + mypy --namespace-packages --explicit-package-bases . + + test-cppcheck: + name: test:cppcheck + runs-on: ubuntu-latest + container: + image: ghcr.io/villasframework/villas/node/dev-fedora:${{ github.sha }} + options: --user root + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Run cppcheck + run: ./tools/run-cppcheck.sh | tee cppcheck.log + + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: cppcheck-log + path: cppcheck.log + + test-unit: + name: test:unit + runs-on: ubuntu-latest + container: + image: ghcr.io/villasframework/villas/node/dev-fedora:${{ github.sha }} + options: --user root + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure + run: cmake -S . -B build + + - name: Build and run unit tests + run: LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH cmake --build build ${{ env.CMAKE_BUILD_OPTS }} --target run-unit-tests run-unit-tests-common + + # test-integration: + # name: test:integration + # runs-on: ubuntu-latest + # container: + # image: ghcr.io/villasframework/villas/node/dev-fedora:${{ github.sha }} + # options: --user root + # services: + # rabbitmq: + # image: rwthacs/rabbitmq + # redis: + # image: redis:6.2 + # steps: + # - uses: actions/checkout@v4 + # with: + # fetch-depth: 0 + # submodules: recursive + # + # - name: Start mosquitto + # run: | + # echo "127.0.0.1 mosquitto" >> /etc/hosts + # cat > /tmp/mosquitto.conf <<'EOF' + # listener 1883 + # allow_anonymous true + # EOF + # mosquitto -c /tmp/mosquitto.conf -d + # + # - name: Configure + # run: | + # cmake -S . -B build --target install ${{ env.CMAKE_OPTS }} + # + # - name: Build and run integration tests + # # run: cmake --build build ${{ env.CMAKE_BUILD_OPTS }} --target run-integration-tests + # run: cmake --build build --target run-integration-tests + # - uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: integration-tests + # path: build/tests/integration/ + + test-reuse: + name: test:reuse + runs-on: ubuntu-latest + container: + image: fsfe/reuse:latest + options: --entrypoint "" + steps: + - uses: actions/checkout@v4 + + - name: Run REUSE lint + run: reuse lint