mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-06 11:39:32 +00:00
Merge branch 'master' into tsl2561
This commit is contained in:
commit
e9a1e2a1b7
238
.github/workflows/pr_tests.yml
vendored
Normal file
238
.github/workflows/pr_tests.yml
vendored
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
# DISABLED: Changed from automatic PR triggers to manual only
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
reason:
|
||||||
|
description: "Reason for manual test run"
|
||||||
|
required: false
|
||||||
|
default: "Manual test execution"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: tests-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
native-tests:
|
||||||
|
name: "🧪 Native Tests"
|
||||||
|
if: github.repository == 'meshtastic/firmware'
|
||||||
|
uses: ./.github/workflows/test_native.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
|
||||||
|
test-summary:
|
||||||
|
name: "📊 Test Results"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [native-tests]
|
||||||
|
if: always()
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
checks: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Get release version string
|
||||||
|
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
|
||||||
|
- name: Download test artifacts
|
||||||
|
if: needs.native-tests.result != 'skipped'
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Parse test results and create detailed summary
|
||||||
|
id: test-results
|
||||||
|
run: |
|
||||||
|
echo "## 🧪 Test Results Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Check overall job status first
|
||||||
|
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||||
|
echo "✅ **Overall Status**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "${{ needs.native-tests.result }}" == "failure" ]]; then
|
||||||
|
echo "❌ **Overall Status**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [[ "${{ needs.native-tests.result }}" == "cancelled" ]]; then
|
||||||
|
echo "⏸️ **Overall Status**: CANCELLED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Tests were cancelled before completion." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "⚠️ **Overall Status**: SKIPPED" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Tests were skipped." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Parse detailed test results if available
|
||||||
|
if [ -f "testreport.xml" ]; then
|
||||||
|
echo "### 🔍 Individual Test Results" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
python3 << 'EOF'
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
tree = ET.parse('testreport.xml')
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
total_tests = 0
|
||||||
|
passed_tests = 0
|
||||||
|
failed_tests = 0
|
||||||
|
skipped_tests = 0
|
||||||
|
|
||||||
|
# Parse testsuite elements
|
||||||
|
for testsuite in root.findall('.//testsuite'):
|
||||||
|
suite_name = testsuite.get('name', 'Unknown')
|
||||||
|
suite_tests = int(testsuite.get('tests', '0'))
|
||||||
|
suite_failures = int(testsuite.get('failures', '0'))
|
||||||
|
suite_errors = int(testsuite.get('errors', '0'))
|
||||||
|
suite_skipped = int(testsuite.get('skipped', '0'))
|
||||||
|
|
||||||
|
total_tests += suite_tests
|
||||||
|
failed_tests += suite_failures + suite_errors
|
||||||
|
skipped_tests += suite_skipped
|
||||||
|
passed_tests += suite_tests - suite_failures - suite_errors - suite_skipped
|
||||||
|
|
||||||
|
if suite_tests > 0:
|
||||||
|
status = "✅" if (suite_failures + suite_errors) == 0 else "❌"
|
||||||
|
print(f"**{status} Test Suite: {suite_name}**")
|
||||||
|
print(f"- Total: {suite_tests}")
|
||||||
|
print(f"- Passed: ✅ {suite_tests - suite_failures - suite_errors - suite_skipped}")
|
||||||
|
print(f"- Failed: ❌ {suite_failures + suite_errors}")
|
||||||
|
if suite_skipped > 0:
|
||||||
|
print(f"- Skipped: ⏭️ {suite_skipped}")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Show individual test results for failed suites
|
||||||
|
if suite_failures + suite_errors > 0:
|
||||||
|
print("**Failed Tests:**")
|
||||||
|
for testcase in testsuite.findall('testcase'):
|
||||||
|
test_name = testcase.get('name', 'Unknown')
|
||||||
|
failure = testcase.find('failure')
|
||||||
|
error = testcase.find('error')
|
||||||
|
|
||||||
|
if failure is not None:
|
||||||
|
msg = failure.get('message', 'Unknown error')[:100]
|
||||||
|
print(f"- ❌ `{test_name}`: {msg}")
|
||||||
|
elif error is not None:
|
||||||
|
msg = error.get('message', 'Unknown error')[:100]
|
||||||
|
print(f"- ❌ `{test_name}`: ERROR - {msg}")
|
||||||
|
print("")
|
||||||
|
else:
|
||||||
|
# Show passed tests for successful suites
|
||||||
|
passed_count = 0
|
||||||
|
for testcase in testsuite.findall('testcase'):
|
||||||
|
if testcase.find('failure') is None and testcase.find('error') is None:
|
||||||
|
if passed_count < 5: # Limit to first 5 to avoid spam
|
||||||
|
test_name = testcase.get('name', 'Unknown')
|
||||||
|
print(f"- ✅ `{test_name}`: PASSED")
|
||||||
|
passed_count += 1
|
||||||
|
if passed_count > 5:
|
||||||
|
print(f"- ... and {passed_count - 5} more tests passed")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Summary statistics
|
||||||
|
print("### 📊 Test Statistics")
|
||||||
|
print(f"- **Total Tests**: {total_tests}")
|
||||||
|
print(f"- **Passed**: ✅ {passed_tests}")
|
||||||
|
print(f"- **Failed**: ❌ {failed_tests}")
|
||||||
|
if skipped_tests > 0:
|
||||||
|
print(f"- **Skipped**: ⏭️ {skipped_tests}")
|
||||||
|
|
||||||
|
if failed_tests > 0:
|
||||||
|
print(f"\n❌ **{failed_tests} tests failed out of {total_tests} total**")
|
||||||
|
else:
|
||||||
|
print(f"\n✅ **All {total_tests} tests passed!**")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error parsing test results: {e}")
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "⚠️ **No detailed test report available**" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Test artifacts may not have been generated properly." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "View detailed logs in the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
- name: Comment test results on PR
|
||||||
|
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Read the step summary to use as PR comment
|
||||||
|
let testSummary = "## 🧪 Test Results Summary\n\n";
|
||||||
|
|
||||||
|
if ("${{ needs.native-tests.result }}" === "success") {
|
||||||
|
testSummary += "✅ **All tests passed!**\n\n";
|
||||||
|
} else if ("${{ needs.native-tests.result }}" === "failure") {
|
||||||
|
testSummary += "❌ **Some tests failed.**\n\n";
|
||||||
|
} else {
|
||||||
|
testSummary += "⚠️ **Tests did not complete normally.**\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
testSummary += `View detailed results: [Actions Run](${context.payload.repository.html_url}/actions/runs/${context.runId})\n\n`;
|
||||||
|
testSummary += "---\n";
|
||||||
|
testSummary += "*This comment will be automatically updated when new commits are pushed.*";
|
||||||
|
|
||||||
|
// Find existing comment
|
||||||
|
const comments = await github.rest.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number
|
||||||
|
});
|
||||||
|
|
||||||
|
const botComment = comments.data.find(comment =>
|
||||||
|
comment.user.type === 'Bot' &&
|
||||||
|
comment.body.includes('🧪 Test Results Summary')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (botComment) {
|
||||||
|
// Update existing comment
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: botComment.id,
|
||||||
|
body: testSummary
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new comment
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body: testSummary
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Set overall status
|
||||||
|
run: |
|
||||||
|
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
|
||||||
|
echo "All tests passed! ✅"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Some tests failed! ❌"
|
||||||
|
exit 1
|
||||||
|
fi
|
@ -61,7 +61,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install \
|
|||||||
|
|
||||||
# Fetch compiled binary from the builder
|
# Fetch compiled binary from the builder
|
||||||
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
|
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
|
||||||
COPY --from=builder /tmp/web /usr/share/meshtasticd/
|
COPY --from=builder /tmp/web /usr/share/meshtasticd/web/
|
||||||
# Copy config templates
|
# Copy config templates
|
||||||
COPY ./bin/config.d /etc/meshtasticd/available.d
|
COPY ./bin/config.d /etc/meshtasticd/available.d
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
[portduino_base]
|
[portduino_base]
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
||||||
https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
|
https://github.com/meshtastic/platform-native/archive/37d986499ce24511952d7146db72d667c6bdaff7.zip
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
|
@ -60,7 +60,7 @@ monitor_speed = 115200
|
|||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0119501e9983bd894830b02f545c377ee08d66fe.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@ -118,7 +118,7 @@ lib_deps =
|
|||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||||
https://github.com/meshtastic/device-ui/archive/0cd108ff783539e41ef38258ba2784ab3b1bdc97.zip
|
https://github.com/meshtastic/device-ui/archive/8f5094b248c15ea2f9acf19cedfef6d2248fc1ff.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
|
@ -192,12 +192,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
|
|
||||||
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||||
{
|
{
|
||||||
if (!available()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// In case we send a FromRadio packet
|
|
||||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
|
||||||
|
|
||||||
// Respond to heartbeat by sending queue status
|
// Respond to heartbeat by sending queue status
|
||||||
if (heartbeatReceived) {
|
if (heartbeatReceived) {
|
||||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||||
@ -209,6 +203,12 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
return numbytes;
|
return numbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!available()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// In case we send a FromRadio packet
|
||||||
|
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||||
|
|
||||||
// Advance states as needed
|
// Advance states as needed
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_SEND_NOTHING:
|
case STATE_SEND_NOTHING:
|
||||||
|
@ -523,8 +523,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
|||||||
// is not in the local nodedb
|
// is not in the local nodedb
|
||||||
// First, only PKC encrypt packets we are originating
|
// First, only PKC encrypt packets we are originating
|
||||||
if (isFromUs(p) &&
|
if (isFromUs(p) &&
|
||||||
// Don't use PKC with simulator
|
#if ARCH_PORTDUINO
|
||||||
radioType != SIM_RADIO &&
|
// Sim radio via the cli flag skips PKC
|
||||||
|
!portduino_config.force_simradio &&
|
||||||
|
#endif
|
||||||
// Don't use PKC with Ham mode
|
// Don't use PKC with Ham mode
|
||||||
!owner.is_licensed &&
|
!owner.is_licensed &&
|
||||||
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested
|
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested
|
||||||
|
@ -279,6 +279,8 @@ struct PubSubConfig {
|
|||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
static constexpr uint16_t defaultPort = 1883;
|
static constexpr uint16_t defaultPort = 1883;
|
||||||
|
static constexpr uint16_t defaultPortTls = 8883;
|
||||||
|
|
||||||
uint16_t serverPort = defaultPort;
|
uint16_t serverPort = defaultPort;
|
||||||
String serverAddr = default_mqtt_address;
|
String serverAddr = default_mqtt_address;
|
||||||
const char *mqttUsername = default_mqtt_username;
|
const char *mqttUsername = default_mqtt_username;
|
||||||
@ -641,7 +643,7 @@ bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTC
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool defaultServer = isDefaultServer(parsed.serverAddr);
|
const bool defaultServer = isDefaultServer(parsed.serverAddr);
|
||||||
if (defaultServer && parsed.serverPort != PubSubConfig::defaultPort) {
|
if (defaultServer && !IS_ONE_OF(parsed.serverPort, PubSubConfig::defaultPort, PubSubConfig::defaultPortTls)) {
|
||||||
const char *warning = "Invalid MQTT config: default server address must not have a port specified";
|
const char *warning = "Invalid MQTT config: default server address must not have a port specified";
|
||||||
LOG_ERROR(warning);
|
LOG_ERROR(warning);
|
||||||
#if !IS_RUNNING_TESTS
|
#if !IS_RUNNING_TESTS
|
||||||
|
@ -34,7 +34,6 @@ std::ofstream traceFile;
|
|||||||
Ch341Hal *ch341Hal = nullptr;
|
Ch341Hal *ch341Hal = nullptr;
|
||||||
char *configPath = nullptr;
|
char *configPath = nullptr;
|
||||||
char *optionMac = nullptr;
|
char *optionMac = nullptr;
|
||||||
bool forceSimulated = false;
|
|
||||||
bool verboseEnabled = false;
|
bool verboseEnabled = false;
|
||||||
|
|
||||||
const char *argp_program_version = optstr(APP_VERSION);
|
const char *argp_program_version = optstr(APP_VERSION);
|
||||||
@ -67,7 +66,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
configPath = arg;
|
configPath = arg;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
forceSimulated = true;
|
portduino_config.force_simradio = true;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
optionMac = arg;
|
optionMac = arg;
|
||||||
@ -190,7 +189,7 @@ void portduinoSetup()
|
|||||||
|
|
||||||
YAML::Node yamlConfig;
|
YAML::Node yamlConfig;
|
||||||
|
|
||||||
if (forceSimulated == true) {
|
if (portduino_config.force_simradio == true) {
|
||||||
settingsMap[use_simradio] = true;
|
settingsMap[use_simradio] = true;
|
||||||
} else if (configPath != nullptr) {
|
} else if (configPath != nullptr) {
|
||||||
if (loadConfig(configPath)) {
|
if (loadConfig(configPath)) {
|
||||||
|
@ -134,4 +134,5 @@ extern struct portduino_config_struct {
|
|||||||
bool has_rfswitch_table = false;
|
bool has_rfswitch_table = false;
|
||||||
uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
|
uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
|
||||||
Module::RfSwitchMode_t rfswitch_table[8];
|
Module::RfSwitchMode_t rfswitch_table[8];
|
||||||
|
bool force_simradio = false;
|
||||||
} portduino_config;
|
} portduino_config;
|
Loading…
Reference in New Issue
Block a user