325 lines
14 KiB
YAML
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);
|