# Copyright 2024 RustFS Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Continuous Integration

on:
  push:
    branches: [ main ]
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "deploy/**"
      - "scripts/dev_*.sh"
      - "scripts/probe.sh"
      - "LICENSE*"
      - ".gitignore"
      - ".dockerignore"
      - "README*"
      - "**/*.png"
      - "**/*.jpg"
      - "**/*.svg"
      - ".github/workflows/build.yml"
      - ".github/workflows/docker.yml"
      - ".github/workflows/audit.yml"
      - ".github/workflows/performance.yml"
  pull_request:
    branches: [ main ]
    paths-ignore:
      - "**.md"
      - "docs/**"
      - "deploy/**"
      - "scripts/dev_*.sh"
      - "scripts/probe.sh"
      - "LICENSE*"
      - ".gitignore"
      - ".dockerignore"
      - "README*"
      - "**/*.png"
      - "**/*.jpg"
      - "**/*.svg"
      - ".github/workflows/build.yml"
      - ".github/workflows/docker.yml"
      - ".github/workflows/audit.yml"
      - ".github/workflows/performance.yml"
  merge_group:
    types: [checks_requested]
  schedule:
    - cron: "0 0 * * 0" # Weekly on Sunday at midnight UTC
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1
  CARGO_BUILD_JOBS: 2

jobs:

  skip-check:
    name: Skip Duplicate Actions
    permissions:
      actions: write
      contents: read
    runs-on: ubicloud-standard-2
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - name: Skip duplicate actions
        id: skip_check
        uses: fkirc/skip-duplicate-actions@v5
        with:
          concurrent_skipping: "same_content_newer"
          cancel_others: true
          paths_ignore: '["*.md", "docs/**", "deploy/**"]'
          do_not_skip: '["workflow_dispatch", "schedule", "merge_group", "release", "push"]'

  typos:
    name: Typos
    needs: skip-check
    if: needs.skip-check.outputs.should_skip != 'true'
    runs-on: ubicloud-standard-2
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Typos check with custom config file
        uses: crate-ci/typos@master

  test-and-lint:
    name: Test and Lint
    needs: skip-check
    if: needs.skip-check.outputs.should_skip != 'true'
    runs-on: ubicloud-standard-4
    timeout-minutes: 60
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Rust environment
        uses: ./.github/actions/setup
        with:
          rust-version: stable
          cache-shared-key: ci-test-${{ hashFiles('**/Cargo.lock') }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
          cache-save-if: ${{ github.ref == 'refs/heads/main' }}

      - name: Run tests
        run: |
          cargo nextest run --all --exclude e2e_test
          cargo test --all --doc

      - name: Check code formatting
        run: cargo fmt --all --check

      - name: Run clippy lints
        run: cargo clippy --all-targets --all-features -- -D warnings

      - name: Check layered dependencies
        run: ./scripts/check_layer_dependencies.sh

  build-rustfs-debug-binary:
    name: Build RustFS Debug Binary
    needs: skip-check
    if: needs.skip-check.outputs.should_skip != 'true'
    runs-on: ubicloud-standard-4
    timeout-minutes: 30
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Rust environment
        uses: ./.github/actions/setup
        with:
          rust-version: stable
          cache-shared-key: ci-rustfs-debug-binary-${{ hashFiles('**/Cargo.lock') }}
          cache-save-if: ${{ github.ref == 'refs/heads/main' }}
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Build debug binary
        run: |
          touch rustfs/build.rs
          cargo build -p rustfs --bins --jobs 2

      - name: Upload debug binary
        uses: actions/upload-artifact@v6
        with:
          name: rustfs-debug-binary
          path: target/debug/rustfs
          if-no-files-found: error
          retention-days: 1

  e2e-tests:
    name: End-to-End Tests
    needs: [ skip-check, build-rustfs-debug-binary ]
    if: needs.skip-check.outputs.should_skip != 'true'
    runs-on: ubicloud-standard-2
    timeout-minutes: 30
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Clean up previous test run
        run: |
          rm -rf /tmp/rustfs
          rm -f /tmp/rustfs.log

      - name: Download debug binary
        uses: actions/download-artifact@v7
        with:
          name: rustfs-debug-binary
          path: target/debug

      - name: Make binary executable
        run: chmod +x ./target/debug/rustfs

      - name: Setup Rust toolchain for s3s-e2e installation
        uses: dtolnay/rust-toolchain@stable

      - name: Install s3s-e2e test tool
        uses: taiki-e/cache-cargo-install-action@v2
        with:
          tool: s3s-e2e
          git: https://github.com/s3s-project/s3s.git
          rev: 4a04a670cf41274d9be9ab65dc36f4aa3f92fbad

      - name: Run end-to-end tests
        run: |
          s3s-e2e --version
          ./scripts/e2e-run.sh ./target/debug/rustfs /tmp/rustfs

      - name: Upload test logs
        if: failure()
        uses: actions/upload-artifact@v6
        with:
          name: e2e-test-logs-${{ github.run_number }}
          path: /tmp/rustfs.log
          retention-days: 3

  s3-implemented-tests:
    name: S3 Implemented Tests
    needs: [ skip-check, build-rustfs-debug-binary ]
    if: needs.skip-check.outputs.should_skip != 'true'
    runs-on: ubicloud-standard-4
    timeout-minutes: 60
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Download debug binary
        uses: actions/download-artifact@v7
        with:
          name: rustfs-debug-binary
          path: target/debug

      - name: Make binary executable
        run: chmod +x ./target/debug/rustfs

      - name: Run implemented s3-tests
        run: |
          DEPLOY_MODE=binary \
          RUSTFS_BINARY=./target/debug/rustfs \
          TEST_MODE=single \
          MAXFAIL=1 \
          ./scripts/s3-tests/run.sh

      - name: Upload s3 test artifacts
        if: always()
        uses: actions/upload-artifact@v6
        with:
          name: s3tests-implemented-${{ github.run_number }}
          path: artifacts/s3tests-single/**
          if-no-files-found: ignore
          retention-days: 3
