diff --git a/.github/CHECKLIST.md b/.github/CHECKLIST.md index 69f7622a6..f6e6ed92c 100644 --- a/.github/CHECKLIST.md +++ b/.github/CHECKLIST.md @@ -27,10 +27,17 @@ Pull Requests Releases -------- +Recommended checkpoints, use at your own discretion: + - Make at least one -betaN release to verify the GitHub workflow well in time release day - Stuff happens, remember kernelkit/infix#735 - Make at least one -rcN to flush out any issues in customer repos - Easy to forget adaptations/hacks in customer repos -- may need Infix change/support + - Verify release artifacts (checksums, completeness, no corrupted files) + - Test on actual hardware for at least one architecture + - Review ChangeLog for completeness + - Check for release-blocking issues + - Verify generated GNS3 appliance, no marketplace update on -rc builds - Ensure the markdown link for the release diff is updated - Ensure subrepos are tagged (can be automated, see kernelkit/infix#393) - Sync tags for all repo. sync activities diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 000000000..3c4f31171 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,92 @@ +name: Build Release + +on: + workflow_call: + inputs: + version: + required: true + type: string + use_cache: + required: false + type: boolean + default: true + +jobs: + build: + name: Build Infix ${{ inputs.version }} [${{ matrix.target }}] + runs-on: [ self-hosted, release ] + strategy: + matrix: + target: [aarch64, x86_64] + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + clean: true + submodules: recursive + + - name: Set Release Variables + id: vars + run: | + ver=${{ inputs.version }} + echo "ver=${ver}" >> $GITHUB_OUTPUT + fver=${ver#v} + target=${{ matrix.target }}-${fver} + echo "dir=infix-$target" >> $GITHUB_OUTPUT + echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT + + - name: Restore Cache of dl/ + if: ${{ inputs.use_cache }} + uses: actions/cache@v4 + with: + path: dl/ + key: dl-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }} + restore-keys: | + dl- + + - name: Restore Cache of .ccache/ + if: ${{ inputs.use_cache }} + uses: actions/cache@v4 + with: + path: .ccache/ + key: ccache-${{ matrix.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }} + restore-keys: | + ccache-${{ matrix.target }}- + ccache- + + - name: Configure & Build + env: + INFIX_RELEASE: ${{ steps.vars.outputs.ver }} + run: | + target=${{ matrix.target }}_defconfig + echo "Building $target ..." + make $target + make + + - name: Generate SBOM from Build + run: | + make legal-info + + - name: Build test specification + run: | + make test-spec + + - name: Prepare Artifacts + run: | + cd output/ + mv images ${{ steps.vars.outputs.dir }} + ln -s ${{ steps.vars.outputs.dir }} images + tar cfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }} + + mv legal-info legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }} + tar cfz legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }}.tar.gz legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }} + + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.target }} + path: output/*.tar.gz + + - uses: actions/upload-artifact@v4 + with: + name: artifact-disk-image-${{ matrix.target }} + path: output/images/*.qcow2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81079e598..b5856decd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,7 @@ on: parallel: required: false type: boolean - default: false + default: true env: NAME: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.name || inputs.name }} @@ -110,6 +110,11 @@ jobs: run: | make ${{ env.TARGET }}_defconfig + - name: Cleanup stale containers and ports + run: | + podman rm -af || true + pkill -9 -f rootlessport || true + - name: Unit Test ${{ env.TARGET }} run: | make test-unit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea785456e..cddcabe14 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,87 +18,27 @@ on: type: string jobs: - build: + set-version: if: github.repository == 'kernelkit/infix' && startsWith(github.ref, 'refs/tags/') - name: Build Infix ${{ github.ref_name }} [${{ matrix.target }}] - runs-on: [ self-hosted, release ] - strategy: - matrix: - target: [aarch64, x86_64] - fail-fast: false + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set-ver.outputs.version }} steps: - - uses: actions/checkout@v4 - with: - clean: true - submodules: recursive - - - name: Set Release Variables - id: vars + - id: set-ver run: | if [ -n "${{ inputs.version }}" ]; then ver=${{ inputs.version }} else ver=${GITHUB_REF#refs/tags/} fi - echo "ver=${ver}" >> $GITHUB_OUTPUT - fver=${ver#v} - target=${{ matrix.target }}-${fver} - echo "dir=infix-$target" >> $GITHUB_OUTPUT - echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT - - - name: Restore Cache of dl/ - uses: actions/cache@v4 - with: - path: dl/ - key: dl-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }} - restore-keys: | - dl- - - - name: Restore Cache of .ccache/ - uses: actions/cache@v4 - with: - path: .ccache/ - key: ccache-${{ matrix.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }} - restore-keys: | - ccache-${{ matrix.target }}- - ccache- - - - name: Configure & Build - env: - INFIX_RELEASE: ${{ steps.vars.outputs.ver }} - run: | - target=${{ matrix.target }}_defconfig - echo "Building $target ..." - make $target - make + echo "version=${ver}" >> $GITHUB_OUTPUT - - name: Generate SBOM from Build - run: | - make legal-info - - - name: Build test specification - run: | - make test-spec - - - name: Prepare Artifacts - run: | - cd output/ - mv images ${{ steps.vars.outputs.dir }} - ln -s ${{ steps.vars.outputs.dir }} images - tar cfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }} - - mv legal-info legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }} - tar cfz legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }}.tar.gz legal-info-${{ matrix.target }}-${{ steps.vars.outputs.ver }} - - - uses: actions/upload-artifact@v4 - with: - name: artifact-${{ matrix.target }} - path: output/*.tar.gz - - - uses: actions/upload-artifact@v4 - with: - name: artifact-disk-image-${{ matrix.target }} - path: output/images/*.qcow2 + build: + needs: set-version + uses: ./.github/workflows/build-release.yml + with: + version: ${{ needs.set-version.outputs.version }} + use_cache: true release: name: Release Infix ${{ github.ref_name }} @@ -156,6 +96,9 @@ jobs: run: | awk '/^-----*$/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \ |head -n -1 > release.md + echo "" >> release.md + echo "> [!TIP]" >> release.md + echo "> **Try Infix in GNS3!** Download the appliance from the [GNS3 Marketplace](https://gns3.com/marketplace/appliances/infix) to test Infix in a virtual network environment without hardware." >> release.md cat release.md - uses: ncipollo/release-action@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8b5073055..f55cf2459 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -90,7 +90,8 @@ jobs: - name: Publish Test Result for ${{ env.TARGET }} # Ensure this runs even if Regression Test fails if: always() - run: cat $TEST_PATH/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY + run: | + cat $TEST_PATH/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY - name: Generate Test Report for ${{ env.TARGET }} # Ensure this runs even if Regression Test fails diff --git a/.github/workflows/trigger.yml b/.github/workflows/trigger.yml index ed1f274da..f43efc9c9 100644 --- a/.github/workflows/trigger.yml +++ b/.github/workflows/trigger.yml @@ -14,29 +14,61 @@ concurrency: cancel-in-progress: true jobs: + # Gate all builds through this check to prevent wasted runs. Only run on + # 'labeled' events when the label is actually 'ci:main'. Concurrency control + # above handles canceling the 'opened' event when 'labeled' arrives quickly + # after (e.g., when creating a PR with ci:main already attached). See #1154. + check-trigger: + if: | + startsWith(github.repository, 'kernelkit/') && + (github.event_name != 'pull_request' || + github.event.action != 'labeled' || + github.event.label.name == 'ci:main') + runs-on: ubuntu-latest + outputs: + x86_64_target: ${{ steps.set-targets.outputs.x86_64_target }} + aarch64_target: ${{ steps.set-targets.outputs.aarch64_target }} + steps: + - run: | + echo "Triggering build, logging meta data ..." + echo "Event : ${{ github.event_name }}" + echo "Action : ${{ github.event.action }}" + echo "Ref : ${{ github.ref }}" + echo "PR : ${{ github.event.pull_request.number }}" + echo "Label : ${{ github.event.label.name }}" + - id: set-targets + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]] && \ + ! echo '${{ toJSON(github.event.pull_request.labels.*.name) }}' \ + | grep -q "ci:main"; then + echo "x86_64_target=x86_64_minimal" >> $GITHUB_OUTPUT + echo "aarch64_target=aarch64_minimal" >> $GITHUB_OUTPUT + else + echo "x86_64_target=x86_64" >> $GITHUB_OUTPUT + echo "aarch64_target=aarch64" >> $GITHUB_OUTPUT + fi + build-x86_64: - if: startsWith(github.repository, 'kernelkit/') + needs: check-trigger uses: ./.github/workflows/build.yml with: name: "infix" - target: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:main') && 'x86_64_minimal' || 'x86_64' }} + target: ${{ needs.check-trigger.outputs.x86_64_target }} build-aarch64: - if: startsWith(github.repository, 'kernelkit/') + needs: check-trigger uses: ./.github/workflows/build.yml with: name: "infix" - target: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:main') && 'aarch64_minimal' || 'aarch64' }} + target: ${{ needs.check-trigger.outputs.aarch64_target }} test-run-x86_64: - if: startsWith(github.repository, 'kernelkit/') - needs: build-x86_64 + needs: [check-trigger, build-x86_64] uses: ./.github/workflows/test.yml with: - target: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:main') && 'x86_64_minimal' || 'x86_64' }} + target: ${{ needs.check-trigger.outputs.x86_64_target }} name: "infix" test-publish-x86_64: - if: startsWith(github.repository, 'kernelkit/') needs: test-run-x86_64 uses: ./.github/workflows/publish.yml diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml new file mode 100644 index 000000000..e02ef647c --- /dev/null +++ b/.github/workflows/weekly.yml @@ -0,0 +1,68 @@ +# Weekly release build to catch flaky tests and verify clean builds. +# Runs without caches (ccache) to ensure reproducibility. See issue #1003. +name: Weekly Build + +on: + schedule: + - cron: '5 0 * * 6' # Saturday at 00:05 UTC, same as Coverity + workflow_dispatch: + +jobs: + build: + if: github.repository == 'kernelkit/infix' + uses: ./.github/workflows/build-release.yml + with: + version: "latest" + use_cache: false + + publish: + name: Publish Weekly Build + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + pattern: "artifact-*" + merge-multiple: true + + - name: Create checksums + run: | + for file in *.tar.gz; do + sha256sum $file > $file.sha256 + done + if ls *.qcow2 &>/dev/null; then + for file in *.qcow2; do + sha256sum "$file" > "$file.sha256" + done + fi + + - uses: ncipollo/release-action@v1 + with: + tag: latest + name: "Latest Weekly Build" + prerelease: true + makeLatest: false + allowUpdates: true + removeArtifacts: true + body: | + Automated weekly build from `${{ github.sha }}`. + + This build runs without caches to catch potential flaky tests and build issues. + Not intended for production use - use official releases instead. + + **Commit:** ${{ github.sha }} + **Built:** ${{ github.run_id }} + artifacts: "*.tar.gz*,*.qcow2*" + + - name: Summary + run: | + cat <> $GITHUB_STEP_SUMMARY + # Weekly Build Published! :package: + + Latest artifacts uploaded to: + + + Built from commit: \`${{ github.sha }}\` + EOF diff --git a/doc/cli/introduction.md b/doc/cli/introduction.md index c880f45db..5ddd23475 100644 --- a/doc/cli/introduction.md +++ b/doc/cli/introduction.md @@ -169,9 +169,14 @@ interfaces { } } admin@host-12-34-56:/config/interface/eth0/> leave -admin@host-12-34-56:/> show interfaces brief -lo UNKNOWN 00:00:00:00:00:00 -eth0 UP 52:54:00:12:34:56 +admin@host-12-34-56:/> show interfaces +INTERFACE PROTOCOL STATE DATA +lo loopback UNKNOWN 00:00:00:00:00:00 + ipv4 127.0.0.1/8 (static) + ipv6 ::1/128 (static) +eth0 ethernet UP 52:54:00:12:34:56 + ipv4 192.168.2.200/24 (static) + ipv6 fe80::5054:ff:fe12:3456/64 (link-layer) admin@host-12-34-56:/> show ip brief lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 192.168.2.200/24 fe80::5054:ff:fe12:3456/64 diff --git a/doc/dhcp.md b/doc/dhcp.md index 323c83ee5..8ebf9d4ab 100644 --- a/doc/dhcp.md +++ b/doc/dhcp.md @@ -116,10 +116,12 @@ When configuring, e.g., `dns-server`, or `router` options with the value subnet. For example: ``` -admin@example:/> show interfaces brief -Interface Status Address -eth0 UP 192.168.1.1/24 -eth1 UP 192.168.2.1/24 +admin@example:/> show interfaces +INTERFACE PROTOCOL STATE DATA +eth0 ethernet UP 02:00:00:00:00:00 + ipv4 192.168.1.1/24 (static) +eth1 ethernet UP 02:00:00:00:00:01 + ipv4 192.168.2.1/24 (static) admin@example:/config/dhcp-server/subnet/192.168.1.0/24/> edit option dns-server admin@example:/config/dhcp-server/subnet/192.168.1.0/24/option/dns-server/> set address auto diff --git a/test/9pm b/test/9pm index ae54b4085..0cea38dc1 160000 --- a/test/9pm +++ b/test/9pm @@ -1 +1 @@ -Subproject commit ae54b40853afe29b3975b911380bd2f5bf9d3fce +Subproject commit 0cea38dc1e0c2280cd8fa64ed99a1d7cc7fa4d2e diff --git a/test/test.mk b/test/test.mk index 9b1d9c03f..78e74a5bc 100644 --- a/test/test.mk +++ b/test/test.mk @@ -1,6 +1,7 @@ base-dir := $(lastword $(subst :, ,$(BR2_EXTERNAL))) test-dir ?= $(BR2_EXTERNAL_INFIX_PATH)/test ninepm := $(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py +ninepm_report := $(BR2_EXTERNAL_INFIX_PATH)/test/9pm/report.py NINEPM_PROJ_CONF ?= $(BR2_EXTERNAL_INFIX_PATH)/test/9pm-proj.yaml spec-dir := $(test-dir)/spec test-specification := $(BINARIES_DIR)/test-specification.pdf @@ -29,6 +30,8 @@ endif test: $(test-dir)/env -r $(base) $(mode) $(binaries) $(pkg-$(ARCH)) $(ninepm) -v $(TESTS) + $(ninepm_report) github $(test-dir)/.log/last/result.json + $(ninepm_report) asciidoc $(test-dir)/.log/last/result.json test-sh: $(test-dir)/env $(base) $(mode) $(binaries) $(pkg-$(ARCH)) -i /bin/sh