diff --git a/.github/reusable-steps/setup-os/action.yml b/.github/reusable-steps/setup-os/action.yml new file mode 100644 index 00000000..13a63c95 --- /dev/null +++ b/.github/reusable-steps/setup-os/action.yml @@ -0,0 +1,16 @@ +name: OS setup + +runs: + using: 'composite' + steps: + - name: Install coreutils + if: runner.os == 'macOS' + shell: bash + run: | + brew install coreutils + - name: Install virtual display + if: runner.os == 'Linux' + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y xvfb diff --git a/.github/reusable-steps/setup-python/action.yml b/.github/reusable-steps/setup-python/action.yml new file mode 100644 index 00000000..db182900 --- /dev/null +++ b/.github/reusable-steps/setup-python/action.yml @@ -0,0 +1,29 @@ +name: Python setup + +inputs: + python: + required: true + project: + required: true + +runs: + using: 'composite' + steps: + - name: Download sample video file + shell: bash + run: | + cd ${{ inputs.project }} + curl -L -o sample_video.mp4 https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4 + - name: Set up Python ${{ inputs.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python }} + - name: Install dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install -r ${{ inputs.project }}/requirements.txt + - name: List dependencies + shell: bash + run: | + pip list diff --git a/.github/reusable-steps/timeouted-action/action.yml b/.github/reusable-steps/timeouted-action/action.yml new file mode 100644 index 00000000..bc256dde --- /dev/null +++ b/.github/reusable-steps/timeouted-action/action.yml @@ -0,0 +1,25 @@ +name: Run action with timeout + +inputs: + command: + required: true + project: + required: true + timeout: + required: false + default: 1h + +runs: + using: 'composite' + steps: + - name: Run JS Project + shell: bash + run: | + cd ${{ inputs.project }} + # linux requires a virtual display + if [ "${{ runner.os }}" == "Linux" ]; then + # the timeout trick "gracefully" kills the app after specified time (waiting for user input otherwise) + timeout ${{ inputs.timeout }} xvfb-run ${{ inputs.command }} || [[ $? -eq 124 ]] + else + timeout ${{ inputs.timeout }} ${{ inputs.command }} || [[ $? -eq 124 ]] + fi diff --git a/.github/workflows/sanity-check.yml b/.github/workflows/sanity-check.yml new file mode 100644 index 00000000..d6ea84d8 --- /dev/null +++ b/.github/workflows/sanity-check.yml @@ -0,0 +1,158 @@ +name: Sanity check + +on: + schedule: + - cron: "0 2 * * *" + +permissions: + contents: read + +jobs: + find-subprojects: + runs-on: ubuntu-latest + outputs: + notebook: ${{ steps.categorize-subprojects.outputs.notebook }} + gradio: ${{ steps.categorize-subprojects.outputs.gradio }} + webcam: ${{ steps.categorize-subprojects.outputs.webcam }} + js: ${{ steps.categorize-subprojects.outputs.js }} + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Categorize subprojects + id: categorize-subprojects + run: | + notebook=() + gradio=() + webcam=() + js=() + + for dir in $(find demos -type d -mindepth 1 -maxdepth 1 ! -name utils); do + if [ -f "$dir/package.json" ]; then + js+=("$dir") + elif [ -f "$dir/main.ipynb" ]; then + notebook+=("$dir") + elif grep -q "gradio" "$dir/requirements.txt"; then + gradio+=("$dir") + elif grep -q -- "--stream" "$dir/main.py"; then + webcam+=("$dir") + fi + done + + notebook_json=$(printf '%s\n' "${notebook[@]}" | jq -R -s -c 'split("\n")[:-1]') + gradio_json=$(printf '%s\n' "${gradio[@]}" | jq -R -s -c 'split("\n")[:-1]') + webcam_json=$(printf '%s\n' "${webcam[@]}" | jq -R -s -c 'split("\n")[:-1]') + js_json=$(printf '%s\n' "${js[@]}" | jq -R -s -c 'split("\n")[:-1]') + + echo "notebook=$notebook_json" | tee -a $GITHUB_OUTPUT + echo "gradio=$gradio_json" | tee -a $GITHUB_OUTPUT + echo "webcam=$webcam_json" | tee -a $GITHUB_OUTPUT + echo "js=$js_json" | tee -a $GITHUB_OUTPUT + + notebook: + needs: find-subprojects + if: ${{ needs.find-subprojects.outputs.notebook != '[]' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python: [3.11] + subproject: ${{ fromJson(needs.find-subprojects.outputs.notebook) }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - uses: ./.github/reusable-steps/setup-python + with: + python: ${{ matrix.python }} + project: ${{ matrix.subproject }} + - uses: ./.github/reusable-steps/timeouted-action + name: Run Notebook + with: + command: jupyter nbconvert --to notebook --execute main.ipynb + project: ${{ matrix.subproject }} + + gradio: + needs: find-subprojects + if: ${{ needs.find-subprojects.outputs.gradio != '[]' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python: [3.11] + subproject: ${{ fromJson(needs.find-subprojects.outputs.gradio) }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/reusable-steps/setup-os + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - uses: ./.github/reusable-steps/setup-python + with: + python: ${{ matrix.python }} + project: ${{ matrix.subproject }} + - name: Login to HF + shell: bash + run: | + huggingface-cli login --token ${{ secrets.HF_TOKEN }} + - uses: ./.github/reusable-steps/timeouted-action + name: Run Gradio App + with: + command: python main.py + project: ${{ matrix.subproject }} + timeout: 30m + + webcam: + needs: find-subprojects + if: ${{ needs.find-subprojects.outputs.webcam != '[]' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python: [3.11] + subproject: ${{ fromJson(needs.find-subprojects.outputs.webcam) }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/reusable-steps/setup-os + - uses: ./.github/reusable-steps/setup-python + with: + python: ${{ matrix.python }} + project: ${{ matrix.subproject }} + - uses: ./.github/reusable-steps/timeouted-action + name: Run Webcam Demo + with: + command: python main.py --stream sample_video.mp4 + project: ${{ matrix.subproject }} + + js: + needs: find-subprojects + if: ${{ needs.find-subprojects.outputs.js != '[]' }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + subproject: ${{ fromJson(needs.find-subprojects.outputs.js) }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/reusable-steps/setup-os + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Install dependencies + run: | + cd ${{ matrix.subproject }} + npm install + - uses: ./.github/reusable-steps/timeouted-action + name: Run JS Project + with: + command: npm start + project: ${{ matrix.subproject }} + timeout: 1m