Skip to content

Conversation

@kanglant
Copy link
Contributor

Add a new GitHub Composite Action to automatically enforce C/C++ code formatting using clang-format in pull requests.

It provides a reusable and standardized action for CI/CD, eliminating the need for individual teams or users to duplicate clang-format setup in their workflows.

@kanglant
Copy link
Contributor Author

kanglant commented May 22, 2025

I've tested this change using a simple workflow that uses this composite action: https://github.com/kanglant/experiments-repo/blob/main/.github/workflows/clang_format.yml.

Three test scenarios:

@MichaelHudgins
Copy link
Collaborator

Additionally please add actions tests to this repo to test the functionality, i see you did some already on your own repo to prove it works. For this repo a minimal set just proving it works would be ideal. We can store files somewhere like tests/(this action name)/* off the root of the repo if necessary.

- name: "Checking out repository"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0 # Fetch full history for accurate diffing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all history or would fetch-depth: 2 work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch-depth: 2 fetches the current commit (HEAD) and its immediate parent commit (HEAD^). If the PR branch diverged from main more than 1 commit, git diff origin/main HEAD will fail.
There are 3 options:

  1. Use fetch-depth: 0. It fetches the entire Git history for all branches and tags.
  2. Use fetch-depth: 2. Add a step to fetch the ${{ github.base_ref }} branch, usually main.
  3. Use fetch-depth: 2 and git diff ... HEAD^ HEAD. Instead of checking all changes in the PR relative to its base branch, we'll be checking only the changes in the last commit of the PR, comparing the current commit (HEAD) against its immediate parent (HEAD^).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you run into this problem? I am mainly pushing more on this so i can update my own understanding of how actions operates here if i am mistaken.

My understanding was in the case of an action running in a PR actions does something somewhat tricky and actually creates a merge commit as the running commit. Which should mean HEAD is the merge commit and its immediate parent is the base branch.

Comment on lines +21 to +23
if: |
github.event.sender.type == 'User' ||
contains(github.event.pull_request.body, 'RUN_CLANG_FORMAT')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have this if check? Shouldn't we want this to always run?

Copy link
Contributor Author

@kanglant kanglant May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a just test workflow. I referred xla's workflow and think this is a good practice as it ensures the workflow runs for user-initiated (not bot-created) PRs and can be explicitly triggered by including RUN_CLANG_FORMAT in the PR description.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we would still want bot created PRs to invoke (dependabot is our only bot at the moment)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if XLA added that case specifically because of copybara being a bot. That would make a big difference in number of executions

Copy link
Contributor Author

@kanglant kanglant May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if XLA added that case specifically because of copybara being a bot. That would make a big difference in number of executions

That's what I thought. TF/XLA have an internal clang-format check, so there's no need to run it on copybara-created PRs.

In this case we would still want bot created PRs to invoke (dependabot is our only bot at the moment)

Okay, let me remove the constraint.

clang_format_version:
description: 'The clang-format version to use.'
required: true
default: "20.1.5"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to sha lock this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep using the version number as uvx attempts to find and download clang-format from PyPI.

uvx clang-format@<version> installs clang-format at a specific version into a temporary isolated environment and invokes it. This is fine as we just need an one-off run of clang-format. Here is the uvx doc. uvx doesn't directly support a --hash flag. If hash-based verification is critical, we'd need to consider a pip compile and installation approach.

Or we can use uvx --from git+https://github.com/ssciwr/clang-format-wheel@<commit sha> clang-format ... to install it from the git repo at a commit, but this would require deps to build the tool.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the minimal set of files we can use for this purpose?

If so we should document where it comes from and how to update it to the live version if necessary

ACTION_DEFAULT_CLANG_FORMAT: ${{ github.action_path }}.clang-format.default
CLANG_FORMAT_VERSION: ${{ inputs.clang_format_version }}
TARGET_BRANCH: ${{ github.base_ref }}
run: |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized but this execution seems only good for PRs.

We should decide what needs done if we run on a push event

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants