From 5130b5af67ea9c94cf3ab5c4d47dd2699af68639 Mon Sep 17 00:00:00 2001 From: Adams Date: Tue, 7 Apr 2026 12:17:00 -0500 Subject: [PATCH] Part cFS/workflows#177, Update Existing Workflows with Internal Logic --- .gitattributes | 5 + .../app-static-analysis-reusable.yml | 34 ++++ ...-deploy-doc.yml => build-doc-reusable.yml} | 86 +++----- .github/workflows/build-run-app-reusable.yml | 186 ++++++++++++++++++ .github/workflows/build-run-app.yml | 119 ----------- .github/workflows/format-check.yml | 43 ++-- ...ge.yml => unit-test-coverage-reusable.yml} | 66 +++++-- actions/cppcheck/action.yml | 82 ++++++++ actions/healthcheck-logs/action.yml | 59 ++++++ actions/setup-app/action.yml | 87 ++++++++ actions/start-cfs-container/action.yml | 41 ++++ actions/stop-cfs-container/action.yml | 24 +++ 12 files changed, 614 insertions(+), 218 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/app-static-analysis-reusable.yml rename .github/workflows/{build-deploy-doc.yml => build-doc-reusable.yml} (63%) create mode 100644 .github/workflows/build-run-app-reusable.yml delete mode 100644 .github/workflows/build-run-app.yml rename .github/workflows/{unit-test-coverage.yml => unit-test-coverage-reusable.yml} (68%) create mode 100644 actions/cppcheck/action.yml create mode 100644 actions/healthcheck-logs/action.yml create mode 100644 actions/setup-app/action.yml create mode 100644 actions/start-cfs-container/action.yml create mode 100644 actions/stop-cfs-container/action.yml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..f5b5e4f58 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +simple_defs export-ignore +simple.mk export-ignore +distbuild.mk export-ignore +apps/sbn_f_remap export-ignore +apps/sbn_udp export-ignore \ No newline at end of file diff --git a/.github/workflows/app-static-analysis-reusable.yml b/.github/workflows/app-static-analysis-reusable.yml new file mode 100644 index 000000000..1ff37602e --- /dev/null +++ b/.github/workflows/app-static-analysis-reusable.yml @@ -0,0 +1,34 @@ +name: Static Analysis Reusable Workflow + +on: + workflow_call: + inputs: + # Optional inputs + source-dir: + description: Directory containing source files + type: string + default: 'fsw' + +# Force bash to apply pipefail option so pipeline failures aren't masked +defaults: + run: + shell: bash + +jobs: + static-analysis: + name: Run Static Analysis + runs-on: ubuntu-22.04 + container: ghcr.io/core-flight-system/cfsbuildenv-linux:latest + timeout-minutes: 15 + + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + path: source + + - name: Run cppcheck + uses: nasa/cFS/actions/cppcheck@dev + id: run-cppcheck + with: + source-dir: source/${{ inputs.source-dir }} \ No newline at end of file diff --git a/.github/workflows/build-deploy-doc.yml b/.github/workflows/build-doc-reusable.yml similarity index 63% rename from .github/workflows/build-deploy-doc.yml rename to .github/workflows/build-doc-reusable.yml index 7f94e710f..9bc5b32fe 100644 --- a/.github/workflows/build-deploy-doc.yml +++ b/.github/workflows/build-doc-reusable.yml @@ -1,4 +1,4 @@ -name: Build Document +name: Build Document Reusable Workflow on: workflow_call: @@ -23,17 +23,20 @@ on: description: Build the PDF type: boolean required: false - default: true - deploy: - description: Deploy archived PDF to gh-pages - type: boolean - required: false - default: true + default: false needs_osal_api: description: Whether this depends on the osal public api (compatibility bridge) type: boolean required: false default: true + defs: + description: '*_defs directory' + type: string + required: false + default: 'sample_defs' + +env: + WORK_PATH: ${{ github.workspace }} # Force bash to apply pipefail option so pipeline failures aren't masked defaults: @@ -58,9 +61,10 @@ jobs: build-doc: needs: checks-for-duplicates - if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') }} + if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') || contains(github.ref, 'dev') }} name: Build Documentation runs-on: ubuntu-22.04 + container: ghcr.io/core-flight-system/cfsbuildenv-doxygen:latest strategy: fail-fast: false @@ -68,52 +72,17 @@ jobs: target: ${{ fromJson(inputs.target) }} steps: - - name: Reject non-compatible deployment settings - if: ${{ inputs.deploy == true && inputs.cache-key != '' }} - run: | - echo "Deployment when using cache not supported due to password fail issue" - exit -1 - - name: Get cache if supplied id: cache-src-bld if: ${{ inputs.cache-key != '' }} uses: actions/cache@v4 with: - path: /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/* + path: ${{ env.WORK_PATH }}/ key: ${{ inputs.cache-key }} - - name: Checkout Bundle Main - if: ${{ inputs.app-name != '' }} - uses: actions/checkout@v4 - with: - submodules: true - repository: nasa/cFS - - - name: Checkout Repo - if: ${{ inputs.app-name != '' }} - uses: actions/checkout@v4 - with: - path: apps/${{ inputs.app-name }} - - - name: Copy Files - run: | - cp ./cfe/cmake/Makefile.sample Makefile - cp -r ./cfe/cmake/sample_defs sample_defs - - - name: Add Repo To Build - if: ${{ inputs.app-name != '' }} - run: echo 'set(MISSION_GLOBAL_APPLIST ${{ inputs.app-name }})' >> sample_defs/targets.cmake - - - name: Make Prep - run: make prep - - - name: Install Doxygen Dependencies - run: sudo apt-get update && sudo apt-get install doxygen graphviz -y - - - name: Install PDF Generation Dependencies - if: ${{ inputs.buildpdf == true }} - run: | - sudo apt-get install texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra + - name: Set up app source + if: steps.cache-src-bld.outputs.cache-hit != 'true' + uses: nasa/cFS/actions/setup-app@dev - name: Generate OSAL header list if: ${{ inputs.needs_osal_api == true }} @@ -137,21 +106,25 @@ jobs: run: | if [[ -s ${{ matrix.target }}_stderr.txt ]]; then cat ${{ matrix.target }}_stderr.txt - exit -1 + exit 1 fi - name: Check For Document Warnings run: | if [[ -s ${{ matrix.target }}-warnings.log ]]; then cat ${{ matrix.target }}-warnings.log - exit -1 + exit 1 fi - name: Generate PDF if: ${{ inputs.buildpdf == true }} run: | - make -C ./build/docs/${{ matrix.target }}/latex - mkdir deploy + if ! make LATEX_CMD="pdflatex -file-line-error -halt-on-error" -C ./build/docs/${{ matrix.target }}/latex > pdflatex.log; then + echo "Errors reported, tail of latex output follows" + tail -100 pdflatex.log + exit -1 + fi + mkdir -p deploy mv ./build/docs/${{ matrix.target }}/latex/refman.pdf ./deploy/${{ matrix.target }}.pdf # Could add pandoc and convert to github markdown # pandoc ${{ matrix.target }}.pdf -t gfm @@ -161,13 +134,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.target }}_pdf - path: ./deploy/${{ matrix.target }}.pdf - - - name: Deploy to GitHub - if: ${{ inputs.deploy == true }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: gh-pages - folder: deploy - single-commit: true + path: ./deploy/${{ matrix.target }}.pdf \ No newline at end of file diff --git a/.github/workflows/build-run-app-reusable.yml b/.github/workflows/build-run-app-reusable.yml new file mode 100644 index 000000000..260bd1c4d --- /dev/null +++ b/.github/workflows/build-run-app-reusable.yml @@ -0,0 +1,186 @@ +name: Build And Run Reusable Workflow + +on: + workflow_call: + inputs: + # Optional inputs + app-name: + description: Application name, if different from repo name + type: string + required: false + default: ${{ github.event.repository.name }} + startup-string: + description: Startup string to confirm, default will use " Initialized." + type: string + required: false + default: '' + # Currently CFS apps have at most one dependency, so this only handles one for now + dependency: + description: Additional module/library that this app depends on + type: string + required: false + default: '' + app-entrypoint-suffix: + description: Symbol suffix to use as app entry point + type: string + required: false + default: 'AppMain' + +# Force bash to apply pipefail option so pipeline failures aren't masked +defaults: + run: + shell: bash + +jobs: + # Checks for duplicate actions. Skips push actions if there is a matching or + # duplicate pull-request action. + checks-for-duplicates: + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@master + with: + concurrent_skipping: 'same_content' + skip_after_successful_duplicate: 'true' + do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' + + build-app: + needs: checks-for-duplicates + if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') || contains(github.ref, 'dev') }} + name: Build CFE with app + runs-on: ubuntu-22.04 + container: ghcr.io/core-flight-system/cfsbuildenv-linux:latest + + steps: + # Note this also sets the APP_UPPER and APP_LOWER environment variables + - name: Set up app source + uses: nasa/cFS/actions/setup-app@dev + with: + app-name: ${{ inputs.app-name }} + dependency: ${{ inputs.dependency }} + + - name: Set up start string for verification + run: | + if [[ "${{ inputs.startup-string }}" == '' ]]; then + echo "START_STRING=$APP_UPPER Initialized." >> $GITHUB_ENV + else + echo "START_STRING=${{ inputs.startup-string }}" >> $GITHUB_ENV + fi + + - name: Make install + run: make -C build mission-install + + - name: Generate Startup Link + run: ln -s core-cpu1 ./build/exe/cpu1/container-start + + - name: Replace startup script + run: | + truncate -s 0 ./build/exe/cpu1/cf/cfe_es_startup.scr + if [ "x$APP_DEP_LOWER" != "x" ] + then + echo "CFE_LIB, $APP_DEP_LOWER, ${APP_DEP_UPPER}_Init, $APP_DEP_UPPER, 0, 0, 0x0, 0;" >> ./build/exe/cpu1/cf/cfe_es_startup.scr + fi + echo "CFE_APP, $APP_LOWER, ${APP_UPPER}_${{ inputs.app-entrypoint-suffix }}, $APP_UPPER, 80, 16384, 0x0, 0;" >> ./build/exe/cpu1/cf/cfe_es_startup.scr + cat ./build/exe/cpu1/cf/cfe_es_startup.scr + + - name: Archive binaries + run: | + cd $GITHUB_WORKSPACE/build/exe + find -maxdepth 1 -mindepth 1 -type d | while read dir + do + inst=$(basename ${dir}) + tar Jcv -f $GITHUB_WORKSPACE/${inst}-bin.tar.xz -C ${inst} . + done + + - name: Upload all artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.app-name }}-bin + path: ./*.tar.xz + + run-app: + needs: build-app + name: Run CFE with app, check for startup messages + runs-on: ubuntu-22.04 + + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.app-name }}-bin + path: ${{ inputs.app-name }}-bin + + - name: List Files 1 + run: ls -lR . + + - name: Unpack artifacts + run: | + for i in cpu1 host + do + mkdir -p "$i" + tar Jxv -C "$i" -f "$GITHUB_WORKSPACE/${{ inputs.app-name }}-bin/$i-bin.tar.xz" + done + + - name: List Files 2 + run: | + pwd + ls -lR . + + - name: Start CPU1 container + id: start-cpu1 + uses: nasa/cFS/actions/start-cfs-container@dev + with: + binary-dir: ${{ github.workspace }}/cpu1 + + - name: Check CPU1 container + id: check-cpu1 + uses: nasa/cFS/actions/healthcheck-logs@dev + with: + container-id: ${{ steps.start-cpu1.outputs.container-id }} + healthcheck-regex: 'CFE_ES_Main entering OPERATIONAL state$' + + - name: Shut down CFE + if: ${{ steps.check-cpu1.outputs.ip-addr != '' }} + working-directory: ./host + run: | + ./cmd_send -v --host=${{ steps.check-cpu1.outputs.ip-addr }} --endian=LE --pktid=0x1806 --cmdcode=2 --half=0x0002 + sleep 2 + + - name: Capture Logs + if: ${{ always() && steps.start-cpu1.outputs.container-id != '' }} + run: docker logs ${{ steps.start-cpu1.outputs.container-id }} > cFS_startup_cpu1.txt + + - name: Stop CPU1 Container + uses: nasa/cFS/actions/stop-cfs-container@dev + with: + container-id: ${{ steps.start-cpu1.outputs.container-id }} + + - name: Archive results + if: success() || failure() + uses: actions/upload-artifact@v4 + with: + name: cFS_startup_log + path: cFS_startup_cpu1.txt + + - name: Confirm startup string + run: | + if [[ -z $(grep "$START_STRING" cFS_startup_cpu1.txt) ]]; then + echo "Startup verification string not found in log: $START_STRING" + echo "" + echo "Possible related event messages:" + grep "/$APP_UPPER " cFS_startup_cpu1.txt + exit -1 + fi + + - name: Check for cFS Warnings + if: success() || failure() + run: | + if [[ -n $(grep -i "warn\|err\|fail" cFS_startup_cpu1.txt) ]]; then + echo "cFS startup warn|err|fail:" + echo "" + grep -i 'warn\|err\|fail' cFS_startup_cpu1.txt + exit -1 + fi \ No newline at end of file diff --git a/.github/workflows/build-run-app.yml b/.github/workflows/build-run-app.yml deleted file mode 100644 index e6249c5ad..000000000 --- a/.github/workflows/build-run-app.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: Build And Run - -on: - workflow_call: - inputs: - # Optional inputs - app-name: - description: Application name, if different from repo name - type: string - required: false - default: ${{ github.event.repository.name }} - startup-string: - description: Startup string to confirm, default will use " Initialized." - type: string - required: false - default: '' - -# Force bash to apply pipefail option so pipeline failures aren't masked -defaults: - run: - shell: bash - -jobs: - # Checks for duplicate actions. Skips push actions if there is a matching or - # duplicate pull-request action. - checks-for-duplicates: - runs-on: ubuntu-latest - # Map a step output to a job output - outputs: - should_skip: ${{ steps.skip_check.outputs.should_skip }} - steps: - - id: skip_check - uses: fkirc/skip-duplicate-actions@master - with: - concurrent_skipping: 'same_content' - skip_after_successful_duplicate: 'true' - do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' - - build-and-run: - needs: checks-for-duplicates - if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') }} - name: Build and run app, confirm startup message - runs-on: ubuntu-22.04 - - steps: - - name: Set up environment variables - # Apps typically use lowercase targets and uppercase names, this logic is fragile but works - run: | - echo "APP_UPPER=$(echo ${{ inputs.app-name }} | sed 's/[a-z]/\U&/g')" >> $GITHUB_ENV - echo "APP_LOWER=$(echo ${{ inputs.app-name }} | sed 's/[A-Z]/\L&/g')" >> $GITHUB_ENV - - - name: Set up start string for verification - run: | - if [[ "${{ inputs.startup-string }}" == '' ]]; then - echo "START_STRING=$APP_UPPER Initialized." >> $GITHUB_ENV - else - echo "START_STRING=${{ inputs.startup-string }}" >> $GITHUB_ENV - fi - - - name: Checkout Bundle Main - uses: actions/checkout@v4 - with: - submodules: true - repository: nasa/cFS - - - name: Checkout Repo - uses: actions/checkout@v4 - with: - path: apps/${{ env.APP_LOWER }} - - - name: Copy Files - run: | - cp ./cfe/cmake/Makefile.sample Makefile - cp -r ./cfe/cmake/sample_defs sample_defs - - - name: Add To Build - run: | - sed -i "/list(APPEND MISSION_GLOBAL_APPLIST/a list(APPEND MISSION_GLOBAL_APPLIST $APP_LOWER)" sample_defs/targets.cmake - - - name: Add To Startup - run: | - sed -i "1i CFE_APP, $APP_LOWER, ${APP_UPPER}_AppMain, $APP_UPPER, 80, 16384, 0x0, 0;" sample_defs/cpu1_cfe_es_startup.scr - cat sample_defs/cpu1_cfe_es_startup.scr - - - name: Make install - run: make SIMULATION=native BUILDTYPE=release OMIT_DEPRECATED=true install - - - name: Run cFS - working-directory: ./build/exe/cpu1 - run: | - ./core-cpu1 > ../../../cFS_startup_cpu1.txt & - sleep 30 - ../host/cmdUtil --endian=LE --pktid=0x1806 --cmdcode=2 --half=0x0002 - - - name: Archive results - uses: actions/upload-artifact@v4 - with: - name: cFS_startup_log - path: cFS_startup_cpu1.txt - - - name: Confirm startup string - run: | - if [[ -z $(grep "$START_STRING" cFS_startup_cpu1.txt) ]]; then - echo "Startup verification string not found in log: $START_STRING" - echo "" - echo "Possible related event messages:" - grep "/$APP_UPPER " cFS_startup_cpu1.txt - exit -1 - fi - - - name: Check for cFS Warnings - if: success() || failure() - run: | - if [[ -n $(grep -i "warn\|err\|fail" cFS_startup_cpu1.txt) ]]; then - echo "cFS startup warn|err|fail:" - echo "" - grep -i 'warn\|err\|fail' cFS_startup_cpu1.txt - exit -1 - fi diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index f0bf34c67..6ab99dbe1 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -1,12 +1,13 @@ name: Format Check -# Run on all push and pull requests on: + workflow_call: pull_request: types: - opened - reopened - synchronize + workflow_dispatch: # Force bash to apply pipefail option so pipeline failures aren't masked defaults: @@ -35,29 +36,30 @@ jobs: if: ${{ needs.check-for-duplicates.outputs.should_skip != 'true' }} runs-on: ubuntu-22.04 timeout-minutes: 15 + container: ghcr.io/core-flight-system/cfsbuildenv-linux:latest steps: - - name: Install format checker - run: | - sudo apt-get update && sudo apt-get install clang-format - - - name: Checkout bundle - uses: actions/checkout@v4 - with: - repository: nasa/cFS - - name: Checkout uses: actions/checkout@v4 with: path: repo - - name: Generate format differences - run: | - cd repo - find . -name "*.[ch]" -exec clang-format -i -style=file {} + - git diff > $GITHUB_WORKSPACE/style_differences.txt + - name: Get style from bundle + run: curl -fsL ${{ github.server_url }}/nasa/cFS/raw/refs/heads/dev/.clang-format > $GITHUB_WORKSPACE/clang-format.yml - - name: Archive Static Analysis Artifacts + - name: Check configuration + run: clang-format-19 -style=file:$GITHUB_WORKSPACE/clang-format.yml --dump-config + + - name: Execute clang-format + run: find ./repo -type f -name "*.[ch]" -print0 | xargs -0 clang-format-19 -style=file:$GITHUB_WORKSPACE/clang-format.yml -i + + - name: Check for differences + run: (cd repo && git diff --exit-code HEAD) | tee $GITHUB_WORKSPACE/style_differences.txt + + # If there are no diffs, this will be a 0 byte file and no need to archive it + # Otherwise if there was a failure the file should be non-empty + - name: Archive difference artifacts + if: failure() uses: actions/upload-artifact@v4 with: name: style_differences @@ -65,9 +67,8 @@ jobs: check-commit-message: name: Check Commit Message - needs: check-for-duplicates # Only run for pull-requests. - if: ${{ github.event_name == 'pull_request' }} + if: ${{ github.event_name == 'pull_request' && !startsWith(github.head_ref, 'ic-') }} runs-on: ubuntu-22.04 timeout-minutes: 15 steps: @@ -76,9 +77,9 @@ jobs: uses: gsactions/commit-message-checker@v2 if: always() with: - pattern: '^((Fix|HotFix|Part)\s\#[0-9]+,\s[a-zA-Z0-9]+|Merge\spull\srequest\s\#[0-9]+\s[a-zA-Z0-9]+|IC:\s[a-zA-Z0-9]+)' - error: 'You need at least one "Fix|HotFix|Part #, " line in the commit message.' + pattern: ^((Fix|HotFix|Part|Issue|Ticket)\s([a-zA-Z0-9_-]+/)?[a-zA-Z0-9_-]*\#[0-9]+[,:]\s.+|Merge\spull\srequest\s\#[0-9]+\s.+|IC:\s.+) + error: 'You need at least one "Fix|HotFix|Part|Issue|Ticket #<,|:> " line in the commit message.' excludeDescription: 'true' excludeTitle: 'true' checkAllCommitMessages: 'true' - accessToken: ${{ secrets.GITHUB_TOKEN }} + accessToken: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/unit-test-coverage.yml b/.github/workflows/unit-test-coverage-reusable.yml similarity index 68% rename from .github/workflows/unit-test-coverage.yml rename to .github/workflows/unit-test-coverage-reusable.yml index c3e0753fe..2328ebbdb 100644 --- a/.github/workflows/unit-test-coverage.yml +++ b/.github/workflows/unit-test-coverage-reusable.yml @@ -1,4 +1,4 @@ -name: Unit Test and Coverage +name: Unit Test and Coverage Reusable Workflow on: workflow_call: @@ -19,6 +19,12 @@ on: type: number required: false default: 0 + # Currently CFS apps have at most one dependency, so this only handles one for now + dependency: + description: Additional module/library that this app depends on + type: string + required: false + default: '' # Force bash to apply pipefail option so pipeline failures aren't masked defaults: @@ -43,45 +49,71 @@ jobs: unit-test-coverage: needs: checks-for-duplicates - if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') }} + if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') || contains(github.ref, 'dev') }} name: Build, run unit tests and enforce coverage runs-on: ubuntu-22.04 + container: ghcr.io/core-flight-system/cfsbuildenv-linux:latest steps: - - name: Install coverage tools - run: sudo apt-get install lcov -y - - name: Set up environment variables # Apps typically use lowercase targets and uppercase names, this logic is fragile but works run: | echo "APP_UPPER=$(echo ${{ inputs.app-name }} | sed 's/[a-z]/\U&/g')" >> $GITHUB_ENV echo "APP_LOWER=$(echo ${{ inputs.app-name }} | sed 's/[A-Z]/\L&/g')" >> $GITHUB_ENV + APP_DEP="${{ inputs.dependency }}" + echo "APP_DEP_UPPER=$(echo ${APP_DEP##*/} | sed 's/[a-z]/\U&/g')" >> $GITHUB_ENV + echo "APP_DEP_LOWER=$(echo ${APP_DEP##*/} | sed 's/[A-Z]/\L&/g')" >> $GITHUB_ENV - - name: Checkout Bundle Main + - name: Checkout CFE uses: actions/checkout@v4 with: - submodules: true - repository: nasa/cFS + repository: nasa/cFE + path: cfe + ref: dev + + - name: Set up Dependencies + uses: ./cfe/.github/actions/setup-cfe + with: + source-dir: . + preferred-ref: ${{ github.head_ref }} + org: nasa - name: Checkout Repo uses: actions/checkout@v4 with: path: apps/${{ env.APP_LOWER }} - - name: Copy Files - run: | - cp ./cfe/cmake/Makefile.sample Makefile - cp -r ./cfe/cmake/sample_defs sample_defs + - name: Checkout Dependency + if: ${{ inputs.dependency != '' }} + uses: actions/checkout@v4 + with: + repository: ${{ inputs.dependency }} + path: apps/${{ inputs.dependency }} + ref: dev - - name: Add Repo To Build + - name: Set up basic targets.cmake config + run: | + cat > ./sample_defs/targets.cmake << EOF + SET(MISSION_NAME GithubActions) + SET(SPACECRAFT_ID 0x42) + SET(MISSION_CPUNAMES cpu1) + SET(cpu1_PROCESSORID 1) + SET(MISSION_GLOBAL_APPLIST $APP_LOWER) + EOF + + - name: Add Dependencies to targets run: | - sed -i "/list(APPEND MISSION_GLOBAL_APPLIST/a list(APPEND MISSION_GLOBAL_APPLIST $APP_LOWER)" sample_defs/targets.cmake + if [ "x$APP_DEP_LOWER" != "x" ] + then + echo "LIST(APPEND MISSION_GLOBAL_APPLIST ${APP_DEP_LOWER})" >> ./sample_defs/targets.cmake + fi + cat ./sample_defs/targets.cmake - name: Make Prep - run: make SIMULATION=native ENABLE_UNIT_TESTS=true OMIT_DEPRECATED=true prep + run: make SIMULATION=native ENABLE_UNIT_TESTS=true OMIT_DEPRECATED=false prep - name: Build app build dependencies - run: make -C build/tools/elf2cfetbl + run: make -C build mission-prebuild - name: Build app target run: | @@ -122,4 +154,4 @@ jobs: path: | test_results.txt lcov_out.txt - lcov + lcov \ No newline at end of file diff --git a/actions/cppcheck/action.yml b/actions/cppcheck/action.yml new file mode 100644 index 000000000..953537f54 --- /dev/null +++ b/actions/cppcheck/action.yml @@ -0,0 +1,82 @@ +name: Static Analysis +description: 'Performs static analysis of source code using cppcheck' + +inputs: + strict-dir-list: + description: 'Directory List' + type: string + default: '' + compile-commands-json: + description: 'Reference to compile command JSON file' + type: string + default: '' + source-dir: + description: 'List of source directories' + type: string + default: '' + cppcheck-xslt-path: + description: 'Path to XSLT file for translating cppcheck XML output' + type: string + default: 'nasa/cFS/raw/refs/heads/main/.github/scripts' + module-path: + description: 'Path to subdirectory containing subject source files' + type: string + default: '/' + +runs: + using: 'composite' + steps: + - name: Fetch conversion XSLT + shell: bash + run: | + wget -O cppcheck-xml2text.xslt ${{ github.server_url }}/${{ inputs.cppcheck-xslt-path }}/cppcheck-xml2text.xslt + wget -O cppcheck-merge.xslt ${{ github.server_url }}/${{ inputs.cppcheck-xslt-path }}/cppcheck-merge.xslt + + # If source dirs are specified, just pass them through. This will examine all .c files in the repo, + # but it will not see the macro definitions, and thus may not correctly interpret macro usage. + - name: Source Directory Setup + if: ${{ inputs.source-dir != '' }} + shell: bash + run: | + echo CPPCHECK_OPTS=${{ inputs.source-dir }} >> $GITHUB_ENV + + # Get the list of files by setting up a build with CMAKE_EXPORT_COMPILE_COMMANDS=ON and + # referencing the compile_commands.json file produced by the tool. This will capture the + # correct include paths and compile definitions based on how the source is actually compiled. + - name: Compile Commands Setup + if: ${{ inputs.compile-commands-json != '' }} + shell: bash + run: | + MODULE_PATH=$(realpath $GITHUB_WORKSPACE/${{ inputs.module-path }}) + echo filtering on $MODULE_PATH + jq '[.[] | if (.file | startswith("'$MODULE_PATH'")) then . else empty end]' ${{ inputs.compile-commands-json }} > local-cppcheck-compile-commands.json + echo CPPCHECK_OPTS=--project=local-cppcheck-compile-commands.json >> $GITHUB_ENV + + - name: Run general cppcheck + shell: bash + run: | + echo Command options: $CPPCHECK_OPTS + cppcheck --force --inline-suppr --xml $CPPCHECK_OPTS 2> cppcheck_err.xml + + # Run strict static analysis for selected portions of source code + - name: Run Strict cppcheck + if: ${{ inputs.strict-dir-list !='' }} + shell: bash + run: | + mv cppcheck_err.xml general_cppcheck_err.xml + cppcheck --force --inline-suppr --std=c99 --language=c --enable=warning,performance,portability,style --suppress=variableScope --inconclusive --xml ${{ inputs.strict-dir-list }} 2> strict_cppcheck_err.xml + xsltproc --stringparam merge_file strict_cppcheck_err.xml cppcheck-merge.xslt general_cppcheck_err.xml > cppcheck_err.xml + + - name: Convert cppcheck results to Markdown + shell: bash + run: xsltproc cppcheck-xml2text.xslt cppcheck_err.xml | tee $GITHUB_STEP_SUMMARY cppcheck_err.txt + + - name: Archive static analysis artifacts + uses: actions/upload-artifact@v4 + with: + name: cppcheck-errors + path: ./*cppcheck_err.* + + - name: Check for reported errors + shell: bash + run: tail -n 1 cppcheck_err.txt | grep -q '^\*\*0 error(s) reported\*\*$' \ No newline at end of file diff --git a/actions/healthcheck-logs/action.yml b/actions/healthcheck-logs/action.yml new file mode 100644 index 000000000..154ba2519 --- /dev/null +++ b/actions/healthcheck-logs/action.yml @@ -0,0 +1,59 @@ +name: Check QEMU cFS Virtual Machine +description: 'Checks boot completion and obtains IP address of virtual machine' + +inputs: + container-id: + description: "Docker container ID" + required: true + healthcheck-regex: + description: 'String to grep for to identify successful boot' + required: true + ssh-keyscan: + description: 'Whether to execute ssh-keyscan, enable if VM has an SSH server' + required: false + default: 'false' + +outputs: + ip-addr: + description: "Docker container IP address" + value: ${{ steps.query-ip-addr.outputs.addr }} + +runs: + using: 'composite' + steps: + - name: Check for boot completion + shell: bash + run: | + count=0 + docker logs "${{ inputs.container-id }}" > ${{ inputs.container-id }}_logs.txt + while [ $count -lt 30 ] && ! grep -q '${{ inputs.healthcheck-regex }}' ${{ inputs.container-id }}_logs.txt + do + sleep 2 + count=$(($count+1)) + docker logs "${{ inputs.container-id }}" > ${{ inputs.container-id }}_logs.txt + done + echo "Boot log after ${count} iterations:" + cat ${{ inputs.container-id }}_logs.txt + grep -q '${{ inputs.healthcheck-regex }}' ${{ inputs.container-id }}_logs.txt + + - name: Get IP address of container + shell: bash + id: query-ip-addr + run: | + IP_ADDR=$(docker inspect "${{ inputs.container-id }}" \ + --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}') + echo "addr=$IP_ADDR" >> $GITHUB_OUTPUT + echo "Container IP: $IP_ADDR" + + - name: Get SSH key + if: inputs.ssh-keyscan == 'true' + shell: bash + run: | + mkdir -p ~/.ssh + chmod 0700 ~/.ssh + touch ~/.ssh/known_hosts + chmod 0600 ~/.ssh/known_hosts + IP_ADDR=$(docker inspect "${{ inputs.container-id }}" \ + --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}') + ssh-keyscan -p 2222 "$IP_ADDR" | tee -a ~/.ssh/known_hosts + cat ~/.ssh/known_hosts \ No newline at end of file diff --git a/actions/setup-app/action.yml b/actions/setup-app/action.yml new file mode 100644 index 000000000..609bffe8f --- /dev/null +++ b/actions/setup-app/action.yml @@ -0,0 +1,87 @@ +name: Set up app source Workflow +description: 'Sets up application source' + +inputs: + app-name: + description: Application name + type: string + required: false + default: ${{ github.event.repository.name }} + dependency: + description: Additional module/library that this app depends on + type: string + required: false + default: '' + cmake-options: + description: Options to pass to the cmake command + type: string + required: false + default: '-DENABLE_UNIT_TESTS=TRUE -DSIMULATION=native -DMISSIONCONFIG=sample -DCMAKE_BUILD_TYPE=debug -DCMAKE_INSTALL_PREFIX=/exe' + +runs: + using: 'composite' + steps: + - name: Set up environment variables + # Apps typically use lowercase targets and uppercase names, this logic is fragile but works + shell: bash + run: | + echo "APP_UPPER=$(echo ${{ inputs.app-name }} | sed 's/[a-z]/\U&/g')" >> $GITHUB_ENV + echo "APP_LOWER=$(echo ${{ inputs.app-name }} | sed 's/[A-Z]/\L&/g')" >> $GITHUB_ENV + APP_DEP="${{ inputs.dependency }}" + echo "APP_DEP_UPPER=$(echo ${APP_DEP##*/} | sed 's/[a-z]/\U&/g')" >> $GITHUB_ENV + echo "APP_DEP_LOWER=$(echo ${APP_DEP##*/} | sed 's/[A-Z]/\L&/g')" >> $GITHUB_ENV + + - name: Checkout cFE + uses: actions/checkout@v4 + with: + repository: nasa/cFE + path: ./cfe + ref: dev + + - name: Set up Dependencies + uses: ./cfe/.github/actions/setup-cfe + with: + source-dir: . + preferred-ref: ${{ github.head_ref }} + org: 'nasa' + + - name: Checkout Dependency + if: ${{ inputs.dependency != '' }} + uses: actions/checkout@v4 + with: + repository: ${{ inputs.dependency }} + path: ./apps/${{ inputs.dependency }} + ref: dev + + - name: Checkout App Repo + if: inputs.app-name != 'cFS' && inputs.app-name != 'cFE' + uses: actions/checkout@v4 + with: + path: ./apps/${{ env.APP_LOWER }} + + - name: Set up basic targets.cmake config + shell: bash + run: | + cat > ./sample_defs/targets.cmake << EOF + SET(MISSION_NAME GithubActions) + SET(SPACECRAFT_ID 0x42) + SET(MISSION_CPUNAMES cpu1) + SET(cpu1_PROCESSORID 1) + SET(MISSION_GLOBAL_APPLIST) + EOF + + - name: Add Dependencies to targets + if: inputs.app-name != 'cFS' && inputs.app-name != 'cFE' + shell: bash + run: echo "LIST(APPEND MISSION_GLOBAL_APPLIST $APP_DEP_LOWER $APP_LOWER)" >> ./sample_defs/targets.cmake + + - name: Check targets.cmake + shell: bash + run: cat ./sample_defs/targets.cmake + + - name: Set up build tree + shell: bash + run: | + mkdir -p $GITHUB_WORKSPACE/build + echo DESTDIR=$GITHUB_WORKSPACE/build >> $GITHUB_ENV + cd $GITHUB_WORKSPACE/build && cmake ${{ inputs.cmake-options }} "$GITHUB_WORKSPACE/cfe" \ No newline at end of file diff --git a/actions/start-cfs-container/action.yml b/actions/start-cfs-container/action.yml new file mode 100644 index 000000000..496ab036e --- /dev/null +++ b/actions/start-cfs-container/action.yml @@ -0,0 +1,41 @@ +name: Start QEMU cFS Virtual Machine +description: 'Boots a QEMU virtual machine to execute a CFS build' + +inputs: + binary-dir: + description: 'Directory containing image files' + required: true + exec-image: + description: 'Docker image name to execute CFS' + required: false + default: ghcr.io/core-flight-system/cfsexec-qemu:latest + +outputs: + container-id: + description: "Docker container ID" + value: ${{ steps.start-container.outputs.id }} + +runs: + using: 'composite' + steps: + - name: Pull CFS container + shell: bash + run: docker pull ${{ inputs.exec-image }} + + - name: Start cFS execution container + shell: bash + id: start-container + run: | + DOCKER_ID=$(docker run -d \ + -v ${{ inputs.binary-dir }}:${{ inputs.binary-dir }} \ + --sysctl fs.mqueue.msg_max=64 \ + -w ${{ inputs.binary-dir }} \ + ${{ inputs.exec-image }} \ + ./container-start) + echo "id=$DOCKER_ID" >> $GITHUB_OUTPUT + echo "Started Container: $DOCKER_ID" + sleep 2 + + - name: List Containers + shell: bash + run: docker ps -a \ No newline at end of file diff --git a/actions/stop-cfs-container/action.yml b/actions/stop-cfs-container/action.yml new file mode 100644 index 000000000..adf380f9a --- /dev/null +++ b/actions/stop-cfs-container/action.yml @@ -0,0 +1,24 @@ +name: Stop QEMU cFS Virtual Machine +description: 'Stops a QEMU virtual machine' + +inputs: + container-id: + description: "Docker container ID" + required: true + +runs: + using: 'composite' + steps: + - name: Stop container + if: inputs.container-id != '' + shell: bash + run: docker stop "${{ inputs.container-id }}" + + - name: Remove container + if: inputs.container-id != '' + shell: bash + run: docker rm "${{ inputs.container-id }}" + + - name: List Containers + shell: bash + run: docker ps -a \ No newline at end of file