diff --git a/.firmwareci/.jinja2_templates/defaults.j2 b/.firmwareci/.jinja2_templates/defaults.j2 new file mode 100644 index 00000000..eb50cbb6 --- /dev/null +++ b/.firmwareci/.jinja2_templates/defaults.j2 @@ -0,0 +1,14 @@ +## Can be changed depending on the FWCI tester device used +{%- set RPI_HOSTNAME = "fwci-dutctl-tester" %} +{%- set RPI_FQDN = RPI_HOSTNAME ~ ".sec.9e.network" %} +## Do not change values below, unless you know what you are doing +{%- set PKG_NAME = "dutctl" %} +{%- set DUTCTL_CONFIG_FILE = "/etc/dutctl.yaml" %} +{%- set SYSTEMD_FILE = "dutagent.service" %} +{%- set DUTAGENT_PORT = "2024" %} +{%- set DUTCTL_ARGS = '"-s", "' ~ RPI_HOSTNAME ~ ':' ~ DUTAGENT_PORT ~ '"' %} +{%- set DUTCTL_ARGS_STR = '-s ' ~ RPI_HOSTNAME ~ ':' ~ DUTAGENT_PORT %} +## Values below should not change +{%- set GPIO_PIN_TEST_OLIMEX = "21" %} +{%- set FAKE_SERIAL_INPUT = "/home/oscar/ttyS1" %} +{%- set FAKE_SERIAL_OUTPUT = "/home/oscar/ttyS2" %} diff --git a/.firmwareci/.jinja2_templates/defaults.yaml b/.firmwareci/.jinja2_templates/defaults.yaml new file mode 100644 index 00000000..f28e72ac --- /dev/null +++ b/.firmwareci/.jinja2_templates/defaults.yaml @@ -0,0 +1,15 @@ +transport_oscar: &transport_oscar + proto: ssh + options: + host: "[[attributes.Host]]" + user: oscar + identity_file: "/root/.ssh/fwci" + # TODO: The ssh keys (identity_file) are hard-coded in the FWCI docker at the moment + +transport_root: &transport_root + proto: ssh + options: + host: "[[attributes.Host]]" + user: root + identity_file: "/root/.ssh/fwci" + # TODO: The ssh keys (identity_file) are hard-coded in the FWCI docker at the moment diff --git a/.firmwareci/.jinja2_templates/post.yaml.j2 b/.firmwareci/.jinja2_templates/post.yaml.j2 new file mode 100644 index 00000000..c2f293b4 --- /dev/null +++ b/.firmwareci/.jinja2_templates/post.yaml.j2 @@ -0,0 +1,33 @@ +post-stage: + # =========================================== + # Delete the files copied over for the test + # =========================================== + - cmd: shell + name: Stop remotelab-server systemd service + transport: *transport_root + parameters: + command: "systemctl stop {{ defaults.SYSTEMD_FILE }} || true" + + - cmd: shell + name: Uninstall the package + transport: *transport_root + parameters: + command: "apt remove --assume-yes {{ defaults.PKG_NAME }} || true" + + - cmd: shell + name: Delete testing configuration file + transport: *transport_root + parameters: + command: "rm {{ defaults.DUTCTL_CONFIG_FILE }} || true" + + - cmd: shell + name: Delete package + transport: *transport_root + parameters: + command: "rm {{ defaults.PKG_NAME }}.deb || true" + + - cmd: shell + name: Shutdown fake serial + transport: *transport_oscar + parameters: + command: "pkill socat || true" diff --git a/.firmwareci/.jinja2_templates/pre.yaml.j2 b/.firmwareci/.jinja2_templates/pre.yaml.j2 new file mode 100644 index 00000000..ededcc62 --- /dev/null +++ b/.firmwareci/.jinja2_templates/pre.yaml.j2 @@ -0,0 +1,76 @@ +pre-stage: + # Fix permissions + - cmd: cmd + name: chmod dutctl + transport: + proto: local + parameters: + executable: chmod + args: ["755", "[[input.DUTCTL]]"] + + # ======================== + # Copy files over to RPI + # ======================== + - cmd: copy + name: Copy over {{ defaults.PKG_NAME }} debian package + transport: *transport_root + parameters: + source: "[[input.REMOTE_PKG]]" + destination: "{{ defaults.PKG_NAME }}.deb" + + - cmd: cmd + name: Install {{ defaults.PKG_NAME }} package + transport: *transport_root + parameters: + executable: dpkg + args: ["-i", "{{ defaults.PKG_NAME }}.deb"] + + - cmd: copy + name: Copy over test config file + transport: *transport_root + parameters: + source: "[[input.CONFIG]]" + destination: {{ defaults.DUTCTL_CONFIG_FILE }} + + - cmd: cmd + name: chmod config file + transport: *transport_root + parameters: + executable: chmod + args: ["644", {{ defaults.DUTCTL_CONFIG_FILE }}] + + # ============== + # Start server + # ============== + - cmd: cmd + name: Reload systemctl daemon + transport: *transport_root + parameters: + executable: systemctl + args: ["daemon-reload"] + + - cmd: cmd + name: Start systemd service + transport: *transport_root + parameters: + executable: systemctl + args: ["start", {{ defaults.SYSTEMD_FILE }}] + + - cmd: shell + name: Status systemd service + transport: *transport_root + parameters: + command: "systemctl status --no-pager {{ defaults.SYSTEMD_FILE }}" + expect: + - regex: "Active: active \\(running\\)" + + # ========================== + # Test dutctl help command + # ========================== + - cmd: cmd + name: dutctl help + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["--help"] diff --git a/.firmwareci/README.md b/.firmwareci/README.md new file mode 100644 index 00000000..968870f0 --- /dev/null +++ b/.firmwareci/README.md @@ -0,0 +1,108 @@ +# FirmwareCI configuration for dutctl testing + +This directory contains FirmwareCI configurations and tests. + +For detailed information on FirmwareCI, please refer to the [official documentation](https://docs.firmware-ci.com/). + + +## Requirements + +- Parametric and re-usable testing +- Easy to expand +- Reliable +- Can be used in CI/CD to verify each pull request + + +## Design decisions + +### Jinja templating +We want to test multiple features (flashing, power control, serial, etc). And to keep everything nicely organised we need to have each feature-specific test as it's own test. + +However, the setup and tear-down of the tests is rather complex: +- copy over the compiled binaries +- copy over configuration files +- spin up the server (agent) +- execute the test +- shut-down the server +- delete the copied binaries +- delete configuration files + +In case of the serial feature, it also means spinning-up "fake" / "dummy" serial that can be used for the testing. + +It would be stupid to copy-paste all this setup into each test file, especially because keeping all of them in sync would be a nightmare. + +For this reason I have decided to use [jinja2 templates](https://jinja.palletsprojects.com/en/stable/api/) to automate this menial and error-prone work. To make it even simpler, I have also included [Taskfile](https://taskfile.dev/docs/guide) which has already all the jobs written there, so to generate all of the templates is as easy as simply calling `task jinja2:templates`. + +Thanks to jinja templating, and how the tests are structured, tests are parametric and easy to expand. + + +### Debian packaging magic +As you might notice, instead of simply copying over the compiled binaries (`dutctl` / `dutagent` / ...) we use Debian packages. + +The biggest motivator here to do this was to accommodate for setup and tear-down, and the differences between tested versions (future-proofing). This way, we leverage the power of package manager to make sure that the cleanup (uninstalling) of old file is complete and that no files are left behind! This makes sure that the environment is always pristine. + +Another huge advantage is, that with this approach, that run-time dependencies are also handled by package manager. Because of this, the test hardware (Raspberry Pi) has minimal (if any) setup required for use in the FWCI tests. Just install package, test and uninstall! Easy! + + +## Project structure + +The most important files / directories are: +- `.firmwareci/Taskfile.yml` +- `.firmwareci/.jinja2_templates/` +- `.firmwareci/workflows/` + +### Taskfile +The `.firmwareci/Taskfile.yml` is there to help and automate some mundane and repetitive jobs. + +Contains task `jinja2:templates` which finds all jinja tempaltes in `.firmwareci/workflows/` and `.firmwareci/duts/` and runs a tempalting engine on them. + +The list of the templates is created dynamically with the shell command: +```bash +find './workflows/' './duts/' -type f -name '*.yaml.j2' +``` + +This list is then iterated over in a for-loop, such as that it takes `./duts/dut-rpi-fti-tester/dut.yaml.j2` as input, and produces `./duts/dut-rpi-fti-tester/dut.yaml`. + + +### jinja2_templates +The `.firmwareci/.jinja2_templates` contains the "common" building-blocks that are shared across templates. The `defaults.j2` and `defaults.yaml` are for storing some basics (they are intentionally separate): +- `defaults.j2` + - this is pure jinja2 syntax, and is imported into template with + ```j2 + {%- import ".jinja2_templates/defaults.j2" as defaults %} + ``` + - import means that the variables will become accessible, and can then be accessed in the template with: + ```j2 + {{ defaults.DUTCTL_ARGS }} + ``` +- `defaults.yaml` + - this is pure YAML file to be `include`d into a template with + ```j2 + {% include ".jinja2_templates/defaults.yaml" with context %} + ``` + - essentially this is to just simply render the content of the file, ignoring any variable assignments or macros within it + - technically speaking it could be renamed to `defaults.yaml.j2`, but it does not contain any jinja2 code and renaming it would have no effect +- `pre.yaml.j2` and `post.yaml.j2` + - for pre-stage and post-stage respectively + - they are copy-pasted into each test (they are `include`d in tests, meaning that their content will be rendered) + + +### workflows +The `workflows` directory is where the tests live. + +### Systemd service +The `dutagent.service` file is a systemd service file to start a dutagent on dutworker. + +At the moment it is only used for firmware-ci testing. + + +## Adding completely new test +To add a completely new tests from scratch, create a new `.yaml.j2` file in `.firmwareci/workflows/workflow-rpi-dutctl-tester/`. I would recommend to copy-paste some existing test (for example the `dummy-modules.yaml.j2` is rather simple) and use it as basis. + +Then update the test according to your needs. + +Run `task fwci:jinja2:templates` and it will automatically generate a complete tests. That is it. It is as simple as that. + +Next, I recommend to run `fwci fwci:validate`. + +As the last step, add all new files (even those jinja2 generated) into git commit. This is because FirmwareCI cannot pre-process the tests on it's own, which is why we have to include even the "generated code". diff --git a/.firmwareci/Taskfile.yml b/.firmwareci/Taskfile.yml new file mode 100644 index 00000000..64a396ce --- /dev/null +++ b/.firmwareci/Taskfile.yml @@ -0,0 +1,50 @@ +--- +version: "3" + +tasks: + jinja2:templates: + desc: Run jinja2 on template files + cmds: + - task: jinja2:find-and-process-all-tempaltes + + fwci:validate: + desc: Validate FWCI files + cmds: + - fwci validate + + #================ + # Internal tasks + #================ + + jinja2:find-and-process-all-tempaltes: + desc: Run jinja2 on jinja2 templates + internal: true + label: 'jinja2-tests-{{.REPO}}' + vars: + WORKFLOWS: + sh: find './workflows/' './duts/' -type f -name '*.yaml.j2' + sources: + - .jinja2_templates/* + - duts/**/*.yaml.j2 + - workflows/workflow-*/defaults.j2 + - workflows/workflow-*/**/*.yaml.j2 + cmds: + - for: + var: WORKFLOWS + task: jinja2:run-on-file + vars: + INPUTFILE: + sh: echo "{{.ITEM}}" + OUTPUTFILE: + sh: echo "{{.ITEM}}" | sed -E 's/\.j2$//g' + + jinja2:run-on-file: + desc: Run jinja2 on a file + internal: true + cmds: + - pwd + - echo 'INPUTFILE = {{.INPUTFILE}}' + - echo 'OUTPUTFILE = {{.OUTPUTFILE}}' + - j2 --format env -o '{{.OUTPUTFILE}}' '{{.INPUTFILE}}' + requires: + vars: ["INPUTFILE", "OUTPUTFILE"] diff --git a/.firmwareci/dutagent.service b/.firmwareci/dutagent.service new file mode 100644 index 00000000..2322f3cf --- /dev/null +++ b/.firmwareci/dutagent.service @@ -0,0 +1,15 @@ +[Unit] +Description=dutagent to run on SBC attached to a devices under test +Documentation=https://github.com/BlindspotSoftware/dutctl +Wants=network-online.target +After=network-online.target +StartLimitInterval=15 +StartLimitBurst=3 + +[Service] +Restart=always +RestartSec=3s +ExecStart=/usr/bin/dutagent -a 0.0.0.0:2024 -c /etc/dutctl.yaml + +[Install] +WantedBy=multi-user.target diff --git a/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml b/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml new file mode 100644 index 00000000..e2c9f11c --- /dev/null +++ b/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml @@ -0,0 +1,6 @@ +--- +# Device-under-Test Configuration for RaspberryPi acting as dutctl tester +name: fwci-dutctl-tester +label: fwci-dutctl-tester +attributes: + Host: "fwci-dutctl-tester.sec.9e.network" diff --git a/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml.j2 b/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml.j2 new file mode 100644 index 00000000..322fec55 --- /dev/null +++ b/.firmwareci/duts/dut-rpi-dutctl-tester/dut.yaml.j2 @@ -0,0 +1,7 @@ +--- +# Device-under-Test Configuration for RaspberryPi acting as dutctl tester +{%- import ".jinja2_templates/defaults.j2" as defaults %} +name: {{ defaults.RPI_HOSTNAME }} +label: {{ defaults.RPI_HOSTNAME }} +attributes: + Host: "{{ defaults.RPI_FQDN }}" diff --git a/.firmwareci/storage/.gitkeep b/.firmwareci/storage/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml b/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml new file mode 100644 index 00000000..c7138d48 --- /dev/null +++ b/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml @@ -0,0 +1,230 @@ +--- +name: Test dummy modules +description: Test that all dummy modules work + +defaults: + transport_oscar: &transport_oscar + proto: ssh + options: + host: "[[attributes.Host]]" + user: oscar + identity_file: "/root/.ssh/fwci" + # TODO: The ssh keys (identity_file) are hard-coded in the FWCI docker at the moment + + transport_root: &transport_root + proto: ssh + options: + host: "[[attributes.Host]]" + user: root + identity_file: "/root/.ssh/fwci" + # TODO: The ssh keys (identity_file) are hard-coded in the FWCI docker at the moment + + +pre-stage: + # Fix permissions + - cmd: cmd + name: chmod dutctl + transport: + proto: local + parameters: + executable: chmod + args: ["755", "[[input.DUTCTL]]"] + + # ======================== + # Copy files over to RPI + # ======================== + - cmd: copy + name: Copy over dutctl debian package + transport: *transport_root + parameters: + source: "[[input.REMOTE_PKG]]" + destination: "dutctl.deb" + + - cmd: cmd + name: Install dutctl package + transport: *transport_root + parameters: + executable: dpkg + args: ["-i", "dutctl.deb"] + + - cmd: copy + name: Copy over test config file + transport: *transport_root + parameters: + source: "[[input.CONFIG]]" + destination: /etc/dutctl.yaml + + - cmd: cmd + name: chmod config file + transport: *transport_root + parameters: + executable: chmod + args: ["644", /etc/dutctl.yaml] + + # ============== + # Start server + # ============== + - cmd: cmd + name: Reload systemctl daemon + transport: *transport_root + parameters: + executable: systemctl + args: ["daemon-reload"] + + - cmd: cmd + name: Start systemd service + transport: *transport_root + parameters: + executable: systemctl + args: ["start", dutagent.service] + + - cmd: shell + name: Status systemd service + transport: *transport_root + parameters: + command: "systemctl status --no-pager dutagent.service" + expect: + - regex: "Active: active \\(running\\)" + + # ========================== + # Test dutctl help command + # ========================== + - cmd: cmd + name: dutctl help + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["--help"] + + +# "contrib/dutagent-cfg-example.yaml" + +stages: + - name: Test Device 1 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["-s", "fwci-dutctl-tester:2024", "device1", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 0 arguments" + options: + timeout: 1s + + - cmd: cmd + name: Test status with argument + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["-s", "fwci-dutctl-tester:2024", "device1", "status", "hello"] + expect: + - regex: "Hello from dummy status module\nCalled with 1 arguments\nArg 0: hello" + options: + timeout: 1s + + - name: Test Device 2 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["-s", "fwci-dutctl-tester:2024", "device2", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 2 arguments" + options: + timeout: 1s + + - cmd: shell + name: Test repeat + transport: + proto: local + parameters: + command: "echo -e 'hello\nhello world\n' | [[input.DUTCTL]] -s fwci-dutctl-tester:2024 device2 repeat" + expect: + - regex: "Hello from dummy repeat module!\nEnter one word per line." + options: + timeout: 1s + + - name: Test Device 3 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["-s", "fwci-dutctl-tester:2024", "device3", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 0 arguments" + options: + timeout: 1s + + - cmd: shell + name: Create test file + transport: + proto: local + parameters: + command: "echo 'hello' > test.txt" + + - cmd: cmd + name: Test file transfer + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: ["-s", "fwci-dutctl-tester:2024", "device3", "file-transfer", "test.txt", "test2.txt"] + expect: + - regex: "Hello from dummy file transfer module" + options: + timeout: 1s + + - cmd: cmd + name: Read test file + transport: + proto: local + parameters: + executable: "cat" + args: ["test2.txt"] + expect: + - regex: "processed by dummy.FT module" + +post-stage: + # =========================================== + # Delete the files copied over for the test + # =========================================== + - cmd: shell + name: Stop remotelab-server systemd service + transport: *transport_root + parameters: + command: "systemctl stop dutagent.service || true" + + - cmd: shell + name: Uninstall the package + transport: *transport_root + parameters: + command: "apt remove --assume-yes dutctl || true" + + - cmd: shell + name: Delete testing configuration file + transport: *transport_root + parameters: + command: "rm /etc/dutctl.yaml || true" + + - cmd: shell + name: Delete package + transport: *transport_root + parameters: + command: "rm dutctl.deb || true" + + - cmd: shell + name: Shutdown fake serial + transport: *transport_oscar + parameters: + command: "pkill socat || true" diff --git a/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml.j2 b/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml.j2 new file mode 100644 index 00000000..f8ac81f6 --- /dev/null +++ b/.firmwareci/workflows/workflow-rpi-dutctl-tester/tests/dummy-modules.yaml.j2 @@ -0,0 +1,109 @@ +--- +name: Test dummy modules +description: Test that all dummy modules work + +defaults: + {%- import ".jinja2_templates/defaults.j2" as defaults %} + {%- macro someop() %}{% include ".jinja2_templates/defaults.yaml" with context %}{% endmacro %} + {{ someop()|indent(2) }} + +{% include ".jinja2_templates/pre.yaml.j2" %} + +# "contrib/dutagent-cfg-example.yaml" + +stages: + - name: Test Device 1 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: [{{ defaults.DUTCTL_ARGS }}, "device1", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 0 arguments" + options: + timeout: 1s + + - cmd: cmd + name: Test status with argument + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: [{{ defaults.DUTCTL_ARGS }}, "device1", "status", "hello"] + expect: + - regex: "Hello from dummy status module\nCalled with 1 arguments\nArg 0: hello" + options: + timeout: 1s + + - name: Test Device 2 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: [{{ defaults.DUTCTL_ARGS }}, "device2", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 2 arguments" + options: + timeout: 1s + + - cmd: shell + name: Test repeat + transport: + proto: local + parameters: + command: "echo -e 'hello\nhello world\n' | [[input.DUTCTL]] {{ defaults.DUTCTL_ARGS_STR }} device2 repeat" + expect: + - regex: "Hello from dummy repeat module!\nEnter one word per line." + options: + timeout: 1s + + - name: Test Device 3 + steps: + - cmd: cmd + name: Test status + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: [{{ defaults.DUTCTL_ARGS }}, "device3", "status"] + expect: + - regex: "Hello from dummy status module\nCalled with 0 arguments" + options: + timeout: 1s + + - cmd: shell + name: Create test file + transport: + proto: local + parameters: + command: "echo 'hello' > test.txt" + + - cmd: cmd + name: Test file transfer + transport: + proto: local + parameters: + executable: "[[input.DUTCTL]]" + args: [{{ defaults.DUTCTL_ARGS }}, "device3", "file-transfer", "test.txt", "test2.txt"] + expect: + - regex: "Hello from dummy file transfer module" + options: + timeout: 1s + + - cmd: cmd + name: Read test file + transport: + proto: local + parameters: + executable: "cat" + args: ["test2.txt"] + expect: + - regex: "processed by dummy.FT module" + +{% include ".jinja2_templates/post.yaml.j2" -%} diff --git a/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml b/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml new file mode 100644 index 00000000..e8a1588a --- /dev/null +++ b/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml @@ -0,0 +1,4 @@ +--- +name: test-dutctl +description: "This is workflow configuration for new dutctl testing" +runs-on: fwci-dutctl-tester diff --git a/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml.j2 b/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml.j2 new file mode 100644 index 00000000..1deed8b5 --- /dev/null +++ b/.firmwareci/workflows/workflow-rpi-dutctl-tester/workflow.yaml.j2 @@ -0,0 +1,5 @@ +--- +{%- import ".jinja2_templates/defaults.j2" as defaults %} +name: test-dutctl +description: "This is workflow configuration for new dutctl testing" +runs-on: {{ defaults.RPI_HOSTNAME }} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d5199fa8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +.firmwareci/duts/dut-rpi-dutctl-tester/*.yaml linguist-generated=true +.firmwareci/workflows/workflow-rpi-dutctl-tester/**/*.yaml linguist-generated=true diff --git a/.github/workflows/fwci-test.yml b/.github/workflows/fwci-test.yml new file mode 100644 index 00000000..38486723 --- /dev/null +++ b/.github/workflows/fwci-test.yml @@ -0,0 +1,83 @@ +--- +name: fwci-test +# Gorelease is used only as build-tool, not handling actual releases. + +on: + pull_request: {} + merge_group: {} + push: + branches: ["main"] + tags: ["v*"] + +env: + APPLY_FIXES: none + APPLY_FIXES_EVENT: pull_request + APPLY_FIXES_MODE: commit + DISABLE_TELEMETRY: 1 +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + # Job to build binaries and linux packages with goreleaser, without making release + build-with-goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: stable + - name: Build packages with goreleaser (without actually making release) + run: | + go install github.com/goreleaser/goreleaser/v2@latest + goreleaser release --draft --snapshot --clean + - name: Upload artifacts + uses: actions/upload-artifact@v7 + with: + name: binaries + path: bin/ + retention-days: 14 days + + + # Run a firmware-ci test suite + client-agent-integration: + runs-on: ubuntu-latest + needs: + - build-with-goreleaser + strategy: + fail-fast: false + matrix: + configfile: + - ./contrib/dutagent-cfg-example.yaml + #- ./test/test-dutagent-cfg.yaml + steps: + - name: Checkout + uses: actions/checkout@v6 + - uses: actions/download-artifact@v8 + with: + name: binaries + path: bin/ + + - name: Get names of binaries + id: filenames + run: | + echo "dutctl_path=$(ls ./bin/dutctl_linux_amd64_*/dutctl)" >> "${GITHUB_OUTPUT}" + echo "dutctl_pkg_path=$(ls ./bin/dutctl_*_arm64.deb)" >> "${GITHUB_OUTPUT}" + - name: Debug + run: | + echo "dutctl_path: ${{ steps.filenames.outputs.dutctl_path }}" + echo "dutctl_pkg_path: ${{ steps.filenames.outputs.dutctl_pkg_path }}" + + - name: Upload to FirmwareCI + uses: docker://firmwareci/action:v5.1 + with: + TOKEN: "${{ secrets.FWCI_TOKEN }}" + WORKFLOW_ID: "${{ secrets.FWCI_WORKFLOW_ID }}" + COMMIT_HASH: ${{ github.event.pull_request.head.sha || github.sha }} + BINARIES: DUTCTL=${{ steps.filenames.outputs.dutctl_path }};REMOTE_PKG=${{ steps.filenames.outputs.dutctl_pkg_path }};CONFIG=${{ matrix.configfile }} + GITHUB_INSTALLATION_ID: 45795153 diff --git a/.gitignore b/.gitignore index e375cf18..4a9a70c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ .vscode/ -taskfile.yml +.task/ /dutctl /cmds/dutctl/dutctl /dutagent /cmds/dutagent/dutagent /dutserver /cmds/exp/dutserver/dutserver -/keys/ \ No newline at end of file +/keys/ +/bin/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..8d9c38ea --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,141 @@ +--- +# Gorelease is used only as build-tool, not handling actual releases. +version: 2 + +dist: "./bin" + +before: + hooks: + - go mod tidy + - go generate ./... + +builds: + - id: "dutctl" + main: ./cmds/dutctl + binary: "dutctl" + ldflags: + - -s -w -X main.builtBy=goreleaser + env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + - arm + - arm64 + goarm: + - "7" + goarm64: + - v8.0 + goamd64: + - v1 + mod_timestamp: "{{ .CommitTimestamp }}" + + - id: "dutagent" + main: ./cmds/dutagent + binary: "dutagent" + ldflags: + - -s -w -X main.builtBy=goreleaser + env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + - arm + - arm64 + goarm: + - "7" + goarm64: + - v8.0 + goamd64: + - v1 + mod_timestamp: "{{ .CommitTimestamp }}" + + - id: "dutserver" + main: ./cmds/exp/dutserver + binary: "dutserver" + ldflags: + - -s -w -X main.builtBy=goreleaser + env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + - arm + - arm64 + goarm: + - "7" + goarm64: + - v8.0 + goamd64: + - v1 + mod_timestamp: "{{ .CommitTimestamp }}" + +checksum: + name_template: "checksums.txt" + algorithm: blake2b + +archives: + - formats: ["tar.gz"] + # this name template makes the OS and Arch compatible with the results of `uname`. + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + # use zip for windows archives + format_overrides: + - goos: windows + formats: ["zip"] + +nfpms: + - id: packages + package_name: "dutctl" + file_name_template: >- + {{- trimsuffix .ConventionalFileName .ConventionalExtension -}} + {{- if and (eq .Arm "6") (eq .ConventionalExtension ".deb") }}6{{ end -}} + {{- if not (eq .Amd64 "v1")}}{{ .Amd64 }}{{ end -}} + {{- .ConventionalExtension -}} + ids: + - dutctl + - dutagent + provides: + - dutctl + - dutagent + vendor: "Blindspot Software" + homepage: "https://github.com/blindspotSoftware/dutctl" + maintainer: "Jens Topp " + description: | + DUT Control is an abstraction layer for remote hardware access. + license: "BSD 2-Clause" + formats: + - deb + - rpm + - archlinux + replaces: + - golang-github-fti + - golang-fti + - fti + bindir: /usr/bin + section: default + priority: extra + contents: + # systemd services + - src: ./.firmwareci/dutagent.service + dst: /usr/lib/systemd/system/dutagent.service + + rpm: + packager: "Jens Topp " + archlinux: + packager: "Jens Topp " + +source: + enabled: true + +release: + prerelease: auto + mode: append