diff --git a/.clang-format b/.clang-format index b1c8cdf81..d6837a719 100755 --- a/.clang-format +++ b/.clang-format @@ -1,45 +1,85 @@ --- -Language: Cpp -AccessModifierOffset: -4 +BasedOnStyle: Google +AccessModifierOffset: '-4' AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: true -AlignConsecutiveDeclarations: true -AlignConsecutiveMacros: true +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + PadOperators: true +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + PadOperators: true AlignEscapedNewlines: Left -AlignOperands: true +AlignConsecutiveBitFields: true +AlignOperands: true AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Empty +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false -BinPackArguments: true -BinPackParameters: true -BreakBeforeBinaryOperators: None +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Allman BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon BreakStringLiterals: true -ColumnLimit: 120 -CommentPragmas: '' -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true +ColumnLimit: '120' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: '4' +ContinuationIndentWidth: '4' +Cpp11BracedListStyle: false DerivePointerAlignment: false -DisableFormat: false ExperimentalAutoDetectBinPacking: false -ForEachMacros: [] -IncludeBlocks: Preserve -IncludeCategories: [] -IncludeIsMainRegex: '$' +FixNamespaceComments: true +IncludeBlocks: Preserve IndentCaseLabels: true IndentPPDirectives: None -IndentWidth: 4 -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' +IndentWidth: '4' +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 @@ -48,19 +88,26 @@ PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right -ReflowComments: true -SortIncludes: false +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false SpaceInEmptyParentheses: false -SpaceBeforeCpp11BracedList: true -SpacesBeforeTrailingComments: 1 +SpacesBeforeTrailingComments: '1' +SpacesInAngles: false SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: c++11 -TabWidth: 8 -UseTab: Never -... +Standard: Cpp11 +TabWidth: '4' +UseTab: Never +... diff --git a/.github/workflows/build-deploy-doc.yml b/.github/workflows/build-doc-reusable.yml similarity index 75% rename from .github/workflows/build-deploy-doc.yml rename to .github/workflows/build-doc-reusable.yml index 7f94e710f..94c9ec287 100644 --- a/.github/workflows/build-deploy-doc.yml +++ b/.github/workflows/build-doc-reusable.yml @@ -23,7 +23,7 @@ on: description: Build the PDF type: boolean required: false - default: true + default: false deploy: description: Deploy archived PDF to gh-pages type: boolean @@ -34,6 +34,14 @@ on: 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: @@ -61,6 +69,7 @@ jobs: if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') }} name: Build Documentation runs-on: ubuntu-22.04 + container: ghcr.io/arielswalker/cfsbuildenv-doxygen:latest strategy: fail-fast: false @@ -79,41 +88,12 @@ jobs: 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: arielswalker/cFS/actions/setup-app@fix-cfs/workflows#177 - name: Generate OSAL header list if: ${{ inputs.needs_osal_api == true }} @@ -137,21 +117,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 @@ -164,10 +148,10 @@ jobs: path: ./deploy/${{ matrix.target }}.pdf - name: Deploy to GitHub - if: ${{ inputs.deploy == true }} + if: ${{ inputs.buildpdf == true && inputs.deploy == true }} uses: JamesIves/github-pages-deploy-action@v4 with: token: ${{ secrets.GITHUB_TOKEN }} branch: gh-pages folder: deploy - single-commit: true + single-commit: true \ 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..2f37b9604 --- /dev/null +++ b/.github/workflows/build-run-app-reusable.yml @@ -0,0 +1,186 @@ +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: '' + # 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') }} + name: Build CFE with app + runs-on: ubuntu-22.04 + container: ghcr.io/arielswalker/cfsbuildenv-linux:latest + + steps: + # Note this also sets the APP_UPPER and APP_LOWER environment variables + - name: Set up app source + uses: arielswalker/cFS/actions/setup-app@fix-cfs/workflows#177 + 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: arielswalker/cFS/actions/start-cfs-container@fix-cfs/workflows#177 + with: + binary-dir: ${{ github.workspace }}/cpu1 + + - name: Check CPU1 container + id: check-cpu1 + uses: arielswalker/cFS/actions/healthcheck-logs@fix-cfs/workflows#177 + 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: arielswalker/cFS/actions/stop-cfs-container@fix-cfs/workflows#177 + 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..82c75568b 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: @@ -14,50 +15,34 @@ defaults: shell: bash jobs: - #Checks for duplicate actions. Skips push actions if there is a matching or duplicate pull-request action. - check-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"]' - format-checker: name: Run format check - #Continue if check-for-duplicates found no duplicates. Always runs for pull-requests. - needs: check-for-duplicates - if: ${{ needs.check-for-duplicates.outputs.should_skip != 'true' }} runs-on: ubuntu-22.04 timeout-minutes: 15 + container: ghcr.io/arielswalker/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 }}/arielswalker/cFS/raw/refs/heads/fix-cfs%2Fworkflows%23177/.clang-format > $GITHUB_WORKSPACE/clang-format.yml + + - 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 - - name: Archive Static Analysis Artifacts + # 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 +50,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 +60,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(cFS/)?[a-zA-Z]*\#[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|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 69% rename from .github/workflows/unit-test-coverage.yml rename to .github/workflows/unit-test-coverage-reusable.yml index c3e0753fe..4937fdd40 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: @@ -46,42 +52,67 @@ jobs: if: ${{ needs.checks-for-duplicates.outputs.should_skip != 'true' || contains(github.ref, 'main') }} name: Build, run unit tests and enforce coverage runs-on: ubuntu-22.04 + container: ghcr.io/arielswalker/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: arielswalker/cFE + path: cfe + ref: fix-cfs-workflows-177 + + - 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 }} - - 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 +153,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..baedfa199 --- /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: 'raw/cFS/workflows/dev/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\*\*$' 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..575d56ccc --- /dev/null +++ b/actions/setup-app/action.yml @@ -0,0 +1,86 @@ +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: arielswalker/cFE + path: ./cfe + ref: fix-cfs-workflows-177 + + - 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 }} + + - 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" diff --git a/actions/start-cfs-container/action.yml b/actions/start-cfs-container/action.yml new file mode 100644 index 000000000..589a5d9d3 --- /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/arielswalker/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 diff --git a/scripts/cppcheck-merge.xslt b/scripts/cppcheck-merge.xslt new file mode 100644 index 000000000..b6d8fcfe6 --- /dev/null +++ b/scripts/cppcheck-merge.xslt @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/cppcheck-xml2text.xslt b/scripts/cppcheck-xml2text.xslt new file mode 100644 index 000000000..12c0b6ae6 --- /dev/null +++ b/scripts/cppcheck-xml2text.xslt @@ -0,0 +1,17 @@ + + + +## CppCheck Summary + +| error | warning | style | performance | portability | information | +| --- | --- | --- | --- | --- | --- | +| | | | | | | + +| severity | location | error id | issue | +| --- | --- | --- | --- | +| | : | | | + + +** error(s) reported** + +