Files
2026-07-13 13:22:34 +08:00

325 lines
14 KiB
YAML

# See .github/workflows/autoformat.md for instructions on how to test this workflow.
name: Autoformat
on:
issue_comment:
types: [created]
defaults:
run:
shell: bash
permissions: {}
jobs:
check-comment:
runs-on: ubuntu-slim
timeout-minutes: 10
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/autoformat')
permissions:
statuses: write # autoformat.createStatus
pull-requests: write # autoformat.createReaction on PRs
outputs:
should_autoformat: ${{ fromJSON(steps.judge.outputs.result).shouldAutoformat }}
repository: ${{ fromJSON(steps.judge.outputs.result).repository }}
head_ref: ${{ fromJSON(steps.judge.outputs.result).head_ref }}
head_sha: ${{ fromJSON(steps.judge.outputs.result).head_sha }}
base_ref: ${{ fromJSON(steps.judge.outputs.result).base_ref }}
base_sha: ${{ fromJSON(steps.judge.outputs.result).base_sha }}
base_repo: ${{ fromJSON(steps.judge.outputs.result).base_repo }}
pull_number: ${{ fromJSON(steps.judge.outputs.result).pull_number }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
.github
- name: judge
id: judge
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
retries: 3
script: |
core.debug(JSON.stringify(context, null, 2));
const autoformat = require('./.github/workflows/autoformat.js');
const { comment } = context.payload;
const shouldAutoformat = autoformat.shouldAutoformat(comment);
if (shouldAutoformat) {
await autoformat.validatePermissions(context, github);
await autoformat.createReaction(context, github);
await autoformat.createStatus(context, github, core);
}
const pullInfo = await autoformat.getPullInfo(context, github);
return { ...pullInfo, shouldAutoformat };
- name: Check maintainer access
if: fromJSON(steps.judge.outputs.result).shouldAutoformat
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
retries: 3
script: |
const autoformat = require('./.github/workflows/autoformat.js');
await autoformat.checkMaintainerAccess(context, github);
format:
runs-on: ubuntu-latest
timeout-minutes: 30
needs: check-comment
if: needs.check-comment.outputs.should_autoformat == 'true'
permissions:
pull-requests: read # view files modified in PR
outputs:
reformatted: ${{ steps.patch.outputs.reformatted }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
repository: ${{ needs.check-comment.outputs.repository }}
ref: ${{ needs.check-comment.outputs.head_ref }}
# Set fetch-depth to merge the base branch
fetch-depth: 100
- name: Verify head SHA
env:
EXPECTED_SHA: ${{ needs.check-comment.outputs.head_sha }}
run: |
actual_sha="$(git rev-parse HEAD)"
if [[ "$actual_sha" != "$EXPECTED_SHA" ]]; then
echo "::error::HEAD has changed since the /autoformat comment (expected $EXPECTED_SHA, got $actual_sha). Please re-comment /autoformat."
exit 1
fi
- name: Check diff
id: diff
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PULL_NUMBER: ${{ needs.check-comment.outputs.pull_number }}
run: |
changed_files="$(gh pr view --repo $REPO $PULL_NUMBER --json files --jq '.files.[].path')"
protos=$([[ -z $(echo "$changed_files" | grep '^\(mlflow/protos\|tests/protos\)') ]] && echo "false" || echo "true")
js=$([[ -z $(echo "$changed_files" | grep '^mlflow/server/js') ]] && echo "false" || echo "true")
docs=$([[ -z $(echo "$changed_files" | grep '^docs/') ]] && echo "false" || echo "true")
r=$([[ -z $(echo "$changed_files" | grep '^mlflow/R/mlflow') ]] && echo "false" || echo "true")
db=$([[ -z $(echo "$changed_files" | grep '^mlflow/store/db_migrations/') ]] && echo "false" || echo "true")
api=$([[ -z $(echo "$changed_files" | grep -E '(^mlflow/.*\.py$|^docs/api_reference/.*\.rst$)') ]] && echo "false" || echo "true")
echo "protos=$protos" >> $GITHUB_OUTPUT
echo "js=$js" >> $GITHUB_OUTPUT
echo "docs=$docs" >> $GITHUB_OUTPUT
echo "r=$r" >> $GITHUB_OUTPUT
echo "db=$db" >> $GITHUB_OUTPUT
echo "api=$api" >> $GITHUB_OUTPUT
# Merge the base branch (which is usually master) to apply formatting using the latest configurations.
- name: Merge base branch
env:
BASE_REPO: ${{ needs.check-comment.outputs.base_repo }}
BASE_REF: ${{ needs.check-comment.outputs.base_ref }}
run: |
# This identity is only used for the temporary merge commit and is not pushed.
git config user.name 'name'
git config user.email 'email'
git remote add base https://github.com/$BASE_REPO.git
git fetch base $BASE_REF
git merge base/$BASE_REF
- uses: ./.github/actions/setup-python
# ************************************************************************
# pre-commit
# ************************************************************************
- run: |
uv run --only-group lint pre-commit install --install-hooks
uv run --only-group lint pre-commit run install-bin -a -v
uv run --only-group lint pre-commit run --all-files --color=always || true
# ************************************************************************
# protos
# ************************************************************************
- if: steps.diff.outputs.protos == 'true'
env:
DOCKER_BUILDKIT: 1
run: |
# Run the script multiple times. The changes generated by the first run
# may trigger additional changes, which need to be applied in subsequent runs.
for i in {1..3}; do
./dev/generate-protos.sh
done
# ************************************************************************
# DB
# ************************************************************************
- if: steps.diff.outputs.db == 'true'
run: |
tests/db/update_schemas.sh
# ************************************************************************
# js
# ************************************************************************
- if: steps.diff.outputs.js == 'true'
uses: ./.github/actions/setup-node
- if: steps.diff.outputs.js == 'true'
working-directory: mlflow/server/js
run: |
yarn install --immutable
- if: steps.diff.outputs.js == 'true'
working-directory: mlflow/server/js
run: |
yarn lint:fix
yarn prettier:fix
- if: steps.diff.outputs.js == 'true'
working-directory: mlflow/server/js
run: |
yarn i18n
- if: steps.diff.outputs.docs == 'true'
working-directory: docs
run: |
npm ci
- if: steps.diff.outputs.docs == 'true'
working-directory: docs
run: |
npm run prettier:fix
# ************************************************************************
# R
# ************************************************************************
- if: steps.diff.outputs.r == 'true'
working-directory: docs/api_reference
run: |
./build-rdoc.sh
# ************************************************************************
# API Reference
# ************************************************************************
- if: steps.diff.outputs.api == 'true'
run: |
uv sync --group docs --extra gateway
uv pip install -r requirements/torch.txt
uv run --directory docs/api_reference make dummy
# ************************************************************************
# Upload patch
# ************************************************************************
- name: Create patch
id: patch
env:
RUN_ID: ${{ github.run_id }}
run: |
git add -N .
git diff > $RUN_ID.diff
reformatted=$([[ -s $RUN_ID.diff ]] && echo "true" || echo "false")
echo "reformatted=$reformatted" >> $GITHUB_OUTPUT
- name: Upload patch
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ github.run_id }}.diff
path: ${{ github.run_id }}.diff
retention-days: 1
if-no-files-found: ignore
push:
runs-on: ubuntu-slim
timeout-minutes: 5
needs: [check-comment, format]
if: needs.format.outputs.reformatted == 'true'
permissions:
contents: read
outputs:
head_sha: ${{ steps.push.outputs.head_sha }}
steps:
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
id: app-token
with:
client-id: ${{ secrets.APP_CLIENT_ID }}
# See https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps
# for how to rotate the private key
private-key: ${{ secrets.APP_PRIVATE_KEY }}
permission-contents: write
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: true
repository: ${{ needs.check-comment.outputs.repository }}
ref: ${{ needs.check-comment.outputs.head_ref }}
# Set fetch-depth to merge the base branch
fetch-depth: 100
# As reported in https://github.com/orgs/community/discussions/25702, if an action pushes
# code using `GITHUB_TOKEN`, that won't trigger new workflow runs on the PR.
# A personal access token is required to trigger new workflow runs.
token: ${{ steps.app-token.outputs.token }}
- name: Verify head SHA
env:
EXPECTED_SHA: ${{ needs.check-comment.outputs.head_sha }}
run: |
actual_sha="$(git rev-parse HEAD)"
if [[ "$actual_sha" != "$EXPECTED_SHA" ]]; then
echo "::error::HEAD has changed since the /autoformat comment (expected $EXPECTED_SHA, got $actual_sha). Please re-comment /autoformat."
exit 1
fi
- name: Configure git
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ needs.check-comment.outputs.pull_number }}
REPO: ${{ github.repository }}
run: |
AUTHOR=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json author --jq '.author')
IS_BOT=$(echo "$AUTHOR" | jq -r '.is_bot')
if [ "$IS_BOT" = "false" ]; then
LOGIN=$(echo "$AUTHOR" | jq -r '.login')
else
LOGIN="mlflow-app[bot]"
fi
ID=$(gh api "users/$LOGIN" --jq '.id')
git config user.name "$LOGIN"
git config user.email "${ID}+${LOGIN}@users.noreply.github.com"
- name: Merge base branch
env:
BASE_REPO: ${{ needs.check-comment.outputs.base_repo }}
BASE_REF: ${{ needs.check-comment.outputs.base_ref }}
run: |
git remote add base https://github.com/${BASE_REPO}.git
git fetch base $BASE_REF
git merge base/${BASE_REF}
- name: Download patch
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ github.run_id }}.diff
path: /tmp
- name: Apply patch and push
id: push
env:
RUN_ID: ${{ github.run_id }}
REPOSITORY: ${{ github.repository }}
run: |
git apply /tmp/${RUN_ID}.diff
git add .
git commit -sm "Autoformat: https://github.com/${REPOSITORY}/actions/runs/${RUN_ID}"
echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
git push
update-status:
runs-on: ubuntu-slim
timeout-minutes: 10
needs: [check-comment, format, push]
if: always() && needs.check-comment.outputs.should_autoformat == 'true'
permissions:
statuses: write # To update check statuses
actions: write # To approve workflow runs
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
.github
- name: Update status
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
NEEDS_JSON: ${{ toJson(needs) }}
HEAD_SHA: ${{ needs.check-comment.outputs.head_sha }}
PUSH_HEAD_SHA: ${{ needs.push.outputs.head_sha }}
with:
retries: 3
script: |
const needs = JSON.parse(process.env.NEEDS_JSON);
const head_sha = process.env.HEAD_SHA;
const autoformat = require('./.github/workflows/autoformat.js');
const push_head_sha = process.env.PUSH_HEAD_SHA;
if (push_head_sha) {
await autoformat.approveWorkflowRuns(context, github, push_head_sha);
}
await autoformat.updateStatus(context, github, head_sha, needs);