# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license name: "Ultralytics Actions" author: "Ultralytics" description: "Optimize code and docs with official Ultralytics Actions for syntax, spelling, and link checks." branding: icon: "code" color: "blue" inputs: token: description: "GitHub token" required: true labels: description: "Run issue and PR auto-labeling" required: false default: "false" python: description: "Run Python formatting" required: false default: "false" markdown: description: "Run Markdown formatting (deprecated in favor of prettier)" required: false default: "false" prettier: description: "Run Prettier formatting for JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, HTML, JSON, GraphQL, Markdown, YAML" required: false default: "false" swift: description: "Run Swift formatting" required: false default: "false" dart: description: "Run Dart formatting" required: false default: "false" spelling: description: "Run Spelling checks" required: false default: "false" links: description: "Run Broken Links checks" required: false default: "false" summary: description: "Run PR Summary" required: false default: "false" header: description: "File header" required: false default: "" brave_api_key: description: "Brave API Key" required: false openai_api_key: description: "OpenAI API Key" required: false openai_model: description: "OpenAI Model" required: false default: "gpt-4.1-2025-04-14" first_issue_response: description: "Example response to a new issue" required: false first_pr_response: description: "Example response to a new PR" required: false github_username: description: "GitHub username for commits" required: false default: "UltralyticsAssistant" # default: "github-actions[bot]" github_email: description: "GitHub email for commits" required: false default: "web@ultralytics.com" # default: "41898282+github-actions[bot]@users.noreply.github.com" runs: using: "composite" steps: - uses: astral-sh/setup-uv@v6 with: enable-cache: false - name: Install Dependencies # Note tomli required for codespell with pyproject.toml # sudo env "PATH=$PATH" uv pip install --system --break-system-packages git+https://github.com/ultralytics/actions@main codespell tomli env: INPUTS_SPELLING: ${{ inputs.spelling }} run: | packages="ultralytics-actions" if [ "$INPUTS_SPELLING" = "true" ]; then packages="$packages codespell tomli" fi # On macOS, don't use sudo as it can cause environment issues if [ "$(uname)" = "Darwin" ]; then uv pip install --system --break-system-packages $packages else sudo env "PATH=$PATH" uv pip install --system --break-system-packages $packages fi ultralytics-actions-info shell: bash # Checkout Repository ---------------------------------------------------------------------------------------------- - name: Checkout Repository if: github.event.action != 'closed' uses: actions/checkout@v4 with: repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} token: ${{ inputs.token }} ref: ${{ github.head_ref || github.ref }} # PR Summary ------------------------------------------------------------------------------------------------------- - name: PR Summary if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.summary == 'true' && github.event.action != 'synchronize' env: GITHUB_TOKEN: ${{ inputs.token }} BRAVE_API_KEY: ${{ inputs.brave_api_key }} OPENAI_API_KEY: ${{ inputs.openai_api_key }} OPENAI_MODEL: ${{ inputs.openai_model }} run: | ultralytics-actions-summarize-pr shell: bash continue-on-error: true # File header ------------------------------------------------------------------------------------------------------ - name: File Header if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action != 'closed' env: HEADER: ${{ inputs.header }} run: | ultralytics-actions-headers shell: bash continue-on-error: false # Python formatting ------------------------------------------------------------------------------------------------ # Ignores the following Docs rules to match Google-style docstrings: # D100: Missing docstring in public module # D104: Missing docstring in public package # D203: 1 blank line required before class docstring # D205: 1 blank line required between summary line and description # D212: Multi-line docstring summary should start at the first line # D213: Multi-line docstring summary should start at the second line # D401: First line of docstring should be in imperative mood # D406: Section name should end with a newline # D407: Missing dashed underline after section # D413: Missing blank line after last section # --target-version is Python 3.8 for --extend-select UP (pyupgrade) - name: Run Python if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.python == 'true' && github.event.action != 'closed' run: | ruff format \ --line-length 120 \ . || true ruff check \ --fix \ --unsafe-fixes \ --extend-select I,D,UP \ --target-version py38 \ --ignore D100,D104,D203,D205,D212,D213,D401,D406,D407,D413 \ . docformatter \ --wrap-summaries 120 \ --wrap-descriptions 120 \ --pre-summary-newline \ --close-quotes-on-newline \ --in-place \ --recursive \ . shell: bash continue-on-error: true # Prettier (JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, HTML, JSON, GraphQL, Markdown, YAML) ------------- - name: Run Prettier if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && (inputs.prettier == 'true' || inputs.markdown == 'true') && github.event.action != 'closed' run: | npm install --global prettier prettier-plugin-sh ultralytics-actions-update-markdown-code-blocks npx prettier --write --list-different "**/*.{js,jsx,ts,tsx,css,less,scss,json,yml,yaml,html,vue,svelte}" '!**/*lock.{json,yaml,yml}' '!**/*.lock' '!**/model.json' # Bash file format if find . -name "*.sh" -type f | grep -q .; then npx prettier --write --list-different --plugin=$(npm root -g)/prettier-plugin-sh/lib/index.cjs "**/*.sh" fi # Handle Markdown separately find . -name "*.md" ! -path "*/docs/*" -exec npx prettier --write --list-different {} + if [ -d "./docs" ]; then find ./docs -name "*.md" ! -path "*/reference/*" -exec npx prettier --tab-width 4 --write --list-different {} + fi shell: bash continue-on-error: true # Swift formatting ------------------------------------------------------------------------------------------------- - name: Run Swift Formatter if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.swift == 'true' && github.event.action != 'closed' run: | brew install swift-format swift-format --in-place --recursive . shell: bash continue-on-error: true # Dart formatting -------------------------------------------------------------------------------------------------- - name: Setup Dart SDK if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.dart == 'true' && github.event.action != 'closed' uses: dart-lang/setup-dart@v1 - name: Run Dart Formatter if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.dart == 'true' && github.event.action != 'closed' run: | dart format . shell: bash continue-on-error: true # Important so that commit step can pick up changes # Spelling --------------------------------------------------------------------------------------------------------- - name: Run Codespell if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && inputs.spelling == 'true' && github.event.action != 'closed' run: | codespell \ --write-changes \ --uri-ignore-words-list * \ --ignore-words-list "RepResNet,Idenfy,WIT,Smoot,EHR,ROUGE,ALS,iTerm,Carmel,FPR,Hach,Calle,ore,COO,MOT,crate,nd,ned,strack,dota,ane,segway,fo,gool,winn,commend,bloc,nam,afterall,skelton,goin" \ --skip "*.pt,*.pth,*.torchscript,*.onnx,*.tflite,*.pb,*.bin,*.param,*.mlmodel,*.engine,*.npy,*.data*,*.csv,*pnnx*,*venv*,*translat*,*lock*,__pycache__*,*.ico,*.jpg,*.png,*.mp4,*.mov,/runs,/.git,./docs/??/*.md,./docs/mkdocs_??.yml" shell: bash continue-on-error: true # Autolabel Issues and PRs (run before commit changes in case commit fails) ---------------------------------------- - name: Autolabel Issues and PRs if: inputs.labels == 'true' && (github.event.action == 'opened' || github.event.action == 'created') env: GITHUB_TOKEN: ${{ inputs.token }} FIRST_ISSUE_RESPONSE: ${{ inputs.first_issue_response }} FIRST_PR_RESPONSE: ${{ inputs.first_pr_response }} BRAVE_API_KEY: ${{ inputs.brave_api_key }} OPENAI_API_KEY: ${{ inputs.openai_api_key }} OPENAI_MODEL: ${{ inputs.openai_model }} run: | ultralytics-actions-first-interaction shell: bash continue-on-error: true # Commit Changes --------------------------------------------------------------------------------------------------- - name: Commit and Push Changes if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action != 'closed' env: GITHUB_USERNAME: ${{ inputs.github_username }} GITHUB_EMAIL: ${{ inputs.github_email }} run: | git config --global user.name "$GITHUB_USERNAME" git config --global user.email "$GITHUB_EMAIL" git add . git reset HEAD -- .github/workflows/ # workflow changes are not permitted with default token if ! git diff --staged --quiet; then git commit -m "Auto-format by https://ultralytics.com/actions" git push else echo "No changes to commit" fi shell: bash continue-on-error: true # Broken links ----------------------------------------------------------------------------------------------------- - name: Broken Link Checker if: inputs.links == 'true' && github.event.action != 'closed' uses: lycheeverse/lychee-action@v2.4.1 with: # Check all markdown and html files in repo. Ignores the following status codes to reduce false positives: # - 403(OpenVINO, "forbidden") # - 429(Instagram, "too many requests") # - 500(Zenodo, "cached") # - 502(Zenodo, "bad gateway") # - 999(LinkedIn, "unknown status code") args: | --scheme https --timeout 60 --insecure --accept 403,429,500,502,999 --exclude-all-private --exclude "https?://(www\.)?(github\.com|linkedin\.com|twitter\.com|instagram\.com|kaggle\.com|fonts\.gstatic\.com|url\.com)" "./**/*.md" "./**/*.html" token: ${{ inputs.token }} output: ../lychee/results.md fail: true continue-on-error: false