diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..1462d81 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,307 @@ +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 \ No newline at end of file diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000..86afdce --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,429 @@ +name: Nightly Build + +on: + schedule: + # Run every day at 2 AM UTC + - cron: '0 2 * * *' + workflow_dispatch: + inputs: + targets: + description: 'Comma-separated list of targets to build (leave empty for all)' + required: false + default: '' + skip_tests: + description: 'Skip running tests' + type: boolean + required: false + default: false + create_prerelease: + description: 'Create pre-release with nightly builds' + type: boolean + required: false + default: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + # Check if we should skip nightly (no changes since last nightly) + check-changes: + name: Check for Changes + runs-on: ubuntu-latest + outputs: + should_build: ${{ steps.changes.outputs.should_build }} + commit_sha: ${{ steps.changes.outputs.commit_sha }} + commit_message: ${{ steps.changes.outputs.commit_message }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check for changes since last nightly + id: changes + run: | + # Get the last nightly tag or fall back to a week ago + LAST_NIGHTLY=$(git tag -l "nightly-*" | sort -V | tail -1) + + if [ -z "$LAST_NIGHTLY" ]; then + # No previous nightly, check for changes in the last week + SINCE="$(date -d '7 days ago' '+%Y-%m-%d')" + echo "No previous nightly found, checking changes since $SINCE" + CHANGES=$(git log --since="$SINCE" --oneline | wc -l) + else + echo "Last nightly: $LAST_NIGHTLY" + CHANGES=$(git rev-list --count "$LAST_NIGHTLY"..HEAD) + fi + + echo "Changes found: $CHANGES" + + if [ "$CHANGES" -gt 0 ] || [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "should_build=true" >> $GITHUB_OUTPUT + echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "commit_message=$(git log -1 --pretty=format:'%s')" >> $GITHUB_OUTPUT + echo "โœ… Changes detected, will build nightly" + else + echo "should_build=false" >> $GITHUB_OUTPUT + echo "โŒ No changes since last nightly, skipping build" + fi + + # Validate setup + validate: + name: Validate Setup + runs-on: ubuntu-latest + needs: check-changes + if: needs.check-changes.outputs.should_build == 'true' + outputs: + nushell-version: ${{ steps.version.outputs.version }} + nightly-tag: ${{ steps.tag.outputs.tag }} + 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: Generate nightly tag + id: tag + run: | + NIGHTLY_TAG="nightly-$(date '+%Y%m%d')" + echo "tag=$NIGHTLY_TAG" >> $GITHUB_OUTPUT + echo "Nightly tag: $NIGHTLY_TAG" + + - 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 upstream changes + run: | + echo "Checking for upstream changes..." + nu scripts/check_upstream_changes.nu || { + echo "โš ๏ธ Upstream changes detected but not failing nightly build" + echo "Consider reviewing and merging upstream changes" + } + + # Build matrix for nightly builds + build-nightly: + name: Nightly Build (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + needs: [check-changes, validate] + if: needs.check-changes.outputs.should_build == 'true' + strategy: + fail-fast: false + matrix: + include: + # Core platforms for nightly + - target: linux-amd64 + os: ubuntu-latest + rust-target: x86_64-unknown-linux-gnu + cross-compile: false + priority: high + - target: darwin-arm64 + os: macos-latest + rust-target: aarch64-apple-darwin + cross-compile: false + priority: high + - target: windows-amd64 + os: windows-latest + rust-target: x86_64-pc-windows-msvc + cross-compile: false + priority: medium + + # Additional targets (can be disabled for faster nightly) + - target: linux-arm64 + os: ubuntu-latest + rust-target: aarch64-unknown-linux-gnu + cross-compile: true + priority: low + - target: darwin-amd64 + os: macos-13 + rust-target: x86_64-apple-darwin + cross-compile: false + priority: low + + 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 }} + + - 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: nightly-${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + nightly-${{ runner.os }}-${{ matrix.target }}-cargo- + ${{ runner.os }}-${{ matrix.target }}-cargo- + + - name: Build nightly binaries + run: | + echo "Building nightly binaries for ${{ matrix.target }}..." + if [ "${{ matrix.cross-compile }}" = "true" ]; then + nu scripts/build_cross.nu --targets ${{ matrix.target }} + else + nu scripts/build_all.nu --target ${{ matrix.rust-target }} + fi + shell: bash + + - name: Run tests + if: github.event.inputs.skip_tests != 'true' && !matrix.cross-compile + run: | + echo "Running tests for ${{ matrix.target }}..." + for plugin in nu_plugin_*; do + if [ -d "$plugin" ]; then + echo "Testing $plugin..." + cd "$plugin" + cargo test --target ${{ matrix.rust-target }} || { + echo "โš ๏ธ Tests failed for $plugin but continuing nightly build" + } + cd .. + fi + done + shell: bash + + - name: Collect nightly artifacts + run: | + echo "Collecting nightly artifacts..." + nu scripts/collect_install.nu --platform ${{ matrix.target }} --force + + - name: Package nightly build + run: | + echo "Creating nightly package..." + nu scripts/pack_dist.nu --platform ${{ matrix.target }} --force + shell: bash + + - name: Upload nightly artifacts + uses: actions/upload-artifact@v4 + with: + name: nightly-${{ matrix.target }} + path: | + bin_archives/*${{ matrix.target }}* + retention-days: 7 + + # Create nightly pre-release + create-nightly-release: + name: Create Nightly Pre-release + runs-on: ubuntu-latest + needs: [check-changes, validate, build-nightly] + if: needs.check-changes.outputs.should_build == 'true' && (github.event.inputs.create_prerelease == 'true' || github.event_name == 'schedule') + permissions: + contents: write + 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 nightly artifacts + uses: actions/download-artifact@v4 + with: + path: nightly-artifacts/ + + - name: Organize nightly assets + run: | + echo "Organizing nightly assets..." + mkdir -p nightly-assets + + # Copy all archives + find nightly-artifacts/ -name "*.tar.gz" -o -name "*.zip" | while read file; do + # Rename to include nightly tag + basename=$(basename "$file") + nightly_name=$(echo "$basename" | sed "s/nushell-plugins/${{ needs.validate.outputs.nightly-tag }}-nushell-plugins/") + cp "$file" "nightly-assets/$nightly_name" + done + + echo "Nightly assets:" + ls -la nightly-assets/ + + - name: Generate checksums for nightly + run: | + cd nightly-assets + sha256sum * > checksums.txt + echo "Generated checksums:" + cat checksums.txt + + - name: Delete previous nightly release + run: | + # Delete previous nightly release if it exists + if gh release view "${{ needs.validate.outputs.nightly-tag }}" >/dev/null 2>&1; then + echo "Deleting previous nightly release..." + gh release delete "${{ needs.validate.outputs.nightly-tag }}" --yes + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + + - name: Create nightly pre-release + run: | + cat > nightly_notes.md << 'EOF' + ## Nightly Build - ${{ needs.validate.outputs.nightly-tag }} + + **โš ๏ธ This is an automated nightly build and may be unstable.** + + ### Build Information + - **Commit**: ${{ needs.check-changes.outputs.commit_sha }} + - **Commit Message**: ${{ needs.check-changes.outputs.commit_message }} + - **Build Date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC') + - **Nushell Version**: ${{ needs.validate.outputs.nushell-version }} + + ### Installation + ```bash + # Quick install (replace PLATFORM with your platform) + curl -L https://github.com/${{ github.repository }}/releases/download/${{ needs.validate.outputs.nightly-tag }}/PLATFORM-${{ needs.validate.outputs.nightly-tag }}-nushell-plugins.tar.gz | tar -xz + cd PLATFORM-${{ needs.validate.outputs.nightly-tag }}-nushell-plugins + nu install_nu_plugins.nu + ``` + + ### Available Platforms + $(ls nightly-assets/*.tar.gz nightly-assets/*.zip 2>/dev/null | sed 's/nightly-assets\///g' | sed 's/^/- /' || echo "- Check release assets below") + + ### Notes + - Nightly builds are automatically created when changes are detected + - Previous nightly builds are automatically cleaned up + - For stable releases, see [Releases](https://github.com/${{ github.repository }}/releases) + - Report issues with nightly builds in [Issues](https://github.com/${{ github.repository }}/issues) + + ### Verification + All nightly assets include SHA256 checksums: + ```bash + sha256sum -c checksums.txt + ``` + EOF + + gh release create "${{ needs.validate.outputs.nightly-tag }}" \ + --title "Nightly Build - ${{ needs.validate.outputs.nightly-tag }}" \ + --notes-file nightly_notes.md \ + --prerelease \ + nightly-assets/* + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Clean up old nightly releases + run: | + echo "Cleaning up old nightly releases (keeping last 7)..." + gh release list --limit 50 | grep "nightly-" | tail -n +8 | while read line; do + tag=$(echo "$line" | awk '{print $1}') + echo "Deleting old nightly release: $tag" + gh release delete "$tag" --yes || echo "Failed to delete $tag" + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Notification and status + notify: + name: Notify Build Status + runs-on: ubuntu-latest + needs: [check-changes, build-nightly] + if: always() && needs.check-changes.outputs.should_build == 'true' + steps: + - name: Report nightly build status + run: | + echo "Nightly Build Status Report" + echo "=========================" + echo "Commit: ${{ needs.check-changes.outputs.commit_sha }}" + echo "Message: ${{ needs.check-changes.outputs.commit_message }}" + echo "Build Status: ${{ needs.build-nightly.result }}" + + if [ "${{ needs.build-nightly.result }}" = "success" ]; then + echo "โœ… Nightly build completed successfully" + elif [ "${{ needs.build-nightly.result }}" = "failure" ]; then + echo "โŒ Nightly build failed" + echo "Check the build logs for details" + else + echo "โš ๏ธ Nightly build status: ${{ needs.build-nightly.result }}" + fi + + # Could add webhook notifications here for Discord/Slack etc. + + # Maintenance tasks during nightly + maintenance: + name: Nightly Maintenance + runs-on: ubuntu-latest + needs: [check-changes, validate] + if: needs.check-changes.outputs.should_build == 'true' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Nushell + uses: hustcer/setup-nu@v3 + with: + version: ${{ needs.validate.outputs.nushell-version }} + + - name: Check for dependency updates + run: | + echo "Checking for outdated dependencies..." + for plugin in nu_plugin_*; do + if [ -d "$plugin" ]; then + echo "Checking $plugin..." + cd "$plugin" + cargo outdated || echo "โš ๏ธ cargo-outdated not available" + cd .. + fi + done + continue-on-error: true + + - name: Generate dependency report + run: | + echo "Generating dependency report..." + echo "# Dependency Status Report - $(date)" > dependency_report.md + echo "" >> dependency_report.md + + for plugin in nu_plugin_*; do + if [ -d "$plugin" ]; then + echo "## $plugin" >> dependency_report.md + echo "" >> dependency_report.md + cd "$plugin" + echo "\`\`\`" >> ../dependency_report.md + cargo tree --depth 1 >> ../dependency_report.md || echo "Failed to generate tree" >> ../dependency_report.md + echo "\`\`\`" >> ../dependency_report.md + echo "" >> ../dependency_report.md + cd .. + fi + done + continue-on-error: true + + - name: Upload dependency report + uses: actions/upload-artifact@v4 + with: + name: nightly-dependency-report + path: dependency_report.md + continue-on-error: true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3ea92b7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,365 @@ +name: Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g., v1.2.3)' + required: true + type: string + prerelease: + description: 'Mark as pre-release' + type: boolean + required: false + default: false + draft: + description: 'Create as draft release' + type: boolean + required: false + default: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + # Validate release version and setup + validate-release: + name: Validate Release + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + tag: ${{ steps.version.outputs.tag }} + nushell-version: ${{ steps.nushell.outputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Determine version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION="${{ github.ref_name }}" + fi + + echo "version=${VERSION#v}" >> $GITHUB_OUTPUT + echo "tag=${VERSION}" >> $GITHUB_OUTPUT + echo "Release version: ${VERSION}" + + - name: Install Nushell + uses: hustcer/setup-nu@v3 + with: + version: '0.107.1' + + - name: Validate nushell version + id: nushell + 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 for existing release + run: | + if gh release view "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then + echo "โŒ Release ${{ steps.version.outputs.tag }} already exists" + exit 1 + fi + echo "โœ… Release tag is available" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Validate changelog + run: | + if [ -f "CHANGELOG.md" ]; then + if grep -q "${{ steps.version.outputs.version }}" CHANGELOG.md; then + echo "โœ… Changelog entry found for version ${{ steps.version.outputs.version }}" + else + echo "โš ๏ธ No changelog entry found for version ${{ steps.version.outputs.version }}" + fi + else + echo "โš ๏ธ No CHANGELOG.md file found" + fi + + # Cross-platform release builds + build-release: + name: Build Release (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + needs: validate-release + 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 + rust-target: x86_64-apple-darwin + cross-compile: false + - target: darwin-arm64 + os: macos-latest + 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-release.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: release-${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + release-${{ runner.os }}-${{ matrix.target }}-cargo- + ${{ runner.os }}-${{ matrix.target }}-cargo- + + - name: Build release binaries + run: | + echo "Building release binaries 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 (native only) + if: "!matrix.cross-compile" + run: | + echo "Running tests for ${{ matrix.target }}..." + for plugin in nu_plugin_*; do + if [ -d "$plugin" ]; then + echo "Testing $plugin..." + cd "$plugin" + cargo test --target ${{ matrix.rust-target }} --release + cd .. + fi + done + shell: bash + + - name: Collect and package release artifacts + run: | + echo "Collecting release artifacts for ${{ matrix.target }}..." + nu scripts/collect_install.nu --platform ${{ matrix.target }} --force + + echo "Creating release package..." + nu scripts/pack_dist.nu --platform ${{ matrix.target }} --force --checksums + shell: bash + + - name: Upload release artifacts + uses: actions/upload-artifact@v4 + with: + name: release-${{ matrix.target }} + path: | + bin_archives/*${{ matrix.target }}* + retention-days: 30 + + # Create GitHub release + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: [validate-release, build-release] + permissions: + contents: write + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + release_id: ${{ steps.create_release.outputs.id }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Nushell + uses: hustcer/setup-nu@v3 + with: + version: ${{ needs.validate-release.outputs.nushell-version }} + + - name: Download all release artifacts + uses: actions/download-artifact@v4 + with: + path: release-artifacts/ + + - name: Organize release assets + run: | + echo "Organizing release assets..." + mkdir -p release-assets + + # Copy all archives to release assets + find release-artifacts/ -name "*.tar.gz" -o -name "*.zip" | while read file; do + cp "$file" release-assets/ + done + + # Copy checksums + find release-artifacts/ -name "checksums.txt" | head -1 | while read file; do + cp "$file" release-assets/checksums.txt + done + + echo "Release assets:" + ls -la release-assets/ + + - name: Generate release notes + id: release_notes + run: | + echo "Generating release notes..." + + # Extract changelog for this version if available + RELEASE_NOTES="## Nushell Plugins ${{ needs.validate-release.outputs.version }} + + ### Changes in this Release + " + + if [ -f "CHANGELOG.md" ]; then + # Try to extract changelog section for this version + awk "/^## \[?${{ needs.validate-release.outputs.version }}\]?/,/^## \[?[0-9]/" CHANGELOG.md | head -n -1 | tail -n +2 >> release_notes.md || true + fi + + # Add generic content if no specific changelog found + if [ ! -s release_notes.md ]; then + cat >> release_notes.md << 'EOF' + This release includes the latest versions of all nushell plugins with bug fixes and improvements. + + ### Installation + + #### Quick Install (Unix-like systems) + ```bash + curl -L https://github.com/${{ github.repository }}/releases/download/${{ needs.validate-release.outputs.tag }}/install.sh | sh + ``` + + #### Manual Install + 1. Download the appropriate archive for your platform + 2. Extract the archive + 3. Run the installation script: `nu install_nu_plugins.nu` + + ### Platform Support + - **Linux**: x86_64 (amd64) and ARM64 + - **macOS**: Intel (x86_64) and Apple Silicon (ARM64) + - **Windows**: x86_64 (amd64) + + ### Available Plugins + - `nu_plugin_clipboard` - Clipboard integration + - `nu_plugin_desktop_notifications` - Desktop notifications + - `nu_plugin_hashes` - Hashing utilities + - `nu_plugin_highlight` - Syntax highlighting + - `nu_plugin_image` - Image processing + - `nu_plugin_kcl` - KCL configuration language support + - `nu_plugin_port_extension` - Port utilities + - `nu_plugin_qr_maker` - QR code generation + - `nu_plugin_tera` - Tera templating + + ### Verification + All release assets include SHA256 checksums. Verify downloads using: + ```bash + sha256sum -c checksums.txt + ``` + EOF + fi + + cat release_notes.md + + # Set output for use in create-release step + echo "RELEASE_NOTES<> $GITHUB_ENV + cat release_notes.md >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Create GitHub release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.validate-release.outputs.tag }} + release_name: Nushell Plugins ${{ needs.validate-release.outputs.version }} + body: ${{ env.RELEASE_NOTES }} + draft: ${{ github.event.inputs.draft == 'true' }} + prerelease: ${{ github.event.inputs.prerelease == 'true' }} + + - name: Upload release assets + run: | + echo "Uploading release assets..." + for asset in release-assets/*; do + if [ -f "$asset" ]; then + echo "Uploading $(basename "$asset")..." + gh release upload "${{ needs.validate-release.outputs.tag }}" "$asset" + fi + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Post-release actions + post-release: + name: Post-Release Actions + runs-on: ubuntu-latest + needs: [validate-release, create-release] + if: always() && needs.create-release.result == 'success' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Notify success + run: | + echo "๐ŸŽ‰ Release ${{ needs.validate-release.outputs.version }} created successfully!" + echo "๐Ÿ“ฆ Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate-release.outputs.tag }}" + + - name: Update installation documentation + run: | + echo "Consider updating:" + echo "- README.md with new version information" + echo "- Installation instructions" + echo "- Compatibility matrix" + + - name: Create follow-up issue for next release + if: "!contains(needs.validate-release.outputs.version, 'rc') && !contains(needs.validate-release.outputs.version, 'beta')" + run: | + NEXT_VERSION=$(echo "${{ needs.validate-release.outputs.version }}" | awk -F. '{print $1"."($2+1)".0"}') + echo "Consider creating planning issue for next release: v$NEXT_VERSION" \ No newline at end of file diff --git a/Dockerfile.cross b/Dockerfile.cross new file mode 100644 index 0000000..dd2d7e3 --- /dev/null +++ b/Dockerfile.cross @@ -0,0 +1,210 @@ +# Multi-Architecture Cross-Compilation Docker Image for Nushell Plugins +# Based on Ubuntu 22.04 LTS for stability and broad compatibility + +FROM ubuntu:22.04 + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + +# Set timezone to UTC +ENV TZ=UTC + +# Install system dependencies and cross-compilation toolchains +RUN apt-get update && \ + apt-get install -y \ + # Build essentials + build-essential \ + curl \ + wget \ + git \ + pkg-config \ + # SSL and crypto libraries + libssl-dev \ + # Cross-compilation toolchains + gcc-x86-64-linux-gnu \ + gcc-aarch64-linux-gnu \ + gcc-arm-linux-gnueabihf \ + # Additional libraries for nushell plugins + libc6-dev-amd64-cross \ + libc6-dev-arm64-cross \ + libc6-dev-armhf-cross \ + # For clipboard plugin + libx11-dev \ + libxcb1-dev \ + libxcb-render0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev \ + # For image processing + libpng-dev \ + libjpeg-dev \ + # General development tools + unzip \ + zip \ + tar \ + gzip \ + file \ + # Clean up to reduce image size + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +# Install Rust with rustup +ENV RUST_VERSION=1.75.0 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${RUST_VERSION} + +# Add Rust to PATH +ENV PATH="/root/.cargo/bin:$PATH" + +# Install cross-compilation targets +RUN rustup target add \ + x86_64-unknown-linux-gnu \ + aarch64-unknown-linux-gnu \ + armv7-unknown-linux-gnueabihf \ + x86_64-pc-windows-gnu \ + x86_64-pc-windows-msvc \ + aarch64-pc-windows-msvc + +# Install additional Rust components +RUN rustup component add \ + rustfmt \ + clippy \ + rust-src + +# Install cargo tools useful for cross-compilation +RUN cargo install \ + cross \ + cargo-audit \ + cargo-outdated \ + cargo-edit + +# Set up cross-compilation environment variables and linkers +ENV CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc +ENV CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++ +ENV AR_x86_64_unknown_linux_gnu=x86_64-linux-gnu-ar +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc + +ENV CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc +ENV CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ +ENV AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc + +ENV CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc +ENV CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ +ENV AR_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar +ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc + +# Set up pkg-config for cross-compilation +ENV PKG_CONFIG_ALLOW_CROSS=1 +ENV PKG_CONFIG_PATH_x86_64_unknown_linux_gnu=/usr/lib/x86_64-linux-gnu/pkgconfig +ENV PKG_CONFIG_PATH_aarch64_unknown_linux_gnu=/usr/lib/aarch64-linux-gnu/pkgconfig +ENV PKG_CONFIG_PATH_armv7_unknown_linux_gnueabihf=/usr/lib/arm-linux-gnueabihf/pkgconfig + +# Install Windows cross-compilation dependencies +RUN apt-get update && \ + apt-get install -y \ + mingw-w64 \ + wine \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +# Set up Windows cross-compilation +ENV CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc +ENV CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++ +ENV AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar +ENV CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc + +# Create workspace directory +WORKDIR /workspace + +# Set up cargo configuration for cross-compilation +RUN mkdir -p /root/.cargo && \ + echo '[target.x86_64-unknown-linux-gnu]' >> /root/.cargo/config.toml && \ + echo 'linker = "x86_64-linux-gnu-gcc"' >> /root/.cargo/config.toml && \ + echo '' >> /root/.cargo/config.toml && \ + echo '[target.aarch64-unknown-linux-gnu]' >> /root/.cargo/config.toml && \ + echo 'linker = "aarch64-linux-gnu-gcc"' >> /root/.cargo/config.toml && \ + echo '' >> /root/.cargo/config.toml && \ + echo '[target.armv7-unknown-linux-gnueabihf]' >> /root/.cargo/config.toml && \ + echo 'linker = "arm-linux-gnueabihf-gcc"' >> /root/.cargo/config.toml && \ + echo '' >> /root/.cargo/config.toml && \ + echo '[target.x86_64-pc-windows-gnu]' >> /root/.cargo/config.toml && \ + echo 'linker = "x86_64-w64-mingw32-gcc"' >> /root/.cargo/config.toml + +# Set default environment variables for better build experience +ENV RUST_BACKTRACE=1 +ENV CARGO_INCREMENTAL=0 +ENV CARGO_NET_RETRY=10 +ENV CARGO_NET_TIMEOUT=30 + +# Create a build script that can be used as entry point +RUN echo '#!/bin/bash' > /usr/local/bin/build-plugin.sh && \ + echo 'set -e' >> /usr/local/bin/build-plugin.sh && \ + echo 'if [ -z "$1" ]; then' >> /usr/local/bin/build-plugin.sh && \ + echo ' echo "Usage: build-plugin.sh [plugin_dir]"' >> /usr/local/bin/build-plugin.sh && \ + echo ' echo "Available targets:"' >> /usr/local/bin/build-plugin.sh && \ + echo ' rustup target list --installed' >> /usr/local/bin/build-plugin.sh && \ + echo ' exit 1' >> /usr/local/bin/build-plugin.sh && \ + echo 'fi' >> /usr/local/bin/build-plugin.sh && \ + echo 'TARGET=$1' >> /usr/local/bin/build-plugin.sh && \ + echo 'PLUGIN_DIR=${2:-.}' >> /usr/local/bin/build-plugin.sh && \ + echo 'echo "Building $PLUGIN_DIR for target $TARGET..."' >> /usr/local/bin/build-plugin.sh && \ + echo 'cd "$PLUGIN_DIR"' >> /usr/local/bin/build-plugin.sh && \ + echo 'cargo build --release --target "$TARGET"' >> /usr/local/bin/build-plugin.sh && \ + echo 'echo "Build completed successfully!"' >> /usr/local/bin/build-plugin.sh && \ + chmod +x /usr/local/bin/build-plugin.sh + +# Create a helper script to list available targets +RUN echo '#!/bin/bash' > /usr/local/bin/list-targets.sh && \ + echo 'echo "Installed Rust targets:"' >> /usr/local/bin/list-targets.sh && \ + echo 'rustup target list --installed' >> /usr/local/bin/list-targets.sh && \ + echo '' >> /usr/local/bin/list-targets.sh && \ + echo 'echo "Available cross-compilation linkers:"' >> /usr/local/bin/list-targets.sh && \ + echo 'ls -1 /usr/bin/*-gcc | grep -E "(x86_64|aarch64|arm)-.*-gcc$" || true' >> /usr/local/bin/list-targets.sh && \ + chmod +x /usr/local/bin/list-targets.sh + +# Create a helper script for environment information +RUN echo '#!/bin/bash' > /usr/local/bin/env-info.sh && \ + echo 'echo "=== Rust Environment ==="' >> /usr/local/bin/env-info.sh && \ + echo 'rustc --version' >> /usr/local/bin/env-info.sh && \ + echo 'cargo --version' >> /usr/local/bin/env-info.sh && \ + echo '' >> /usr/local/bin/env-info.sh && \ + echo 'echo "=== Installed Targets ==="' >> /usr/local/bin/env-info.sh && \ + echo 'rustup target list --installed' >> /usr/local/bin/env-info.sh && \ + echo '' >> /usr/local/bin/env-info.sh && \ + echo 'echo "=== Cross-Compilation Toolchains ==="' >> /usr/local/bin/env-info.sh && \ + echo 'echo "GCC versions:"' >> /usr/local/bin/env-info.sh && \ + echo 'x86_64-linux-gnu-gcc --version 2>/dev/null | head -1 || echo "x86_64-linux-gnu-gcc: not found"' >> /usr/local/bin/env-info.sh && \ + echo 'aarch64-linux-gnu-gcc --version 2>/dev/null | head -1 || echo "aarch64-linux-gnu-gcc: not found"' >> /usr/local/bin/env-info.sh && \ + echo 'arm-linux-gnueabihf-gcc --version 2>/dev/null | head -1 || echo "arm-linux-gnueabihf-gcc: not found"' >> /usr/local/bin/env-info.sh && \ + echo 'x86_64-w64-mingw32-gcc --version 2>/dev/null | head -1 || echo "x86_64-w64-mingw32-gcc: not found"' >> /usr/local/bin/env-info.sh && \ + echo '' >> /usr/local/bin/env-info.sh && \ + echo 'echo "=== Environment Variables ==="' >> /usr/local/bin/env-info.sh && \ + echo 'env | grep -E "(CC_|CXX_|AR_|CARGO_TARGET_|PKG_CONFIG)" | sort' >> /usr/local/bin/env-info.sh && \ + chmod +x /usr/local/bin/env-info.sh + +# Add helpful aliases to bashrc +RUN echo 'alias ll="ls -la"' >> /root/.bashrc && \ + echo 'alias targets="list-targets.sh"' >> /root/.bashrc && \ + echo 'alias envinfo="env-info.sh"' >> /root/.bashrc && \ + echo 'alias build="build-plugin.sh"' >> /root/.bashrc + +# Verify installation and show information +RUN echo "=== Docker Image Build Complete ===" && \ + echo "Rust version:" && rustc --version && \ + echo "Cargo version:" && cargo --version && \ + echo "Installed targets:" && rustup target list --installed && \ + echo "Cross-compilation toolchains:" && \ + (x86_64-linux-gnu-gcc --version | head -1) && \ + (aarch64-linux-gnu-gcc --version | head -1) && \ + (arm-linux-gnueabihf-gcc --version | head -1) && \ + (x86_64-w64-mingw32-gcc --version | head -1) && \ + echo "=== Ready for cross-compilation! ===" + +# Set default command to show environment info +CMD ["env-info.sh"] + +# Metadata labels +LABEL maintainer="Nushell Plugins Team" +LABEL description="Cross-compilation environment for Nushell plugins" +LABEL version="1.0.0" +LABEL rust.version="1.75.0" +LABEL targets="x86_64-unknown-linux-gnu,aarch64-unknown-linux-gnu,armv7-unknown-linux-gnueabihf,x86_64-pc-windows-gnu" \ No newline at end of file diff --git a/README.md b/README.md index 0143c9e..e18fe54 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A comprehensive collection of nushell plugins with automated upstream tracking, - [Quick Start](#-quick-start) - [Why This Repository?](#-why-this-repository) - [Plugin Collection](#-plugin-collection) +- [Building and Cross-Compilation](#-building-and-cross-compilation) - [Development Workflows](#-development-workflows) - [Upstream Tracking System](#-upstream-tracking-system) - [Scripts and Tools](#-scripts-and-tools) @@ -95,6 +96,82 @@ This repository provides: - ๐Ÿ  **Local**: Developed locally without upstream - ๐Ÿ”’ **Private**: Private repository with tracking (requires authentication) +## ๐Ÿ—๏ธ Building and Cross-Compilation + +This repository includes a comprehensive cross-platform build system that supports building plugins for multiple platforms from a single machine. + +### ๐Ÿš€ Quick Build Commands + +```bash +# Build for your current platform +just build + +# Build for all supported platforms +just build-cross-all + +# Build for specific platform +just build-cross linux-amd64 +just build-cross darwin-arm64 +just build-cross windows-amd64 + +# Complete cross-platform release +just release-cross +``` + +### ๐ŸŽฏ Supported Platforms + +| Platform | Architecture | Native | Docker | Status | +|----------|-------------|--------|---------|--------| +| **Linux** | AMD64 | โœ… | โœ… | Full support | +| **Linux** | ARM64 | โš ๏ธ | โœ… | Docker recommended | +| **macOS** | Intel | ๐ŸŽ | โŒ | macOS host only | +| **macOS** | Apple Silicon | ๐ŸŽ | โŒ | macOS host only | +| **Windows** | AMD64 | ๐ŸชŸ | โœ… | Windows host or Docker | + +### ๐Ÿณ Docker Cross-Compilation + +For consistent builds across all platforms: + +```bash +# Build Docker cross-compilation environment +just build-docker-image + +# Use Docker for specific targets +just build-docker linux-arm64 +just build-docker windows-amd64 + +# Complete Docker workflow +just docker-flow +``` + +### ๐Ÿ“ฆ Distribution + +```bash +# Collect binaries for all platforms +just collect-all + +# Package with checksums +just pack-checksums + +# One-liner: build, collect, and package everything +just release-cross +``` + +### ๐Ÿ“– Comprehensive Guide + +For detailed instructions, platform-specific setup, troubleshooting, and advanced topics, see: + +**โžก๏ธ [Complete Building and Cross-Compilation Guide](docs/BUILDING.md)** + +The guide covers: +- Platform-specific setup and dependencies +- Native vs Docker cross-compilation +- Configuration and customization +- Troubleshooting common issues +- Performance optimization +- CI/CD integration +- Custom target addition + ## ๐Ÿ”„ Development Workflows ### Daily Development (Using Just) @@ -117,13 +194,17 @@ just test-plugin nu_plugin_clipboard ### Release Workflow ```bash -# Complete release pipeline +# Complete release pipeline (native) just release-flow +# Cross-platform release pipeline +just release-flow-cross + # Or step by step: -just build # Build all plugins -just collect # Collect binaries -just pack # Create distribution archive +just build # Build all plugins (native) +just build-cross-all # Build all plugins (all platforms) +just collect-all # Collect binaries (all platforms) +just pack-checksums # Create distribution archives with checksums ``` ### Quality Assurance diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 0000000..91fc500 --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,850 @@ +# Building and Cross-Compilation Guide + +This comprehensive guide covers everything you need to know about building nushell plugins, including cross-compilation for multiple platforms. + +## Table of Contents + +- [Quick Start](#quick-start) +- [Prerequisites](#prerequisites) +- [Build Methods](#build-methods) +- [Cross-Compilation](#cross-compilation) +- [Docker Builds](#docker-builds) +- [Platform Support](#platform-support) +- [Configuration](#configuration) +- [Workflows](#workflows) +- [Distribution](#distribution) +- [Troubleshooting](#troubleshooting) +- [FAQ](#faq) +- [Advanced Topics](#advanced-topics) + +## Quick Start + +### Native Build (Host Platform Only) +```bash +# Clone repository +git clone https://github.com/YOUR_ORG/nushell-plugins.git +cd nushell-plugins + +# Build all plugins for your platform +just build + +# Or using the script directly +./scripts/run.sh build_all.nu +``` + +### Cross-Platform Build +```bash +# Build for all supported platforms +just build-cross-all + +# Build for specific platform +just build-cross linux-amd64 + +# Build and create distribution packages +just release-cross +``` + +## Prerequisites + +### Required Tools + +1. **Rust Toolchain** (1.70.0+) + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` + +2. **Nushell** (0.107.1) + ```bash + # Install via cargo + cargo install nu + + # Or download from https://github.com/nushell/nushell/releases + ``` + +3. **Just** (optional but recommended) + ```bash + cargo install just + ``` + +4. **Git** with submodules + ```bash + git clone --recursive https://github.com/YOUR_ORG/nushell-plugins.git + ``` + +### Optional Tools + +- **Docker** - For Docker-based cross-compilation +- **Cross-compilation toolchains** - For native cross-compilation + +## Build Methods + +### 1. Native Compilation + +Build plugins for your current platform using the standard Rust toolchain. + +```bash +# Basic build +just build + +# Verbose output +just build-verbose + +# Parallel build (experimental) +just build-parallel + +# Build specific plugins +./scripts/run.sh build_all.nu --plugins nu_plugin_clipboard,nu_plugin_image +``` + +**Pros:** +- Fast compilation +- Native debugging support +- Simple setup + +**Cons:** +- Only builds for current platform +- Limited cross-compilation support + +### 2. Cross-Compilation + +Build plugins for different platforms using Rust's cross-compilation features. + +```bash +# List available targets +just build-targets + +# Build for specific target +just build-cross linux-amd64 +just build-cross darwin-arm64 +just build-cross windows-amd64 + +# Build for all targets +just build-cross-all + +# Parallel cross-compilation +just build-cross-parallel +``` + +**Pros:** +- Multiple platforms from single machine +- Fast execution +- No Docker dependency + +**Cons:** +- May require target-specific toolchains +- Some targets may not work on all hosts + +### 3. Docker-Based Cross-Compilation + +Use Docker containers with pre-configured cross-compilation environments. + +```bash +# Build Docker image +just build-docker-image + +# Build specific target with Docker +just build-docker linux-arm64 + +# Force Docker for all builds +./scripts/run.sh build_cross.nu --all-targets --docker +``` + +**Pros:** +- Consistent build environment +- All toolchains pre-installed +- Works on any Docker-capable host + +**Cons:** +- Slower than native compilation +- Requires Docker +- Larger resource usage + +## Cross-Compilation + +### Supported Platforms + +| Platform | Target Triple | Native | Docker | Notes | +|----------|---------------|--------|---------|-------| +| **Linux AMD64** | `x86_64-unknown-linux-gnu` | โœ… | โœ… | Most common Linux | +| **Linux ARM64** | `aarch64-unknown-linux-gnu` | โš ๏ธ | โœ… | Requires cross toolchain | +| **macOS Intel** | `x86_64-apple-darwin` | ๐ŸŽ | โŒ | macOS host only | +| **macOS Apple Silicon** | `aarch64-apple-darwin` | ๐ŸŽ | โŒ | macOS host only | +| **Windows AMD64** | `x86_64-pc-windows-msvc` | ๐ŸชŸ | โœ… | Windows host or Docker | + +**Legend:** +- โœ… Fully supported +- โš ๏ธ Requires additional setup +- ๐ŸŽ macOS host required +- ๐ŸชŸ Windows host required +- โŒ Not supported + +### Setting Up Cross-Compilation + +#### Linux โ†’ Linux ARM64 +```bash +# Install cross-compilation toolchain (Ubuntu/Debian) +sudo apt-get install gcc-aarch64-linux-gnu + +# Add Rust target +rustup target add aarch64-unknown-linux-gnu + +# Configure environment +export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc + +# Build +just build-cross linux-arm64 +``` + +#### Any โ†’ Windows (Docker) +```bash +# Build Docker image with Windows toolchain +just build-docker-image + +# Cross-compile to Windows +just build-docker windows-amd64 +``` + +### Configuration Files + +#### Build Targets (`etc/build_targets.toml`) + +```toml +[targets.linux-amd64] +rust_target = "x86_64-unknown-linux-gnu" +platform_name = "linux-amd64" +archive_format = "tar.gz" +docker_required = false +native_build = true +description = "Linux x86_64 (64-bit Intel/AMD)" + +[targets.linux-arm64] +rust_target = "aarch64-unknown-linux-gnu" +platform_name = "linux-arm64" +archive_format = "tar.gz" +docker_required = true +native_build = false +description = "Linux ARM64 (64-bit ARM)" +linker = "aarch64-linux-gnu-gcc" +``` + +#### Environment Variables +```bash +# Cross-compilation linkers +export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc +export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ +export AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar + +# Cargo configuration +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc +``` + +## Docker Builds + +### Docker Image + +The cross-compilation Docker image includes: +- **Rust toolchain** (1.75.0) +- **Cross-compilation targets** for all supported platforms +- **System toolchains** (GCC, MinGW, etc.) +- **Build tools** (cargo, rustfmt, clippy) + +### Docker Commands + +```bash +# Build Docker image +just build-docker-image + +# Rebuild from scratch +just build-docker-image-fresh + +# Show Docker environment info +just docker-info + +# Build specific plugin with Docker +just build-docker-plugin nu_plugin_clipboard linux-arm64 + +# Clean up Docker artifacts +just docker-cleanup +``` + +### Docker Workflow + +```bash +# Complete Docker-based workflow +just docker-flow + +# This runs: +# 1. just validate-nushell +# 2. just build-docker-image +# 3. just build-cross-all --docker +# 4. just collect-all +# 5. just pack-all +``` + +### Manual Docker Usage + +```bash +# Run interactive Docker container +docker run -it --rm \ + -v $(pwd):/workspace \ + -w /workspace \ + nushell-plugins-cross:latest \ + bash + +# Build specific target in container +docker run --rm \ + -v $(pwd):/workspace \ + -w /workspace/nu_plugin_clipboard \ + nushell-plugins-cross:latest \ + cargo build --release --target aarch64-unknown-linux-gnu +``` + +## Platform Support + +### Linux + +**Supported Architectures:** +- x86_64 (AMD64) - Native and Docker +- ARM64 (AArch64) - Docker recommended +- ARM32 (ARMv7) - Docker only, disabled by default + +**Dependencies:** +```bash +# Ubuntu/Debian +sudo apt-get install build-essential libssl-dev pkg-config + +# Cross-compilation toolchains +sudo apt-get install gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf +``` + +### macOS + +**Supported Architectures:** +- Intel (x86_64) - Native only +- Apple Silicon (ARM64) - Native only + +**Dependencies:** +```bash +# Install Xcode command line tools +xcode-select --install + +# Add Rust targets +rustup target add x86_64-apple-darwin aarch64-apple-darwin +``` + +**Cross-compilation:** +```bash +# On Apple Silicon, build for Intel +just build-cross darwin-amd64 + +# On Intel, build for Apple Silicon (may not work) +just build-cross darwin-arm64 +``` + +### Windows + +**Supported Architectures:** +- x86_64 (AMD64) - Native and Docker + +**Native Dependencies:** +- Visual Studio Build Tools or Visual Studio Community +- Windows SDK + +**Docker Alternative:** +```bash +# Use Docker for Windows builds on any platform +just build-docker windows-amd64 +``` + +## Configuration + +### Global Configuration + +The main configuration file is `etc/build_targets.toml`: + +```toml +[metadata] +version = "1.0.0" +description = "Cross-compilation targets for nushell plugins" + +[defaults] +docker_image = "nushell-plugins-cross:latest" +strip_binaries = true +optimize_size = true + +[targets.custom-target] +rust_target = "x86_64-unknown-linux-musl" +platform_name = "linux-amd64-musl" +archive_format = "tar.gz" +docker_required = false +description = "Linux x86_64 with musl libc" +enabled = true +``` + +### Environment Configuration + +Create an `env` file for local overrides: + +```bash +# env file +TARGET_PATH=distribution +INSTALL_BIN_PATH=/usr/local/bin +BIN_ARCHIVES_DIR_PATH=bin_archives +APP_NAME=nushell-plugins +``` + +### Plugin-Specific Configuration + +Some plugins may require special build configurations: + +```toml +# In plugin's Cargo.toml +[package.metadata.cross] +image = "ghcr.io/cross-rs/cross:aarch64-unknown-linux-gnu" + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" +``` + +## Workflows + +### Development Workflows + +#### Standard Development +```bash +just dev-flow +# 1. Validate nushell version +# 2. Check upstream changes +# 3. Build for host platform +# 4. Run tests +# 5. Show status +``` + +#### Cross-Platform Development +```bash +just dev-flow-cross +# 1. Validate nushell version +# 2. Check upstream changes +# 3. Build for all platforms +# 4. Show status +``` + +#### Quality Assurance +```bash +just quality-flow +# 1. Validate nushell version +# 2. Format code +# 3. Run clippy linting +# 4. Run tests +``` + +### Release Workflows + +#### Standard Release +```bash +just release-flow +# 1. Validate nushell version +# 2. Build for host platform +# 3. Collect binaries +# 4. Create packages +``` + +#### Cross-Platform Release +```bash +just release-flow-cross +# 1. Validate nushell version +# 2. Build for all platforms +# 3. Collect all binaries +# 4. Create packages with checksums +``` + +#### CI Simulation +```bash +just ci-flow +# Simulates exactly what GitHub Actions will run +``` + +### Custom Workflows + +You can create custom workflows by combining individual commands: + +```bash +# Custom workflow for testing +just validate-nushell +just build-cross linux-amd64 +just build-cross darwin-arm64 +just test +just collect-platform linux-amd64 +just pack-platform linux-amd64 +``` + +## Distribution + +### Collection + +Collect built binaries for distribution: + +```bash +# Collect all platforms +just collect-all + +# Collect specific platform +just collect-platform linux-amd64 + +# List what can be collected +just collect-platforms +``` + +### Packaging + +Create distribution archives: + +```bash +# Package all platforms +just pack-all + +# Package with checksums +just pack-checksums + +# Package specific platform +just pack-platform darwin-arm64 + +# List packaging options +just pack-platforms +``` + +### Directory Structure + +After building and packaging: + +``` +distribution/ +โ”œโ”€โ”€ linux-amd64/ +โ”‚ โ”œโ”€โ”€ nu_plugin_clipboard +โ”‚ โ”œโ”€โ”€ nu_plugin_image +โ”‚ โ”œโ”€โ”€ install_nu_plugins.nu +โ”‚ โ”œโ”€โ”€ LICENSE +โ”‚ โ””โ”€โ”€ README +โ”œโ”€โ”€ darwin-arm64/ +โ”‚ โ””โ”€โ”€ [same structure] +โ””โ”€โ”€ windows-amd64/ + โ”œโ”€โ”€ nu_plugin_clipboard.exe + โ””โ”€โ”€ [same structure] + +bin_archives/ +โ”œโ”€โ”€ linux-amd64-nushell-plugins.tar.gz +โ”œโ”€โ”€ darwin-arm64-nushell-plugins.tar.gz +โ”œโ”€โ”€ windows-amd64-nushell-plugins.zip +โ””โ”€โ”€ checksums.txt +``` + +### Checksums + +All distribution packages include SHA256 checksums: + +```bash +# Generate checksums +just pack-checksums + +# Verify downloaded archive +sha256sum -c checksums.txt + +# Manual verification +sha256sum linux-amd64-nushell-plugins.tar.gz +``` + +## Troubleshooting + +### Common Issues + +#### Version Mismatch +```bash +# Error: Nushell version mismatch detected +# Fix: Update versions automatically +just fix-nushell + +# Or manually check and fix +./scripts/run.sh check_version.nu --fix +``` + +#### Missing Cross-Compilation Target +```bash +# Error: target 'aarch64-unknown-linux-gnu' not found +# Fix: Install the target +rustup target add aarch64-unknown-linux-gnu +``` + +#### Cross-Compilation Linker Error +```bash +# Error: linker 'aarch64-linux-gnu-gcc' not found +# Fix: Install cross-compilation toolchain +sudo apt-get install gcc-aarch64-linux-gnu + +# Set environment variable +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc +``` + +#### Docker Build Failures +```bash +# Error: Docker daemon not running +# Fix: Start Docker service +sudo systemctl start docker + +# Error: Docker image not found +# Fix: Build the image +just build-docker-image + +# Error: Permission denied in Docker +# Fix: Add user to docker group +sudo usermod -aG docker $USER +``` + +#### Build Failures + +**Plugin-specific failures:** +```bash +# Debug specific plugin +cd nu_plugin_clipboard +cargo build --release --verbose + +# Check plugin dependencies +cargo tree +``` + +**Cross-compilation failures:** +```bash +# Use Docker instead of native cross-compilation +just build-docker linux-arm64 + +# Check target support +rustup target list | grep installed +``` + +### Debug Mode + +Enable verbose output for troubleshooting: + +```bash +# Verbose build output +just build-verbose + +# Verbose cross-compilation +./scripts/run.sh build_cross.nu --all-targets --verbose + +# Debug Docker builds +./scripts/run.sh build_docker_cross.nu --verbose +``` + +### Log Files + +Build logs are preserved for debugging: + +```bash +# View recent build logs +ls -la target/*/build/*/out/ + +# Search for specific errors +grep -r "error" target/*/build/*/stderr +``` + +## FAQ + +### General Questions + +**Q: What platforms are supported?** +A: Linux (AMD64, ARM64), macOS (Intel, Apple Silicon), and Windows (AMD64). See the [Platform Support](#platform-support) section for details. + +**Q: Do I need Docker for cross-compilation?** +A: Not always. Native cross-compilation works for many targets, but Docker provides a more consistent environment and supports more target combinations. + +**Q: How long does cross-compilation take?** +A: Building all platforms takes 10-30 minutes depending on your hardware. Parallel builds (`just build-cross-parallel`) can significantly reduce this time. + +**Q: Can I add new platforms?** +A: Yes! Edit `etc/build_targets.toml` to add new targets. See [Configuration](#configuration) for details. + +### Build Questions + +**Q: Why do some builds fail on my platform?** +A: Cross-compilation has limitations. macOS targets can only be built on macOS, and some Linux ARM64 builds require specific toolchains. Use Docker builds as a fallback. + +**Q: How can I speed up builds?** +A: Use parallel builds (`--parallel`), enable incremental compilation, and use Docker layer caching. Consider building only the platforms you need. + +**Q: Can I build just one plugin?** +A: Yes! Use `./scripts/run.sh build_all.nu --plugins nu_plugin_clipboard` or build manually in the plugin directory. + +**Q: Why are Windows binaries so large?** +A: Windows binaries include the MSVC runtime. You can reduce size by using the GNU toolchain or enabling link-time optimization. + +### Docker Questions + +**Q: How much disk space does the Docker image use?** +A: The cross-compilation image is approximately 2-3 GB, including all toolchains and dependencies. + +**Q: Can I use my own Docker image?** +A: Yes! Modify the `docker.image_tag` setting in `etc/build_targets.toml` to use a custom image. + +**Q: How do I update the Docker image?** +A: Run `just build-docker-image-fresh` to rebuild from scratch with the latest dependencies. + +### Distribution Questions + +**Q: How do I install the plugins?** +A: Use the universal installer: `curl -L https://github.com/YOUR_ORG/nushell-plugins/releases/latest/download/install.sh | sh` + +**Q: Can I distribute individual plugins?** +A: Yes! Each plugin binary is self-contained. Just copy the `nu_plugin_*` files to the target system. + +**Q: What's the difference between tar.gz and zip archives?** +A: Unix-like systems (Linux, macOS) use tar.gz for better compression and permission preservation. Windows uses zip for compatibility. + +### Troubleshooting Questions + +**Q: Build fails with "permission denied" errors** +A: Check that you have write permissions to the target directory and that antivirus software isn't blocking the builds. + +**Q: Cross-compilation fails with "unsupported target"** +A: Install the target with `rustup target add TARGET_NAME` or use Docker builds instead. + +**Q: Docker builds are very slow** +A: Make sure Docker has sufficient resources allocated. Consider using Docker BuildKit for faster builds. + +## Advanced Topics + +### Custom Build Targets + +Add support for new platforms by editing `etc/build_targets.toml`: + +```toml +[targets.linux-musl] +rust_target = "x86_64-unknown-linux-musl" +platform_name = "linux-amd64-musl" +archive_format = "tar.gz" +docker_required = false +description = "Linux x86_64 with musl libc" +strip_binaries = true + +[environment.linux-musl] +CC = "musl-gcc" +``` + +### Build Optimization + +#### Link-Time Optimization (LTO) +```toml +# In Cargo.toml +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" +strip = "symbols" +``` + +#### Size Optimization +```toml +[profile.release] +opt-level = "z" # Optimize for size +lto = true +strip = "symbols" +``` + +#### Build Scripts + +Create custom build scripts for complex scenarios: + +```bash +#!/bin/bash +# scripts/custom_build.sh + +# Custom build with specific optimizations +export RUSTFLAGS="-C target-cpu=native" +cargo build --release --target x86_64-unknown-linux-gnu + +# Strip debug symbols +strip target/x86_64-unknown-linux-gnu/release/nu_plugin_* +``` + +### Continuous Integration + +The repository includes GitHub Actions workflows for: + +- **Build & Test** (`.github/workflows/build.yml`) +- **Release** (`.github/workflows/release.yml`) +- **Nightly Builds** (`.github/workflows/nightly.yml`) + +To set up in your own repository: + +1. Copy the `.github/workflows/` directory +2. Update the repository references +3. Configure GitHub secrets if needed +4. Customize the build matrix for your needs + +### Plugin Development + +When developing new plugins: + +1. **Use the template:** + ```bash + just make-plugin nu_plugin_yourname + ``` + +2. **Follow the plugin structure:** + ``` + nu_plugin_yourname/ + โ”œโ”€โ”€ Cargo.toml + โ”œโ”€โ”€ src/ + โ”‚ โ”œโ”€โ”€ main.rs + โ”‚ โ””โ”€โ”€ lib.rs + โ””โ”€โ”€ README.md + ``` + +3. **Test cross-compilation early:** + ```bash + just build-cross-all + ``` + +4. **Add to the registry:** + Update `etc/plugin_registry.toml` if the plugin has an upstream repository. + +### Performance Tuning + +#### Parallel Compilation +```bash +# Set number of parallel jobs +export CARGO_BUILD_JOBS=8 + +# Use parallel cross-compilation +just build-cross-parallel +``` + +#### Caching +```bash +# Use sccache for distributed compilation caching +cargo install sccache +export RUSTC_WRAPPER=sccache +``` + +#### Memory Usage +```bash +# Limit memory usage for large builds +export CARGO_BUILD_RUSTFLAGS="-C link-arg=-Wl,--no-keep-memory" +``` + +--- + +## Getting Help + +- **Issues**: [GitHub Issues](https://github.com/YOUR_ORG/nushell-plugins/issues) +- **Discussions**: [GitHub Discussions](https://github.com/YOUR_ORG/nushell-plugins/discussions) +- **Documentation**: [Repository Wiki](https://github.com/YOUR_ORG/nushell-plugins/wiki) +- **Nushell Community**: [Discord](https://discord.gg/NtAbbGn) | [Reddit](https://www.reddit.com/r/Nushell/) + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on contributing to the build system and adding new platforms or features. + +--- + +*This documentation is maintained by the nushell-plugins team. Last updated: 2024-09-20* \ No newline at end of file diff --git a/etc/build_targets.toml b/etc/build_targets.toml new file mode 100644 index 0000000..c0893e8 --- /dev/null +++ b/etc/build_targets.toml @@ -0,0 +1,173 @@ +# Build Targets Configuration for Nushell Plugins +# Defines supported cross-compilation targets and their requirements + +[metadata] +version = "1.0.0" +description = "Cross-compilation targets for nushell plugins" +last_updated = "2024-09-20" + +# Default settings applied to all targets unless overridden +[defaults] +docker_image = "nushell-plugins-cross:latest" +strip_binaries = true +optimize_size = true + +# Linux AMD64 - Most common Linux target +[targets.linux-amd64] +rust_target = "x86_64-unknown-linux-gnu" +platform_name = "linux-amd64" +archive_format = "tar.gz" +docker_required = false +native_build = true +description = "Linux x86_64 (64-bit Intel/AMD)" + +# Linux ARM64 - Growing in popularity with cloud instances +[targets.linux-arm64] +rust_target = "aarch64-unknown-linux-gnu" +platform_name = "linux-arm64" +archive_format = "tar.gz" +docker_required = true +native_build = false +description = "Linux ARM64 (64-bit ARM)" +linker = "aarch64-linux-gnu-gcc" + +# macOS AMD64 - Intel Macs +[targets.darwin-amd64] +rust_target = "x86_64-apple-darwin" +platform_name = "darwin-amd64" +archive_format = "tar.gz" +docker_required = false +native_build = true +host_required = "darwin" +description = "macOS x86_64 (Intel Macs)" + +# macOS ARM64 - Apple Silicon Macs +[targets.darwin-arm64] +rust_target = "aarch64-apple-darwin" +platform_name = "darwin-arm64" +archive_format = "tar.gz" +docker_required = false +native_build = true +host_required = "darwin" +description = "macOS ARM64 (Apple Silicon)" + +# Windows AMD64 - Standard Windows target +[targets.windows-amd64] +rust_target = "x86_64-pc-windows-msvc" +platform_name = "windows-amd64" +archive_format = "zip" +docker_required = true +native_build = false +description = "Windows x86_64 (64-bit)" +binary_extension = ".exe" + +# Additional targets that can be enabled +[targets.linux-arm32] +rust_target = "armv7-unknown-linux-gnueabihf" +platform_name = "linux-arm32" +archive_format = "tar.gz" +docker_required = true +native_build = false +enabled = false +description = "Linux ARM32 (32-bit ARM)" + +[targets.windows-arm64] +rust_target = "aarch64-pc-windows-msvc" +platform_name = "windows-arm64" +archive_format = "zip" +docker_required = true +native_build = false +enabled = false +description = "Windows ARM64" +binary_extension = ".exe" + +# Docker configuration for cross-compilation +[docker] +dockerfile = "Dockerfile.cross" +image_tag = "nushell-plugins-cross:latest" +cache_from = ["nushell-plugins-cross:cache"] +build_args = [] + +# Platform-specific configurations +[platform_configs.linux] +package_formats = ["tar.gz", "deb", "rpm"] +install_prefix = "/usr/local/bin" +config_dir = "/etc/nushell-plugins" + +[platform_configs.darwin] +package_formats = ["tar.gz", "dmg"] +install_prefix = "/usr/local/bin" +config_dir = "/usr/local/etc/nushell-plugins" + +[platform_configs.windows] +package_formats = ["zip", "msi"] +install_prefix = "C:\\Program Files\\nushell-plugins" +config_dir = "C:\\ProgramData\\nushell-plugins" + +# Build optimization settings +[optimization] +# Enable link-time optimization for release builds +lto = true +# Code generation units for parallel compilation +codegen_units = 1 +# Panic strategy (abort for smaller binaries) +panic_strategy = "abort" +# Strip debug symbols +strip = "symbols" + +# Environment variables for cross-compilation +[environment] +# Common variables for all builds +RUST_BACKTRACE = "1" +CARGO_INCREMENTAL = "0" + +# Target-specific environment variables +[environment.linux-arm64] +CC = "aarch64-linux-gnu-gcc" +CXX = "aarch64-linux-gnu-g++" +AR = "aarch64-linux-gnu-ar" +CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = "aarch64-linux-gnu-gcc" + +[environment.windows-amd64] +# Windows-specific environment setup would go here +# Usually handled by the Docker container + +# Feature flags for conditional compilation +[features] +# Default features to enable for all builds +default = ["clipboard", "image", "networking"] + +# Optional features that can be disabled for smaller binaries +optional = ["crypto", "database", "desktop-notifications"] + +# Platform-specific features +[features.linux] +available = ["clipboard", "image", "networking", "desktop-notifications"] + +[features.darwin] +available = ["clipboard", "image", "networking", "desktop-notifications"] + +[features.windows] +available = ["clipboard", "image", "networking"] + +# Validation rules for targets +[validation] +# Minimum required Rust version +min_rust_version = "1.70.0" + +# Required tools for native compilation +required_tools = ["cargo", "rustc"] + +# Required tools for Docker compilation +docker_tools = ["docker"] + +# Testing configuration +[testing] +# Run tests for these targets during CI +ci_targets = ["linux-amd64", "darwin-amd64", "darwin-arm64"] + +# Quick test targets for development +dev_targets = ["linux-amd64"] + +# Full integration test targets +integration_targets = ["linux-amd64", "darwin-arm64"] \ No newline at end of file diff --git a/etc/plugin_registry.toml b/etc/plugin_registry.toml index dd51119..d46bedf 100644 --- a/etc/plugin_registry.toml +++ b/etc/plugin_registry.toml @@ -59,7 +59,7 @@ has_local_changes = true description = "Desktop notifications plugin for nushell (local development)" [plugins.nu_plugin_fluent] -upstream_url = "https://github.com/YourUsername/nu_plugin_fluent" +upstream_url = "ssh://git@repo.jesusperez.pro:32225/jesus/nu_plugin_fluent.git" upstream_branch = "main" last_checked_commit = "" last_checked_date = "" @@ -118,7 +118,7 @@ notify_on_pending_changes = true upstream_url = "https://github.com/cptpiepmatz/nu-plugin-highlight" upstream_branch = "main" last_checked_commit = "ee5c049314cae074dffffddac5c1d4f7a6374b6a" -last_checked_date = "2025-09-20 14:35:04" +last_checked_date = "2025-09-20 18:38:38" status = "pending" auto_ok_on_nu_deps_only = true local_path = "nu_plugin_highlight" @@ -129,7 +129,7 @@ description = "Syntax highlighting plugin for nushell" upstream_url = "https://github.com/FMotalleb/nu_plugin_clipboard" upstream_branch = "main" last_checked_commit = "ffb98a64720ac18329f578eac9a751dbc99951b5" -last_checked_date = "2025-09-20 14:35:06" +last_checked_date = "2025-09-20 18:38:39" status = "pending" auto_ok_on_nu_deps_only = true local_path = "nu_plugin_clipboard" @@ -141,7 +141,7 @@ upstream_url = "" upstream_branch = "" last_checked_commit = "" last_checked_date = "" -status = "local_only" +status = "ok" auto_ok_on_nu_deps_only = false local_path = "nu_plugin_image" has_local_changes = true @@ -152,7 +152,7 @@ upstream_url = "" upstream_branch = "" last_checked_commit = "" last_checked_date = "" -status = "local_only" +status = "ok" auto_ok_on_nu_deps_only = false local_path = "nu_plugin_hashes" has_local_changes = true @@ -163,18 +163,18 @@ upstream_url = "" upstream_branch = "" last_checked_commit = "" last_checked_date = "" -status = "local_only" +status = "ok" auto_ok_on_nu_deps_only = false local_path = "nu_plugin_desktop_notifications" has_local_changes = true description = "Desktop notifications plugin for nushell (local development)" ["plugins.nu_plugin_fluent"] -upstream_url = "https://github.com/YourUsername/nu_plugin_fluent" +upstream_url = "ssh://git@repo.jesusperez.pro:32225/jesus/nu_plugin_fluent.git" upstream_branch = "main" -last_checked_commit = "" -last_checked_date = "" -status = "error" +last_checked_commit = "7f11208aef1ad6db0530ce5e15402d52e326daea" +last_checked_date = "2025-09-20 18:38:40" +status = "pending" auto_ok_on_nu_deps_only = true local_path = "nu_plugin_fluent" has_local_changes = true @@ -183,8 +183,8 @@ description = "Fluent localization plugin for nushell" ["plugins.nu_plugin_tera"] upstream_url = "ssh://git@repo.jesusperez.pro:32225/jesus/nu_plugin_tera.git" upstream_branch = "main" -last_checked_commit = "da1ee63a250bfa4ee3c32e846f22b53c00a1385f" -last_checked_date = "2025-09-20 14:35:07" +last_checked_commit = "714e70e5593243b4bf6e25724c1d4aab308d3aab" +last_checked_date = "2025-09-20 18:38:40" status = "ok" auto_ok_on_nu_deps_only = true local_path = "nu_plugin_tera" @@ -194,8 +194,8 @@ description = "Tera templating plugin for nushell (private repo)" ["plugins.nu_plugin_kcl"] upstream_url = "ssh://git@repo.jesusperez.pro:32225/jesus/nu_plugin_kcl.git" upstream_branch = "main" -last_checked_commit = "02870456387c94bfd68fd26f6bf0cfaf8b3accff" -last_checked_date = "2025-09-20 14:35:08" +last_checked_commit = "1e6df05931fb6f771e4805fd7d71a841ee301b62" +last_checked_date = "2025-09-20 18:38:41" status = "ok" auto_ok_on_nu_deps_only = true local_path = "nu_plugin_kcl" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..0d1f6c0 --- /dev/null +++ b/install.sh @@ -0,0 +1,476 @@ +#!/bin/bash + +# Universal Installer for Nushell Plugins +# Downloads and installs the latest release of nushell plugins for your platform + +set -e + +# Configuration +REPO_OWNER="YOUR_ORG" # Replace with actual GitHub org/user +REPO_NAME="nushell-plugins" +GITHUB_REPO="${REPO_OWNER}/${REPO_NAME}" +BASE_URL="https://github.com/${GITHUB_REPO}" +API_URL="https://api.github.com/repos/${GITHUB_REPO}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default values +INSTALL_DIR="/usr/local/bin" +VERSION="" +PLATFORM="" +FORCE=false +DRY_RUN=false +QUIET=false +TEMP_DIR="" + +# Functions +log() { + if [ "$QUIET" != "true" ]; then + echo -e "${BLUE}[INFO]${NC} $1" + fi +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" >&2 +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +usage() { + cat << EOF +Universal Installer for Nushell Plugins + +USAGE: + $0 [OPTIONS] + +OPTIONS: + -d, --install-dir DIR Installation directory (default: /usr/local/bin) + -v, --version VERSION Specific version to install (default: latest) + -p, --platform PLATFORM Force specific platform (auto-detected by default) + -f, --force Force installation even if already installed + --dry-run Show what would be done without doing it + --quiet Suppress non-essential output + -h, --help Show this help message + +EXAMPLES: + # Install latest version to default location + $0 + + # Install to custom directory + $0 --install-dir ~/.local/bin + + # Install specific version + $0 --version v1.2.3 + + # Force specific platform + $0 --platform linux-amd64 + +PLATFORMS: + linux-amd64 Linux x86_64 + linux-arm64 Linux ARM64 + darwin-amd64 macOS Intel + darwin-arm64 macOS Apple Silicon + windows-amd64 Windows x86_64 + +REQUIREMENTS: + - curl or wget + - tar (for Unix-like systems) + - nushell (nu command should be available) + - Write permissions to installation directory + +MORE INFO: + Repository: ${BASE_URL} + Releases: ${BASE_URL}/releases +EOF +} + +# Platform detection +detect_platform() { + local os="" + local arch="" + + # Detect OS + case "$(uname -s)" in + Linux*) os="linux" ;; + Darwin*) os="darwin" ;; + CYGWIN*|MINGW*|MSYS*) os="windows" ;; + *) + error "Unsupported operating system: $(uname -s)" + exit 1 + ;; + esac + + # Detect architecture + case "$(uname -m)" in + x86_64|amd64) arch="amd64" ;; + aarch64|arm64) arch="arm64" ;; + armv7l) arch="arm32" ;; + *) + error "Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + + echo "${os}-${arch}" +} + +# Check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check prerequisites +check_prerequisites() { + log "Checking prerequisites..." + + # Check for download tools + if ! command_exists curl && ! command_exists wget; then + error "Neither curl nor wget found. Please install one of them." + exit 1 + fi + + # Check for tar (Unix-like systems) + if [ "$PLATFORM" != "windows-amd64" ] && ! command_exists tar; then + error "tar command not found. Please install tar." + exit 1 + fi + + # Check for nushell + if ! command_exists nu; then + warn "nushell (nu) not found in PATH. You'll need to install nushell to use these plugins." + warn "Visit: https://www.nushell.sh/book/installation.html" + fi + + # Check write permissions + if [ ! -w "$(dirname "$INSTALL_DIR")" ]; then + error "No write permission to $(dirname "$INSTALL_DIR")" + error "Try running with sudo or choose a different directory with --install-dir" + exit 1 + fi +} + +# Download file +download_file() { + local url="$1" + local output="$2" + + log "Downloading: $url" + + if command_exists curl; then + curl -L --fail --progress-bar "$url" -o "$output" + elif command_exists wget; then + wget --progress=bar:force:noscroll "$url" -O "$output" + else + error "No download tool available" + exit 1 + fi +} + +# Get latest release info +get_latest_release() { + local api_url="${API_URL}/releases/latest" + + log "Fetching latest release information..." + + if command_exists curl; then + curl -s "$api_url" + elif command_exists wget; then + wget -qO- "$api_url" + else + error "No download tool available" + exit 1 + fi +} + +# Get specific release info +get_release_info() { + local version="$1" + local api_url="${API_URL}/releases/tags/${version}" + + log "Fetching release information for ${version}..." + + if command_exists curl; then + curl -s "$api_url" + elif command_exists wget; then + wget -qO- "$api_url" + else + error "No download tool available" + exit 1 + fi +} + +# Parse JSON (simple parsing for our needs) +parse_json_value() { + local json="$1" + local key="$2" + + echo "$json" | sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1 +} + +# Find download URL for platform +find_download_url() { + local release_json="$1" + local platform="$2" + + # Look for asset with platform name + echo "$release_json" | grep -o '"browser_download_url":"[^"]*"' | \ + grep "$platform" | \ + head -1 | \ + sed 's/"browser_download_url":"\([^"]*\)"/\1/' +} + +# Extract archive +extract_archive() { + local archive="$1" + local dest_dir="$2" + + log "Extracting archive to $dest_dir..." + + case "$archive" in + *.tar.gz) + tar -xzf "$archive" -C "$dest_dir" + ;; + *.zip) + if command_exists unzip; then + unzip -q "$archive" -d "$dest_dir" + else + error "unzip command not found. Please install unzip to extract .zip files." + exit 1 + fi + ;; + *) + error "Unsupported archive format: $archive" + exit 1 + ;; + esac +} + +# Install plugins +install_plugins() { + local extract_dir="$1" + + log "Installing plugins to $INSTALL_DIR..." + + # Find the extracted directory (might be nested) + local plugin_dir="" + if [ -d "$extract_dir" ]; then + # Look for nu_plugin_* files directly or in subdirectories + plugin_dir=$(find "$extract_dir" -name "nu_plugin_*" -type f -executable | head -1 | xargs dirname) + + if [ -z "$plugin_dir" ]; then + # Try to find a directory with plugins + plugin_dir=$(find "$extract_dir" -type d -name "*nushell-plugins*" | head -1) + if [ -z "$plugin_dir" ]; then + plugin_dir="$extract_dir" + fi + fi + fi + + if [ ! -d "$plugin_dir" ]; then + error "Could not find plugin directory in extracted archive" + exit 1 + fi + + log "Found plugins in: $plugin_dir" + + # Create install directory if it doesn't exist + mkdir -p "$INSTALL_DIR" + + # Copy plugin binaries + local installed_count=0 + for plugin in "$plugin_dir"/nu_plugin_*; do + if [ -f "$plugin" ] && [ -x "$plugin" ]; then + local plugin_name=$(basename "$plugin") + local dest_path="$INSTALL_DIR/$plugin_name" + + if [ "$DRY_RUN" = "true" ]; then + log "Would install: $plugin_name -> $dest_path" + else + log "Installing: $plugin_name" + cp "$plugin" "$dest_path" + chmod +x "$dest_path" + fi + + installed_count=$((installed_count + 1)) + fi + done + + if [ $installed_count -eq 0 ]; then + error "No plugin binaries found to install" + exit 1 + fi + + success "Installed $installed_count plugins" + + # Copy installation script if available + if [ -f "$plugin_dir/install_nu_plugins.nu" ] && [ "$DRY_RUN" != "true" ]; then + log "Running nushell installation script..." + if command_exists nu; then + cd "$plugin_dir" + nu install_nu_plugins.nu --bin-path "$INSTALL_DIR" || { + warn "Installation script failed, but binaries were copied" + } + else + warn "Nushell not available, skipping automatic plugin registration" + fi + fi +} + +# Cleanup temporary files +cleanup() { + if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then + log "Cleaning up temporary files..." + rm -rf "$TEMP_DIR" + fi +} + +# Main installation function +main() { + # Set up cleanup trap + trap cleanup EXIT + + log "Nushell Plugins Universal Installer" + log "Repository: $GITHUB_REPO" + + # Auto-detect platform if not specified + if [ -z "$PLATFORM" ]; then + PLATFORM=$(detect_platform) + log "Detected platform: $PLATFORM" + else + log "Using specified platform: $PLATFORM" + fi + + # Check prerequisites + check_prerequisites + + # Get release information + local release_json="" + if [ -z "$VERSION" ]; then + log "Getting latest release..." + release_json=$(get_latest_release) + VERSION=$(parse_json_value "$release_json" "tag_name") + else + log "Getting release $VERSION..." + release_json=$(get_release_info "$VERSION") + fi + + if [ -z "$release_json" ] || [ "$release_json" = "null" ]; then + error "Failed to get release information" + exit 1 + fi + + log "Version: $VERSION" + + # Find download URL + local download_url=$(find_download_url "$release_json" "$PLATFORM") + + if [ -z "$download_url" ]; then + error "No download found for platform: $PLATFORM" + error "Available platforms can be found at: ${BASE_URL}/releases/tag/${VERSION}" + exit 1 + fi + + log "Download URL: $download_url" + + if [ "$DRY_RUN" = "true" ]; then + log "DRY RUN: Would download and install from $download_url" + log "DRY RUN: Would install to $INSTALL_DIR" + return 0 + fi + + # Create temporary directory + TEMP_DIR=$(mktemp -d) + local archive_name=$(basename "$download_url") + local archive_path="$TEMP_DIR/$archive_name" + + # Download archive + download_file "$download_url" "$archive_path" + + # Verify download + if [ ! -f "$archive_path" ]; then + error "Download failed: $archive_path not found" + exit 1 + fi + + # Extract archive + extract_archive "$archive_path" "$TEMP_DIR" + + # Install plugins + install_plugins "$TEMP_DIR" + + success "Installation completed successfully!" + + # Show next steps + echo "" + echo "๐ŸŽ‰ Nushell plugins have been installed to: $INSTALL_DIR" + echo "" + echo "๐Ÿ“ Next steps:" + echo " 1. Make sure $INSTALL_DIR is in your PATH" + echo " 2. In nushell, register plugins with:" + + # List installed plugins + for plugin in "$INSTALL_DIR"/nu_plugin_*; do + if [ -f "$plugin" ] && [ -x "$plugin" ]; then + echo " plugin add $plugin" + fi + done + + echo "" + echo "๐Ÿ’ก Or run this one-liner in nushell:" + echo " ls $INSTALL_DIR/nu_plugin_* | each { |plugin| plugin add \$plugin.name }" + echo "" + echo "๐Ÿ”— More info: ${BASE_URL}" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -d|--install-dir) + INSTALL_DIR="$2" + shift 2 + ;; + -v|--version) + VERSION="$2" + shift 2 + ;; + -p|--platform) + PLATFORM="$2" + shift 2 + ;; + -f|--force) + FORCE=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --quiet) + QUIET=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + error "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Run main function +main \ No newline at end of file diff --git a/scripts/build_cross.nu b/scripts/build_cross.nu new file mode 100644 index 0000000..4294dd8 --- /dev/null +++ b/scripts/build_cross.nu @@ -0,0 +1,464 @@ +#!/usr/bin/env nu + +# Cross-Platform Build Script for Nushell Plugins +# Orchestrates cross-compilation for multiple platforms using Rust targets and Docker + +# Version check - mandatory for all plugin operations +def version_check [] { + try { + nu scripts/check_version.nu --quiet | ignore + } catch { + print "โŒ Nushell version mismatch detected!" + print "๐Ÿ”ง Run: nu scripts/check_version.nu --fix" + exit 1 + } +} + +# Load build targets configuration +def load_targets_config [] { + if not ("etc/build_targets.toml" | path exists) { + error make {msg: "Build targets configuration not found: etc/build_targets.toml"} + } + + open etc/build_targets.toml +} + +# Get host platform information +def get_host_info [] { + let arch = match (uname -m) { + "x86_64" => "amd64", + "aarch64" => "arm64", + "arm64" => "arm64", + $arch if ($arch | str starts-with "arm") => "arm64", + $other => $other + } + + let platform = match (uname -s | str downcase) { + $os if ($os | str contains "linux") => "linux", + $os if ($os | str contains "darwin") => "darwin", + $os if ($os | str contains "windows") => "windows", + $other => $other + } + + {platform: $platform, arch: $arch, full: $"($platform)-($arch)"} +} + +# Validate if target can be built on current host +def validate_target [target: record, host_info: record] { + # Check if target requires specific host + if ($target | get host_required? | default null) != null { + if $target.host_required != $host_info.platform { + return { + valid: false, + reason: $"Target requires ($target.host_required) host, but running on ($host_info.platform)" + suggestion: "Use Docker build or switch to appropriate host" + } + } + } + + # Check if native build is possible + if $target.native_build and not $target.docker_required { + # Check if rust target is installed + let installed_targets = (rustup target list --installed | lines) + if not ($target.rust_target in $installed_targets) { + return { + valid: false, + reason: $"Rust target ($target.rust_target) not installed" + suggestion: $"Run: rustup target add ($target.rust_target)" + } + } + } + + {valid: true, reason: null, suggestion: null} +} + +# Get all plugin directories +def get_plugin_directories [] { + glob "nu_plugin_*" | where ($it | path type) == "dir" +} + +# Build a single plugin for a specific target +def build_plugin_for_target [ + plugin_dir: string, + target: record, + use_docker: bool = false, + verbose: bool = false +] { + print $"๐Ÿ”จ Building ($plugin_dir) for ($target.platform_name)..." + + let start_time = date now + let original_dir = pwd + + try { + cd $plugin_dir + + mut build_cmd = ["cargo", "build", "--release"] + + if not $use_docker { + $build_cmd = ($build_cmd | append ["--target", $target.rust_target]) + } + + # Set environment variables if specified + let config = load_targets_config + let env_vars = $config.environment? | default {} + let target_env = $env_vars | get $target.platform_name? | default {} + + mut env_settings = [] + for var in ($target_env | transpose key value) { + $env_settings = ($env_settings | append $"($var.key)=($var.value)") + } + + if $verbose { + print $" Command: ($build_cmd | str join ' ')" + if ($env_settings | length) > 0 { + print $" Environment: ($env_settings | str join ', ')" + } + } + + # Execute build command + if $use_docker { + # Docker build will be handled by build_docker_cross.nu + let docker_result = nu ../scripts/build_docker_cross.nu --target $target.platform_name --plugin $plugin_dir + if $docker_result.exit_code != 0 { + error make {msg: $"Docker build failed for ($plugin_dir)"} + } + } else { + # Native cross-compilation + with-env ($target_env) { + run-external "cargo" ...$build_cmd + } + } + + cd $original_dir + let end_time = date now + let duration = $end_time - $start_time + + print $"โœ… Built ($plugin_dir) for ($target.platform_name) in ($duration)" + + { + plugin: $plugin_dir, + target: $target.platform_name, + status: "success", + duration: $duration, + error: null + } + } catch {|err| + cd $original_dir + print $"โŒ Error building ($plugin_dir) for ($target.platform_name): ($err.msg)" + + { + plugin: $plugin_dir, + target: $target.platform_name, + status: "error", + duration: null, + error: $err.msg + } + } +} + +# Check if Docker is available and running +def check_docker [] { + try { + docker --version | ignore + docker info | ignore + true + } catch { + false + } +} + +# Install missing Rust targets +def install_rust_targets [targets: list] { + print "๐Ÿฆ€ Installing missing Rust targets..." + + let installed = (rustup target list --installed | lines) + let missing = ($targets | where $it not-in $installed) + + if ($missing | length) > 0 { + print $"๐Ÿ“ฅ Installing targets: ($missing | str join ', ')" + for target in $missing { + print $" Installing ($target)..." + rustup target add $target + } + print "โœ… All targets installed" + } else { + print "โœ… All required targets already installed" + } +} + +# Get binary path for a plugin and target +def get_binary_path [plugin_dir: string, target: record] { + let binary_name = if ($target | get binary_extension? | default "") != "" { + $"($plugin_dir)($target.binary_extension)" + } else { + $plugin_dir + } + + $"($plugin_dir)/target/($target.rust_target)/release/($binary_name)" +} + +# Verify built binaries exist +def verify_built_binaries [results: list] { + print "๐Ÿ” Verifying built binaries..." + + let config = load_targets_config + mut verification_results = [] + + for result in $results { + if $result.status == "success" { + let target_config = $config.targets | get $result.target + let binary_path = get_binary_path $result.plugin $target_config + + if ($binary_path | path exists) { + let size = (ls $binary_path | get 0.size) + print $" โœ… ($result.plugin) โ†’ ($result.target): ($size)" + $verification_results = ($verification_results | append { + plugin: $result.plugin, + target: $result.target, + binary_path: $binary_path, + size: $size, + verified: true + }) + } else { + print $" โŒ ($result.plugin) โ†’ ($result.target): Binary not found at ($binary_path)" + $verification_results = ($verification_results | append { + plugin: $result.plugin, + target: $result.target, + binary_path: $binary_path, + size: null, + verified: false + }) + } + } + } + + $verification_results +} + +# Main function +def main [ + --targets (-t): list = [] # Specific targets to build (e.g., linux-amd64, darwin-arm64) + --all-targets (-a) # Build for all enabled targets + --plugins (-p): list = [] # Specific plugins to build + --docker (-d) # Force use of Docker for all builds + --native (-n) # Force native compilation (fail if not possible) + --parallel # Build plugins in parallel (experimental) + --install-targets # Install missing Rust targets automatically + --verbose (-v) # Verbose output + --dry-run # Show what would be built without building + --list-targets (-l) # List available targets and exit +] { + # Mandatory version check + version_check + + # Ensure we're in the repository root + if not ("nu_plugin_clipboard" | path exists) { + error make {msg: "Please run this script from the nushell-plugins repository root directory"} + } + + print "๐Ÿš€ Nushell Plugins Cross-Platform Build System" + + # Load configuration + let config = load_targets_config + let host_info = get_host_info + + print $"๐Ÿ–ฅ๏ธ Host: ($host_info.full)" + + # List targets mode + if $list_targets { + print "\n๐Ÿ“‹ Available build targets:" + for target_name in ($config.targets | transpose name config | get name) { + let target_config = $config.targets | get $target_name + let enabled = $target_config | get enabled? | default true + let status = if $enabled { "โœ…" } else { "โŒ" } + print $" ($status) ($target_name): ($target_config.description)" + print $" Rust target: ($target_config.rust_target)" + print $" Docker required: ($target_config.docker_required)" + print $" Native build: ($target_config.native_build)" + } + return + } + + # Determine targets to build + let available_targets = $config.targets | transpose name config + let enabled_targets = $available_targets | where ($it.config | get enabled? | default true) + + mut targets_to_build = if $all_targets { + $enabled_targets + } else if ($targets | length) > 0 { + $enabled_targets | where $it.name in $targets + } else { + # Default to host platform if available + let host_target = $enabled_targets | where $it.name == $host_info.full + if ($host_target | length) > 0 { + $host_target + } else { + # Fallback to first enabled target + $enabled_targets | first 1 + } + } + + if ($targets_to_build | length) == 0 { + print "โ“ No targets to build" + if ($targets | length) > 0 { + print $"๐Ÿ’ก Available targets: ($enabled_targets | get name | str join ', ')" + } + return + } + + # Determine plugins to build + let all_plugins = get_plugin_directories + let plugins_to_build = if ($plugins | length) > 0 { + $all_plugins | where $it in $plugins + } else { + $all_plugins + } + + if ($plugins_to_build | length) == 0 { + print "โ“ No plugins to build" + return + } + + print $"\n๐Ÿ“ฆ Plugins to build: ($plugins_to_build | length)" + for plugin in $plugins_to_build { + print $" - ($plugin)" + } + + print $"\n๐ŸŽฏ Targets to build: ($targets_to_build | length)" + for target in $targets_to_build { + print $" - ($target.name): ($target.config.description)" + } + + # Dry run mode + if $dry_run { + print "\n๐Ÿ” Dry run - showing build matrix:" + for plugin in $plugins_to_build { + for target in $targets_to_build { + let method = if $docker or $target.config.docker_required { "Docker" } else { "Native" } + print $" ($plugin) ร— ($target.name) โ†’ ($method)" + } + } + return + } + + # Check prerequisites + let docker_available = check_docker + print $"\n๐Ÿ”ง Prerequisites:" + print $" Docker: (if $docker_available { 'โœ…' } else { 'โŒ' })" + print $" Rust: โœ…" + + # Validate targets and collect required Rust targets + mut rust_targets_needed = [] + mut valid_builds = [] + + for target in $targets_to_build { + let validation = validate_target $target.config $host_info + let use_docker = $docker or $target.config.docker_required or (not $validation.valid) + + if $native and $use_docker { + print $"โŒ Cannot build ($target.name) natively: ($validation.reason)" + continue + } + + if $use_docker and not $docker_available { + print $"โŒ Docker required for ($target.name) but Docker not available" + print $"๐Ÿ’ก ($validation.suggestion)" + continue + } + + if not $use_docker and $validation.valid { + $rust_targets_needed = ($rust_targets_needed | append $target.config.rust_target) + } + + $valid_builds = ($valid_builds | append { + target: $target, + use_docker: $use_docker, + validation: $validation + }) + } + + # Install missing Rust targets + if ($rust_targets_needed | length) > 0 and $install_targets { + install_rust_targets $rust_targets_needed + } + + # Build plugins + print $"\n๐Ÿ”จ Starting builds..." + let start_time = date now + mut all_results = [] + + if $parallel { + print "โšก Building in parallel mode..." + # Build all plugin-target combinations in parallel + let build_jobs = [] + for plugin in $plugins_to_build { + for build in $valid_builds { + $build_jobs = ($build_jobs | append { + plugin: $plugin, + target: $build.target.config, + use_docker: $build.use_docker + }) + } + } + + $all_results = ($build_jobs | par-each {|job| + build_plugin_for_target $job.plugin $job.target $job.use_docker $verbose + }) + } else { + # Sequential builds + for plugin in $plugins_to_build { + for build in $valid_builds { + let result = build_plugin_for_target $plugin $build.target.config $build.use_docker $verbose + $all_results = ($all_results | append $result) + print "---" + } + } + } + + let end_time = date now + let total_duration = $end_time - $start_time + + # Verify binaries + print "\n๐Ÿ” Verification:" + let verification = verify_built_binaries $all_results + + # Summary + print "\n๐Ÿ“Š Build Summary:" + let successful = $all_results | where status == "success" + let failed = $all_results | where status == "error" + let verified = $verification | where verified == true + + print $"โœ… Successful builds: ($successful | length)" + print $"โŒ Failed builds: ($failed | length)" + print $"๐Ÿ” Verified binaries: ($verified | length)" + print $"โฑ๏ธ Total time: ($total_duration)" + + if ($failed | length) > 0 { + print "\nโŒ Failed builds:" + for failure in $failed { + print $" - ($failure.plugin) โ†’ ($failure.target): ($failure.error)" + } + } + + if ($verified | length) > 0 { + print "\nโœ… Built binaries:" + for binary in $verified { + print $" - ($binary.plugin) โ†’ ($binary.target): ($binary.size)" + } + } + + # Exit with error if any builds failed + if ($failed | length) > 0 { + exit 1 + } else { + print "\n๐ŸŽ‰ All builds completed successfully!" + print "๐Ÿ’ก Next steps:" + print " - Collect binaries: just collect" + print " - Create packages: just pack" + print " - Test binaries: just test" + } +} + +if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") { + main +} \ No newline at end of file diff --git a/scripts/build_docker_cross.nu b/scripts/build_docker_cross.nu new file mode 100644 index 0000000..ec6aefb --- /dev/null +++ b/scripts/build_docker_cross.nu @@ -0,0 +1,476 @@ +#!/usr/bin/env nu + +# Docker Cross-Compilation Manager for Nushell Plugins +# Manages Docker-based cross-compilation environment and builds + +# Load build targets configuration +def load_targets_config [] { + if not ("etc/build_targets.toml" | path exists) { + error make {msg: "Build targets configuration not found: etc/build_targets.toml"} + } + open etc/build_targets.toml +} + +# Check if Docker is available and running +def check_docker [] { + try { + let version = (docker --version | complete) + if $version.exit_code != 0 { + return {available: false, reason: "Docker command failed"} + } + + let info = (docker info | complete) + if $info.exit_code != 0 { + return {available: false, reason: "Docker daemon not running"} + } + + {available: true, reason: null} + } catch {|err| + {available: false, reason: $"Docker not found: ($err.msg)"} + } +} + +# Build the Docker cross-compilation image +def build_docker_image [ + --force (-f) # Force rebuild of image + --no-cache # Don't use Docker build cache + --verbose (-v) # Verbose output +] { + let config = load_targets_config + let docker_config = $config.docker + let dockerfile = $docker_config.dockerfile + let image_tag = $docker_config.image_tag + + print $"๐Ÿณ Building Docker cross-compilation image..." + print $"๐Ÿ“„ Dockerfile: ($dockerfile)" + print $"๐Ÿท๏ธ Tag: ($image_tag)" + + # Check if Dockerfile exists + if not ($dockerfile | path exists) { + error make {msg: $"Dockerfile not found: ($dockerfile)"} + } + + # Check if image already exists + let existing_image = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete) + if $existing_image.exit_code == 0 and ($existing_image.stdout | str trim | str length) > 0 and not $force { + print $"โœ… Docker image already exists: ($image_tag)" + print "๐Ÿ’ก Use --force to rebuild or --no-cache for fresh build" + return {success: true, image: $image_tag, rebuilt: false} + } + + let start_time = date now + + try { + mut build_args = ["docker", "build", "-f", $dockerfile, "-t", $image_tag] + + if $no_cache { + $build_args = ($build_args | append "--no-cache") + } + + # Add cache-from if specified + if ($docker_config.cache_from? | default [] | length) > 0 { + for cache in $docker_config.cache_from { + $build_args = ($build_args | append ["--cache-from", $cache]) + } + } + + # Add build args if specified + if ($docker_config.build_args? | default [] | length) > 0 { + for arg in $docker_config.build_args { + $build_args = ($build_args | append ["--build-arg", $arg]) + } + } + + $build_args = ($build_args | append ".") + + if $verbose { + print $"๐Ÿ”จ Build command: ($build_args | str join ' ')" + } + + print "๐Ÿ”จ Building Docker image (this may take several minutes)..." + let result = (run-external "docker" ...($build_args | skip 1)) + + let end_time = date now + let duration = $end_time - $start_time + + print $"โœ… Docker image built successfully: ($image_tag)" + print $"โฑ๏ธ Build time: ($duration)" + + # Show image info + let image_info = (docker images $image_tag --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}") + print $"๐Ÿ“Š Image info:" + print $image_info + + {success: true, image: $image_tag, rebuilt: true, duration: $duration} + } catch {|err| + print $"โŒ Failed to build Docker image: ($err.msg)" + {success: false, image: null, error: $err.msg} + } +} + +# Get all plugin directories +def get_plugin_directories [] { + glob "nu_plugin_*" | where ($it | path type) == "dir" +} + +# Build a single plugin in Docker for a specific target +def build_plugin_in_docker [ + plugin_dir: string, + target_name: string, + image_tag: string, + verbose: bool = false +] { + let config = load_targets_config + let target_config = $config.targets | get $target_name + + print $"๐Ÿณ Building ($plugin_dir) for ($target_name) in Docker..." + + let start_time = date now + let work_dir = "/workspace" + let source_dir = $"/workspace/($plugin_dir)" + + # Prepare Docker run command + mut docker_args = [ + "docker", "run", "--rm", + "-v", $"(pwd):($work_dir)", + "-w", $source_dir, + $image_tag + ] + + # Set environment variables for the target + let env_vars = $config.environment? | default {} + let target_env = $env_vars | get $target_name? | default {} + + for var in ($target_env | transpose key value) { + $docker_args = ($docker_args | append ["-e", $"($var.key)=($var.value)"]) + } + + # Add common environment variables + $docker_args = ($docker_args | append [ + "-e", "RUST_BACKTRACE=1", + "-e", "CARGO_INCREMENTAL=0" + ]) + + # Build command + let build_cmd = ["cargo", "build", "--release", "--target", $target_config.rust_target] + $docker_args = ($docker_args | append $build_cmd) + + try { + if $verbose { + print $" ๐Ÿ”จ Docker command: ($docker_args | str join ' ')" + } + + let result = (run-external "docker" ...($docker_args | skip 1)) + + let end_time = date now + let duration = $end_time - $start_time + + # Check if binary was created + let binary_name = if ($target_config | get binary_extension? | default "") != "" { + $"($plugin_dir)($target_config.binary_extension)" + } else { + $plugin_dir + } + + let binary_path = $"($plugin_dir)/target/($target_config.rust_target)/release/($binary_name)" + + if ($binary_path | path exists) { + let size = (ls $binary_path | get 0.size) + print $"โœ… Built ($plugin_dir) for ($target_name) in ($duration) โ†’ ($size)" + + { + plugin: $plugin_dir, + target: $target_name, + status: "success", + duration: $duration, + binary_path: $binary_path, + size: $size, + error: null + } + } else { + error make {msg: $"Binary not found at expected path: ($binary_path)"} + } + } catch {|err| + let end_time = date now + let duration = $end_time - $start_time + + print $"โŒ Error building ($plugin_dir) for ($target_name): ($err.msg)" + + { + plugin: $plugin_dir, + target: $target_name, + status: "error", + duration: $duration, + binary_path: null, + size: null, + error: $err.msg + } + } +} + +# Clean up Docker build artifacts +def cleanup_docker [ + --images (-i) # Remove Docker images + --containers (-c) # Remove stopped containers + --volumes (-v) # Remove unused volumes + --all (-a) # Clean everything +] { + print "๐Ÿงน Cleaning up Docker artifacts..." + + if $all or $containers { + print "๐Ÿ—‘๏ธ Removing stopped containers..." + try { + docker container prune -f + } catch { + print "โš ๏ธ No containers to remove" + } + } + + if $all or $volumes { + print "๐Ÿ—‘๏ธ Removing unused volumes..." + try { + docker volume prune -f + } catch { + print "โš ๏ธ No volumes to remove" + } + } + + if $all or $images { + print "๐Ÿ—‘๏ธ Removing unused images..." + try { + docker image prune -f + } catch { + print "โš ๏ธ No images to remove" + } + } + + print "โœ… Docker cleanup completed" +} + +# Show Docker environment information +def show_docker_info [] { + let docker_status = check_docker + + print "๐Ÿณ Docker Environment Information" + print $"Status: (if $docker_status.available { 'โœ… Available' } else { 'โŒ Not available' })" + + if not $docker_status.available { + print $"Reason: ($docker_status.reason)" + return + } + + try { + print "\n๐Ÿ“Š Docker Version:" + docker --version + + print "\n๐Ÿ’พ Docker System Info:" + docker system df + + print "\n๐Ÿ–ผ๏ธ Available Images:" + docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" + + let config = load_targets_config + let image_tag = $config.docker.image_tag + + print $"\n๐ŸŽฏ Cross-compilation Image Status:" + let image_exists = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete) + if $image_exists.exit_code == 0 and ($image_exists.stdout | str trim | str length) > 0 { + print $"โœ… Image exists: ($image_tag)" + let image_info = (docker images $image_tag --format "table {{.Size}}\t{{.CreatedAt}}") + print $image_info + } else { + print $"โŒ Image not found: ($image_tag)" + print "๐Ÿ’ก Run with --build-image to create it" + } + } catch {|err| + print $"โŒ Error getting Docker info: ($err.msg)" + } +} + +# Main function +def main [ + --target (-t): string = "" # Specific target to build for + --plugin (-p): string = "" # Specific plugin to build + --build-image (-b) # Build Docker image before building + --force-rebuild (-f) # Force rebuild of Docker image + --no-cache # Don't use Docker build cache + --parallel # Build plugins in parallel + --cleanup # Clean up Docker artifacts after build + --info (-i) # Show Docker environment info + --verbose (-v) # Verbose output + --dry-run # Show what would be built +] { + # Ensure we're in the repository root + if not ("nu_plugin_clipboard" | path exists) { + error make {msg: "Please run this script from the nushell-plugins repository root directory"} + } + + print "๐Ÿณ Docker Cross-Compilation Manager for Nushell Plugins" + + # Check Docker availability + let docker_status = check_docker + if not $docker_status.available { + print $"โŒ Docker not available: ($docker_status.reason)" + print "๐Ÿ’ก Please install and start Docker to use cross-compilation" + exit 1 + } + + # Info mode + if $info { + show_docker_info + return + } + + let config = load_targets_config + let image_tag = $config.docker.image_tag + + # Build Docker image if requested or if it doesn't exist + let image_exists = (docker images $image_tag --format "{{.Repository}}:{{.Tag}}" | complete) + let should_build_image = $build_image or $force_rebuild or ( + $image_exists.exit_code != 0 or ($image_exists.stdout | str trim | str length) == 0 + ) + + if $should_build_image { + let build_result = build_docker_image --force=$force_rebuild --no-cache=$no_cache --verbose=$verbose + if not $build_result.success { + print "โŒ Failed to build Docker image, cannot proceed" + exit 1 + } + } + + # Determine targets and plugins + let available_targets = $config.targets | transpose name config | where ($it.config | get enabled? | default true) + let docker_targets = $available_targets | where $it.config.docker_required + + let targets_to_build = if ($target | str length) > 0 { + let selected = $docker_targets | where $it.name == $target + if ($selected | length) == 0 { + print $"โŒ Target not found or doesn't require Docker: ($target)" + print $"๐Ÿ’ก Available Docker targets: ($docker_targets | get name | str join ', ')" + exit 1 + } + $selected + } else { + $docker_targets + } + + let all_plugins = get_plugin_directories + let plugins_to_build = if ($plugin | str length) > 0 { + let selected = $all_plugins | where $it == $plugin + if ($selected | length) == 0 { + print $"โŒ Plugin not found: ($plugin)" + print $"๐Ÿ’ก Available plugins: ($all_plugins | str join ', ')" + exit 1 + } + $selected + } else { + $all_plugins + } + + if ($targets_to_build | length) == 0 { + print "โ“ No Docker targets to build" + print $"๐Ÿ’ก Available Docker targets: ($docker_targets | get name | str join ', ')" + return + } + + if ($plugins_to_build | length) == 0 { + print "โ“ No plugins to build" + return + } + + print $"\n๐Ÿ“ฆ Plugins to build: ($plugins_to_build | length)" + for plugin in $plugins_to_build { + print $" - ($plugin)" + } + + print $"\n๐ŸŽฏ Docker targets: ($targets_to_build | length)" + for target in $targets_to_build { + print $" - ($target.name): ($target.config.description)" + } + + # Dry run mode + if $dry_run { + print "\n๐Ÿ” Dry run - showing build matrix:" + for plugin in $plugins_to_build { + for target in $targets_to_build { + print $" ๐Ÿณ ($plugin) ร— ($target.name)" + } + } + return + } + + # Build plugins + print $"\n๐Ÿ”จ Starting Docker builds..." + let start_time = date now + mut all_results = [] + + if $parallel { + print "โšก Building in parallel mode..." + let build_jobs = [] + for plugin in $plugins_to_build { + for target in $targets_to_build { + $build_jobs = ($build_jobs | append { + plugin: $plugin, + target: $target.name + }) + } + } + + $all_results = ($build_jobs | par-each {|job| + build_plugin_in_docker $job.plugin $job.target $image_tag $verbose + }) + } else { + # Sequential builds + for plugin in $plugins_to_build { + for target in $targets_to_build { + let result = build_plugin_in_docker $plugin $target.name $image_tag $verbose + $all_results = ($all_results | append $result) + print "---" + } + } + } + + let end_time = date now + let total_duration = $end_time - $start_time + + # Summary + print "\n๐Ÿ“Š Docker Build Summary:" + let successful = $all_results | where status == "success" + let failed = $all_results | where status == "error" + + print $"โœ… Successful builds: ($successful | length)" + print $"โŒ Failed builds: ($failed | length)" + print $"โฑ๏ธ Total time: ($total_duration)" + + if ($successful | length) > 0 { + print "\nโœ… Built binaries:" + for success in $successful { + print $" - ($success.plugin) โ†’ ($success.target): ($success.size)" + } + } + + if ($failed | length) > 0 { + print "\nโŒ Failed builds:" + for failure in $failed { + print $" - ($failure.plugin) โ†’ ($failure.target): ($failure.error)" + } + } + + # Cleanup if requested + if $cleanup { + print "" + cleanup_docker --containers --volumes + } + + # Exit with error if any builds failed + if ($failed | length) > 0 { + exit 1 + } else { + print "\n๐ŸŽ‰ All Docker builds completed successfully!" + print "๐Ÿ’ก Binaries are available in plugin/target/RUST_TARGET/release/ directories" + } +} + +if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") { + main +} \ No newline at end of file