name: Build and Test on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] workflow_dispatch: inputs: targets: description: 'Comma-separated list of targets to build (e.g., linux-amd64,darwin-arm64)' required: false default: 'host' test: description: 'Run tests' type: boolean required: false default: true env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 jobs: # Job to validate nushell version consistency validate: name: Validate Setup runs-on: ubuntu-latest outputs: nushell-version: ${{ steps.version.outputs.version }} steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive - name: Install Nushell uses: hustcer/setup-nu@v3 with: version: '0.107.1' - name: Validate nushell version id: version run: | echo "Validating nushell version consistency..." nu scripts/check_version.nu --quiet || { echo "Version mismatch detected, attempting fix..." nu scripts/check_version.nu --fix } echo "version=0.107.1" >> $GITHUB_OUTPUT - name: Check repository structure run: | echo "Checking repository structure..." ls -la echo "Plugin directories:" ls -d nu_plugin_* || echo "No plugin directories found" echo "Configuration files:" ls -la etc/ || echo "No etc directory found" # Build matrix for different platforms build: name: Build (${{ matrix.target }}) runs-on: ${{ matrix.os }} needs: validate strategy: fail-fast: false matrix: include: # Linux builds - target: linux-amd64 os: ubuntu-latest rust-target: x86_64-unknown-linux-gnu cross-compile: false - target: linux-arm64 os: ubuntu-latest rust-target: aarch64-unknown-linux-gnu cross-compile: true # macOS builds - target: darwin-amd64 os: macos-13 # Intel macOS rust-target: x86_64-apple-darwin cross-compile: false - target: darwin-arm64 os: macos-latest # Apple Silicon rust-target: aarch64-apple-darwin cross-compile: false # Windows builds - target: windows-amd64 os: windows-latest rust-target: x86_64-pc-windows-msvc cross-compile: false steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.rust-target }} components: rustfmt, clippy - name: Install Nushell uses: hustcer/setup-nu@v3 with: version: ${{ needs.validate.outputs.nushell-version }} - name: Set up cross-compilation (Linux ARM64) if: matrix.cross-compile && matrix.target == 'linux-arm64' run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu echo "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" >> $GITHUB_ENV echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - name: Cache Cargo dependencies uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-${{ matrix.target }}-cargo- ${{ runner.os }}-cargo- - name: Check code formatting run: | echo "Checking code formatting..." nu scripts/run.sh format_check.nu || { echo "Code formatting check failed. Run 'just fmt' to fix." exit 1 } - name: Run Clippy linting run: | echo "Running Clippy linting..." for plugin in nu_plugin_*; do if [ -d "$plugin" ]; then echo "Linting $plugin..." cd "$plugin" cargo clippy --target ${{ matrix.rust-target }} -- -D warnings cd .. fi done shell: bash - name: Build plugins run: | echo "Building plugins for ${{ matrix.target }}..." if [ "${{ matrix.cross-compile }}" = "true" ]; then nu scripts/build_cross.nu --targets ${{ matrix.target }} --verbose else nu scripts/build_all.nu --target ${{ matrix.rust-target }} --verbose fi shell: bash - name: Run tests if: github.event.inputs.test != 'false' && !matrix.cross-compile run: | echo "Running tests..." for plugin in nu_plugin_*; do if [ -d "$plugin" ]; then echo "Testing $plugin..." cd "$plugin" cargo test --target ${{ matrix.rust-target }} cd .. fi done shell: bash - name: Collect build artifacts run: | echo "Collecting build artifacts..." nu scripts/collect_install.nu --platform ${{ matrix.target }} --force - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: plugins-${{ matrix.target }} path: | distribution/ retention-days: 30 - name: Upload build logs if: failure() uses: actions/upload-artifact@v4 with: name: build-logs-${{ matrix.target }} path: | target/*/build/*/out target/*/build/*/stderr retention-days: 7 # Security audit audit: name: Security Audit runs-on: ubuntu-latest needs: validate steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Install cargo-audit run: cargo install cargo-audit - name: Run security audit run: | echo "Running security audit..." for plugin in nu_plugin_*; do if [ -d "$plugin" ]; then echo "Auditing $plugin..." cd "$plugin" cargo audit cd .. fi done # Collect and package results package: name: Package Results runs-on: ubuntu-latest needs: [validate, build] if: github.event_name != 'pull_request' steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Nushell uses: hustcer/setup-nu@v3 with: version: ${{ needs.validate.outputs.nushell-version }} - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts/ - name: Organize artifacts run: | echo "Organizing build artifacts..." mkdir -p distribution for artifact in artifacts/plugins-*; do if [ -d "$artifact" ]; then target=$(basename "$artifact" | sed 's/plugins-//') echo "Processing $target..." cp -r "$artifact"/* distribution/ fi done - name: Create packages run: | echo "Creating distribution packages..." nu scripts/pack_dist.nu --all-platforms --force --checksums - name: Upload packages uses: actions/upload-artifact@v4 with: name: distribution-packages path: | bin_archives/ retention-days: 90 # Quality gate quality-gate: name: Quality Gate runs-on: ubuntu-latest needs: [build, audit] if: always() steps: - name: Check build results run: | echo "Build results:" echo "Build status: ${{ needs.build.result }}" echo "Audit status: ${{ needs.audit.result }}" if [ "${{ needs.build.result }}" != "success" ]; then echo "❌ Build failed" exit 1 fi if [ "${{ needs.audit.result }}" != "success" ]; then echo "⚠️ Security audit failed" # Don't fail the build for audit issues, but warn fi echo "✅ Quality gate passed" - name: Report status if: always() run: | if [ "${{ job.status }}" = "success" ]; then echo "🎉 All checks passed!" else echo "❌ Some checks failed" fi