name: Rust Cross-Platform Build on: workflow_dispatch: push: branches: [ "development", "main", "feature/*", "bugfix/*", "enhancement/*" ] tags: [ "v*.*.*" ] pull_request: branches: [ "development", "main" ] env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 REGISTRY: git.triggermeelmo.com IMAGE_NAME: donpat1to/watcher-agent TAG: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'development' || github.ref_type == 'tag' && github.ref_name || 'pr' }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: detect-project: name: Detect Rust Project runs-on: ubuntu-latest outputs: project-dir: ${{ steps.detect.outputs.project-dir }} project-name: ${{ steps.detect.outputs.project-name }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Detect Rust project id: detect run: | if [ -f "Cargo.toml" ]; then echo "project-dir=." >> $GITHUB_OUTPUT PROJECT_NAME=$(grep -m1 '^name =' "Cargo.toml" | sed -n 's/^name = "\(.*\)"/\1/p') echo "project-name=${PROJECT_NAME}" >> $GITHUB_OUTPUT else for dir in */; do if [ -f "${dir}Cargo.toml" ]; then echo "project-dir=${dir%/}" >> $GITHUB_OUTPUT PROJECT_NAME=$(grep -m1 '^name =' "${dir}Cargo.toml" | sed -n 's/^name = "\(.*\)"/\1/p') echo "project-name=${PROJECT_NAME}" >> $GITHUB_OUTPUT break fi done fi if [ -z "$(grep 'project-name=' $GITHUB_OUTPUT | cut -d= -f2)" ]; then echo "::error::No Rust project found!" exit 1 fi setup-rust: name: Setup Rust Toolchain needs: detect-project if: ${{ !failure() && !cancelled() }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable targets: x86_64-unknown-linux-gnu, x86_64-pc-windows-gnu components: rustfmt, clippy test: name: Run Tests needs: [detect-project, setup-rust] if: ${{ !failure() && !cancelled() }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Run tests working-directory: ${{ needs.detect-project.outputs.project-dir }} run: cargo test --verbose --no-fail-fast - name: Run clippy working-directory: ${{ needs.detect-project.outputs.project-dir }} run: cargo clippy -- -D warnings audit: name: Security Audit needs: [detect-project, setup-rust] if: ${{ !failure() && !cancelled() }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Install cargo-audit run: cargo install cargo-audit - name: Audit dependencies working-directory: ${{ needs.detect-project.outputs.project-dir }} run: cargo audit --ignore RUSTSEC-2020-0071 --ignore RUSTSEC-2020-0159 build: name: Build (${{ matrix.target }}) needs: [detect-project, setup-rust, test, audit] if: ${{ !failure() && !cancelled() }} runs-on: ubuntu-latest strategy: matrix: include: - target: x86_64-unknown-linux-gnu os: linux artifact-name: linux-binary strip-command: strip - target: x86_64-pc-windows-gnu os: windows artifact-name: windows-binary strip-command: x86_64-w64-mingw32-strip timeout-minutes: 30 steps: - uses: actions/checkout@v4 - name: Install Rust and target uses: dtolnay/rust-toolchain@stable with: toolchain: stable target: ${{ matrix.target }} - name: Install cross-compilation tools if: matrix.target == 'x86_64-pc-windows-gnu' run: | sudo apt-get update && sudo apt-get install -y gcc-mingw-w64-x86-64 - name: Build release binary working-directory: ${{ needs.detect-project.outputs.project-dir }} run: | cargo build --release --target ${{ matrix.target }} echo "Built ${{ matrix.target }} successfully" - name: Strip debug symbols working-directory: ${{ needs.detect-project.outputs.project-dir }} run: | binary_name="${{ needs.detect-project.outputs.project-name }}" if [ "${{ matrix.os }}" = "windows" ]; then ${{ matrix.strip-command }} "target/${{ matrix.target }}/release/$binary_name.exe" else ${{ matrix.strip-command }} "target/${{ matrix.target }}/release/$binary_name" fi - name: Upload artifact uses: actions/upload-artifact@v3 with: name: ${{ matrix.artifact-name }} path: | ${{ needs.detect-project.outputs.project-dir }}/target/${{ matrix.target }}/release/${{ needs.detect-project.outputs.project-name }}${{ matrix.os == 'windows' && '.exe' || '' }} docker-build: name: Build and Push Docker Image needs: [detect-project, build] if: | always() && needs.detect-project.result == 'success' && needs.build.result == 'success' && github.event_name != 'pull_request' runs-on: ubuntu-latest environment: production steps: - uses: actions/checkout@v4 - name: Download Linux artifact uses: actions/download-artifact@v3 with: name: linux-binary path: dist/ - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Registry if: ${{ env.REGISTRY != '' }} uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Extract metadata for Docker id: meta run: | if [ "${{ github.ref_type }}" = "tag" ]; then echo "tags=${{ env.IMAGE_NAME }}:${{ github.ref_name }},${{ env.IMAGE_NAME }}:latest" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/main" ]; then echo "tags=${{ env.IMAGE_NAME }}:latest,${{ env.IMAGE_NAME }}:$(date +%Y%m%d)" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/development" ]; then echo "tags=${{ env.IMAGE_NAME }}:development,${{ env.IMAGE_NAME }}:$(date +%Y%m%d)" >> $GITHUB_OUTPUT else echo "tags=${{ env.IMAGE_NAME }}:pr-$(echo ${{ github.ref }} | sed 's/refs\/heads\///' | tr '/' '-')" >> $GITHUB_OUTPUT fi - name: Build Docker image uses: docker/build-push-action@v4 with: context: . file: Dockerfile.linux platforms: linux/amd64 build-args: | BINARY_NAME=${{ needs.detect-project.outputs.project-name }} tags: ${{ steps.meta.outputs.tags }} push: ${{ env.REGISTRY != '' }} load: ${{ env.REGISTRY == '' }} summary: name: Workflow Summary needs: [test, audit, build, docker-build] if: always() runs-on: ubuntu-latest steps: - name: Create summary run: | echo "# 🚀 Build Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "## 📊 Job Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY echo "| Project Detection | ${{ needs.detect-project.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Tests | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Security Audit | ${{ needs.audit.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Build | ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Docker Build | ${{ needs.docker-build.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ "${{ needs.test.result }}" == "success" ] && [ "${{ needs.audit.result }}" == "success" ] && [ "${{ needs.build.result }}" == "success" ]; then echo "✅ All checks passed!" >> $GITHUB_STEP_SUMMARY else echo "❌ Some checks failed. Please review the logs." >> $GITHUB_STEP_SUMMARY fi