diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 3df77615c..000000000 --- a/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ -service_name: travis-ci -repo_token: yRccoqBMWwHa9gryKQ3BVIEN6l7RMGlFo diff --git a/.flake8 b/.flake8 index bcb8ec51d..a3dd005e8 100644 --- a/.flake8 +++ b/.flake8 @@ -3,3 +3,4 @@ ignore = E501, F403, C901, F601, W503 max-line-length = 79 max-complexity = 18 select = B,C,E,F,W,T4,B9 +exclude = .git,docs/,env/,venv/,.venv,.venv39 diff --git a/.github/CI_CD.md b/.github/CI_CD.md new file mode 100644 index 000000000..9901aa2b7 --- /dev/null +++ b/.github/CI_CD.md @@ -0,0 +1,36 @@ +# CI/CD and Release + +This repository uses [GitHub Actions](https://github.com/Cloud-CV/evalai-cli/actions) for continuous integration and package publishing. + +## Workflows + +| Workflow | Purpose | +|----------|---------| +| `ci-cd.yml` | Lint, test, and publish packages | +| `upload-coverage.yml` | Upload test coverage to Codecov after CI succeeds | + +## Required repository secrets + +Configure these under **Settings → Secrets and variables → Actions**: + +| Secret | Used for | +|--------|----------| +| `CODECOV_TOKEN` | Codecov upload | +| `TEST_PYPI_USERNAME` | TestPyPI publish on `staging` pushes | +| `TEST_PYPI_PASSWORD` | TestPyPI publish on `staging` pushes | +| `PYPI_API_TOKEN` | Production PyPI publish on tag pushes | + +## GitHub environments + +Create optional environments for publish job scoping and approvals: + +- `staging` — TestPyPI publishes from the `staging` branch +- `production` — PyPI publishes from git tags + +## Release process + +1. Bump the version in `evalai/version.py`. +2. Push to `staging` to publish a test build to [TestPyPI](https://test.pypi.org/project/evalai/). +3. Create and push a git tag to publish to [PyPI](https://pypi.org/project/evalai/). + +CI runs on pull requests and pushes to `master` and `staging` using Python 3.9.21 (aligned with the main EvalAI repository). diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 000000000..801788c5b --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,179 @@ +name: ci-cd + +on: + push: + branches: + - master + - staging + tags: + - "*" + pull_request: + workflow_dispatch: + inputs: + publish_target: + description: "Optional manual publish target" + required: true + type: choice + options: + - none + - testpypi + default: none + +concurrency: + group: ci-cd-${{ github.ref }} + cancel-in-progress: true + +env: + PYTHON_RUNTIME_VERSION: "3.9.21" + +jobs: + code_quality: + name: Code quality + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_RUNTIME_VERSION }} + cache: pip + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install flake8==7.1.2 + + - name: Run flake8 + run: flake8 ./ + + test: + name: Tests + runs-on: ubuntu-latest + permissions: + contents: read + actions: write + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_RUNTIME_VERSION }} + cache: pip + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools==70.0.0 + pip install -r requirements.txt + pip install \ + coverage \ + pytest==3.5.1 \ + pytest-cov==2.5.1 \ + pytest-env==0.6.2 \ + responses==0.9.0 + + - name: Run tests with coverage + run: | + pytest \ + --cov evalai \ + --cov-config .coveragerc \ + --cov-report=xml:coverage.xml + + - name: Save pull request number for downstream coverage upload + run: | + mkdir -p pr-metadata + echo "${{ github.event.pull_request.number }}" > pr-metadata/pr_number.txt + + - name: Upload test and coverage artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ci-test-reports + path: coverage.xml + if-no-files-found: error + + - name: Upload pull request metadata artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: pr-metadata + path: pr-metadata/pr_number.txt + if-no-files-found: error + + publish_to_testpypi: + name: Publish to TestPyPI + runs-on: ubuntu-latest + needs: + - code_quality + - test + if: > + (github.event_name == 'push' && github.ref == 'refs/heads/staging') || + (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'testpypi') + environment: staging + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_RUNTIME_VERSION }} + cache: pip + + - name: Build and publish package to TestPyPI + env: + TWINE_USERNAME: ${{ secrets.TEST_PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_PASSWORD }} + run: | + python -m pip install --upgrade pip setuptools wheel twine + python setup.py sdist bdist_wheel + python -m twine upload \ + --repository-url https://test.pypi.org/legacy/ \ + dist/* + + publish_to_pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: + - code_quality + - test + if: startsWith(github.ref, 'refs/tags/') + environment: production + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ env.PYTHON_RUNTIME_VERSION }} + cache: pip + + - name: Build and publish package to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + python -m pip install --upgrade pip setuptools wheel twine + python setup.py sdist bdist_wheel + python -m twine upload dist/* diff --git a/.github/workflows/upload-coverage.yml b/.github/workflows/upload-coverage.yml new file mode 100644 index 000000000..b659412c8 --- /dev/null +++ b/.github/workflows/upload-coverage.yml @@ -0,0 +1,87 @@ +name: upload-coverage + +# Runs after ci-cd completes — in the BASE repo's security context so +# CODECOV_TOKEN is always available, including for fork PRs. +on: + workflow_run: + workflows: [ci-cd] + types: [completed] + +permissions: + contents: read + actions: read + statuses: write + +jobs: + upload_coverage: + name: Upload coverage to Codecov + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + + steps: + - name: Checkout repository (Codecov CLI reads codecov.yml from default branch) + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 1 + persist-credentials: false + + - name: Download pull request metadata from triggering run + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: pr-metadata + path: pr-metadata + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Download test coverage artifact from triggering run + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: ci-test-reports + path: codecov-reports + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Resolve pull request number + id: pr + run: | + pr_number="$(tr -d '[:space:]' < pr-metadata/pr_number.txt || true)" + echo "number=${pr_number}" >> "$GITHUB_OUTPUT" + + - name: Verify coverage file exists + run: test -f codecov-reports/coverage.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: codecov-reports/coverage.xml + flags: cli + disable_search: true + fail_ci_if_error: true + verbose: true + override_commit: ${{ github.event.workflow_run.head_sha }} + override_branch: ${{ github.event.workflow_run.head_branch }} + override_pr: ${{ steps.pr.outputs.number }} + + - name: Report coverage upload status to triggering commit + if: always() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SHA: ${{ github.event.workflow_run.head_sha }} + CONCLUSION: ${{ job.status }} + run: | + if [ "${CONCLUSION}" = "success" ]; then + state="success" + description="Coverage uploaded to Codecov successfully." + else + state="failure" + description="Coverage upload to Codecov failed." + fi + gh api \ + --method POST \ + "/repos/${{ github.repository }}/statuses/${SHA}" \ + --field state="${state}" \ + --field description="${description}" \ + --field context="upload-coverage / Codecov" \ + --field target_url="https://app.codecov.io/gh/${{ github.repository }}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5939ad636..fc95d1a3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: rev: stable hooks: - id: black - language_version: python3.7 + language_version: python3.9 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v1.2.3 hooks: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 156a2405e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -dist: jammy -language: python -sudo: false -python: -- '3.8' -install: -- pip install -r requirements.txt -- pip install coverage -- pip install flake8 pytest PyYAML -- pip install coveralls -- pip install setuptools==70.0.0 -script: -- flake8 ./ || travis_terminate 1; -- python setup.py test || travis_terminate 1; -after_success: -- coveralls --rcfile=.coveragerc -- "./deploy-staging.sh" -notifications: - email: - on_success: change - on_failure: always - slack: cloudcv:gy3CGQGNXLwXOqVyzXGZfdea -deploy: - provider: pypi - user: cloudcv - password: - secure: RiELCr5IMQayDlvwkHh18spa/20xO3T5a8XCEr/ZrY27AygTaFICR8zjKVdbPXRfn/uPtgRRlKJaIdQLpmHnDjmGbNNQo8Ga5E3N89sPcGjHSM7PKPcwSUu8Gwgk70YjXEnos/Bz4u/12kJ2UrgQZHlPpl0ySZRFjk8sqxwQbcxqM8lO1Wu+lGvuCufzllLDcC/Fz1OQjuXUuete/W1RHtDZD8khnKBa5p9P6c0H6AZI9+uvzCsA9qeWFqIiU9TuLja4jb9EcZdtXDDdKTbSHvft6/4JqzQBbsk5x31q5qjG8JFxh7DBrKSv3KXn5XQ7vXQOC7q4RJYDcxFrDBH3hrbO/wkRh/1qHcjzlACGQAUzECnmh+dkr1u9gnUdkC1ti78jkiC4ICA3YfCm9VDYUPSYfOgIXj2Fbv5FfJnggItO+8CLI/vnsu5MqSp/fmrjugO++lo3BC/b6/gonkt3HvyfX2DN7yMG0OMPpr2/5o+NDJekcpKg5VLAv7y3/I8hSj2LMIJ8zV9oAJAxA/qAPuOupxBTjn1+GeiR8Erx7Jak4BRm426JtvICU4h5Zn+d6Vcr1q12OwcZrOUJiBsFhfZebgUmctZjDtsJQwtysF7fovVsT6pXrTGLFgxgkxzXdn/+pyC9bwdKEdw+vbdEjxGasRtijS9BIa676ZFMRsM= - distributions: sdist bdist_wheel - on: - tags: true diff --git a/README.md b/README.md index 5b5c9ff9a..cadb9cc53 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,9 @@ EvalAI-CLI is designed to extend the functionality of the EvalAI web application ------------------------------------------------------------------------------------------ [![Join the chat at https://gitter.im/Cloud-CV/EvalAI](https://badges.gitter.im/Cloud-CV/EvalAI.svg)](https://gitter.im/Cloud-CV/EvalAI?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/Cloud-CV/evalai-cli.svg?branch=master)](https://travis-ci.org/Cloud-CV/evalai-cli) -[![Coverage Status](https://coveralls.io/repos/github/Cloud-CV/evalai-cli/badge.svg?branch=master)](https://coveralls.io/github/Cloud-CV/evalai-cli?branch=master) +[![CI/CD](https://github.com/Cloud-CV/evalai-cli/actions/workflows/ci-cd.yml/badge.svg?branch=master)](https://github.com/Cloud-CV/evalai-cli/actions/workflows/ci-cd.yml) +[![CodeRabbit Reviews](https://img.shields.io/coderabbit/prs/github/Cloud-CV/evalai-cli?style=flat-square)](https://coderabbit.ai) +[![codecov](https://codecov.io/gh/Cloud-CV/evalai-cli/branch/master/graph/badge.svg)](https://codecov.io/gh/Cloud-CV/evalai-cli) [![Documentation Status](https://readthedocs.org/projects/markdown-guide/badge/?version=latest)](https://cli.eval.ai/) ## Installation diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..fcaadac5a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,40 @@ +codecov: + branch: master + strict_yaml_branch: master + +coverage: + precision: 2 + round: down + range: "70...100" + ignore: + - "*/python?.?/*" + - "*/tests/*" + - "setup.py" + status: + project: + default: + target: 70% + threshold: 1% + flags: + - cli + if_ci_failed: error + informational: false + patch: + default: + target: 80% + threshold: 0% + if_ci_failed: error + only_pulls: true + +flags: + cli: + paths: + - evalai/ + +comment: + layout: "header, diff, flags, files, footer" + behavior: default + require_head: yes + +github_checks: + annotations: true diff --git a/deploy-staging.sh b/deploy-staging.sh deleted file mode 100755 index 8b29fdcef..000000000 --- a/deploy-staging.sh +++ /dev/null @@ -1,17 +0,0 @@ -build_and_push() { - python3 -m pip install --upgrade setuptools wheel twine keyring==21.4.0 - python3 setup.py sdist bdist_wheel - python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* -u ${TEST_PYPI_USERNAME} -p ${TEST_PYPI_PASSWORD} - -} - -if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then - echo "Skipping deploy to https://test-pypi.org; The commit is not on staging branch" - exit 0 -elif [ "${TRAVIS_BRANCH}" == "staging" ]; then - build_and_push $TRAVIS_BRANCH - exit 0 -else - echo "Skipping deploy!" - exit 0 -fi diff --git a/setup.py b/setup.py index 862118f8a..271df3ae8 100644 --- a/setup.py +++ b/setup.py @@ -34,8 +34,7 @@ def run_tests(self): tests_require = [ "coverage", - "coveralls==1.3.0", - "flake8==3.0.4", + "flake8==7.1.2", "pytest==3.5.1", "pytest-cov==2.5.1", "pytest-env==0.6.2",