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: Install Rust Clippy working-directory: ${{ needs.detect-project.outputs.project-dir }} run: rustup component add clippy - 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 set-tag: name: Set Tag Name runs-on: ubuntu-latest outputs: tag_name: ${{ steps.set_tag.outputs.tag_name }} steps: - name: Determine next semantic version tag id: set_tag run: | git fetch --tags # Find latest tag matching vX.Y.Z latest_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n 1) if [[ -z "$latest_tag" ]]; then major=0 minor=0 patch=0 else version="${latest_tag#v}" IFS='.' read -r major minor patch <<< "$version" fi if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then major=$((major + 1)) minor=0 patch=0 elif [[ "${GITHUB_REF}" == "refs/heads/development" ]]; then minor=$((minor + 1)) patch=0 else patch=$((patch + 1)) fi new_tag="v${major}.${minor}.${patch}" echo "tag_name=${new_tag}" >> $GITHUB_OUTPUT # 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, set-tag] 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 uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.AUTOMATION_USERNAME }} password: ${{ secrets.AUTOMATION_PASSWORD }} - 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: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.set-tag.outputs.tag_name }} push: true tag: name: Create Tag needs: [build, set-tag] if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Git user run: | git config user.name "GitHub Actions" git config user.email "actions@github.com" - name: Create and push tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git tag ${{ needs.set-tag.outputs.tag_name }} git push origin ${{ needs.set-tag.outputs.tag_name }} 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 #test