Skip to content

tools: introduce git-change-exec tool - #4202

Draft
christoph-zededa wants to merge 7 commits into
lf-edge:masterfrom
christoph-zededa:git_change_detect
Draft

tools: introduce git-change-exec tool#4202
christoph-zededa wants to merge 7 commits into
lf-edge:masterfrom
christoph-zededa:git_change_detect

Conversation

@christoph-zededa

Copy link
Copy Markdown
Contributor

This new tool detects if in your git tree changed files (compared to master branch and local-only changed files) and uses this information to run only the specified actions.

Here it is used to run pillar's go-tests only if something changed there, same for this tool itself and the get-deps tool.

@christoph-zededa

Copy link
Copy Markdown
Contributor Author

No, this is not possible with what gh checks out here, so closing ...

@christoph-zededa
christoph-zededa force-pushed the git_change_detect branch 4 times, most recently from a9877ab to 575f3f0 Compare September 3, 2024 18:19
@christoph-zededa

Copy link
Copy Markdown
Contributor Author

No, this is not possible with what gh checks out here, so closing ...

hmm, seems it is possible, but I don't know what I am doing with those gh actions ...

@OhmSpectator

Copy link
Copy Markdown
Member

Do you know how you're going to use it? Will developers use it for local runs, or will you make it part of CI?

@christoph-zededa

Copy link
Copy Markdown
Contributor Author

Do you know how you're going to use it? Will developers use it for local runs, or will you make it part of CI?

It is used by make test which is also used by the CI.
So the answer is: both

@deitch

deitch commented Sep 4, 2024

Copy link
Copy Markdown
Contributor

I am a little bit confused. Is this tool's purpose to check which directories have changed files, and thus determine where we should run go test? For example, if pkg/a had changes and pkg/b did not, then run go test ./pkg/a as opposed to go test ./...?

Does it actually execute the go test, or just give the relevant paths, sort of like:

$ gce ./...
pkg/a
pkg/c
pkg/q

thus telling us which dirs changed? And we always can choose to ignore it and run all tests? And with it, I would run go test $(gce ./...)? Something like that?

A README would help, along with some sample of what it would look like when run.

@OhmSpectator

Copy link
Copy Markdown
Member

Aha! I've got the idea. I like it. Good luck with the implementation =) I find it easier to write such tools in interpreted languages, but okay, let it be =)

@christoph-zededa

christoph-zededa commented Sep 4, 2024

Copy link
Copy Markdown
Contributor Author

I am a little bit confused. Is this tool's purpose to check which directories have changed files, and thus determine where we should run go test? For example, if pkg/a had changes and pkg/b did not, then run go test ./pkg/a as opposed to go test ./...?

Does it actually execute the go test, or just give the relevant paths, sort of like:

$ gce ./...
pkg/a
pkg/c
pkg/q

thus telling us which dirs changed? And we always can choose to ignore it and run all tests? And with it, I would run go test $(gce ./...)? Something like that?

A README would help, along with some sample of what it would look like when run.

It does not do magic, it just looks in actions.go what to do exactly, f.e.: https://github.com/lf-edge/eve/pull/4202/files#diff-5bf19742ee6060707f406c1625c23226124e161d2049d12a80a8a61ef076ace8R47

Let me just be lazy with the example: https://github.com/lf-edge/eve/actions/runs/10699251983/job/29660387351?pr=4202#step:3:120 :-)

@christoph-zededa
christoph-zededa force-pushed the git_change_detect branch 5 times, most recently from 6a6c964 to ea1bf18 Compare September 4, 2024 10:48
@christoph-zededa
christoph-zededa marked this pull request as ready for review September 4, 2024 11:35
Comment thread tools/git-change-exec/Makefile Outdated
Comment thread tools/git-change-exec/main.go Outdated
return dir
}

dir = filepath.Join(dir, "..")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I remember the case when Eden removed my home dir because it accessed something in .., and I'm afraid of such patterns now. Could you consider using git rev-parse --show-toplevel instead?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Doesn't git rev-parse --show-toplevel just do the same?

45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/agentlog", {st_mode=S_IFDIR|0755, st_size=226, ...}, 0) = 0
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/agentlog/.git", 0x7ffe67413ec0, 0) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/agentlog/.git/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/agentlog/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar", {st_mode=S_IFDIR|0755, st_size=1204, ...}, 0) = 0
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/.git", 0x7ffe67413ec0, 0) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/.git/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/pillar/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg", {st_mode=S_IFDIR|0755, st_size=804, ...}, 0) = 0
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/.git", 0x7ffe67413ec0, 0) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/.git/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/pkg/HEAD", 0x7ffe67413d70, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve", {st_mode=S_IFDIR|0755, st_size=824, ...}, 0) = 0
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/.git", {st_mode=S_IFDIR|0755, st_size=240, ...}, 0) = 0
45077 newfstatat(AT_FDCWD, "/home/christoph/projects/eve/.git/HEAD", {st_mode=S_IFREG|0644, st_size=49, ...}, AT_SYMLINK_NOFOLLOW) = 0

https://github.com/go-git/go-git ?

I am not sure what you mean.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm afraid of manually adding logic to reverse directories. If there is a tool that does it for us, I would prefer it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yay, thanks! Will take a look at the rest of the script later...

@christoph-zededa
christoph-zededa marked this pull request as draft November 11, 2024 13:34
@christoph-zededa

Copy link
Copy Markdown
Contributor Author

I didn't have time lately to work on it, so I move it into draft state.

@christoph-zededa

Copy link
Copy Markdown
Contributor Author

Some updates:

  1. Now gce (git-change-exec) does not only provide the path to the changed file, but also:
  • the diff
  • tree-sitter annotations
  1. Actions can now be written in go or in lua
  2. The two steps (match and do) are now less entangled and one can export a json from the first step and load it into the second

Still there are some things I have to make nicer:

  • rethink if we really need 3 binaries
  • the cli parameters (f.e. introduce a debug subcommand)
  • Unify MatchDiff and MatchPath
  • Your input
  • etc.

@christoph-zededa

Copy link
Copy Markdown
Contributor Author

Can we make this more modular by splitting it into a git-change tool and a change-exec tool, with a list of files being the output from the first and the input to the second?

Yes, for example you can run:

$ ./cmd/gc/gc actions/tests/debug/ssh.gce.lua
2025/10/05 12:36:53 Loading /home/christoph/projects/eve-3/tools/git-change-exec/actions/tests/debug/ssh.gce.lua ...
{
	"Actions": {
		"lua:/home/christoph/projects/eve-3/tools/git-change-exec/actions/tests/debug/ssh.gce.lua": [
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 0,
					"Line": "RUN ln -s /usr/src/openssh-${OPENSSH_VERSION} /usr/src/openssh",
					"LineNumber": 79,
					"TypeOfLine": {
						"run_instruction": [
							{
								"ColFrom": 0,
								"ColTo": 62
							}
						],
						"shell_command": [
							{
								"ColFrom": 4,
								"ColTo": 62
							}
						],
						"shell_fragment": [
							{
								"ColFrom": 4,
								"ColTo": 62
							}
						],
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "",
					"LineNumber": 129,
					"TypeOfLine": {
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "FROM build AS test",
					"LineNumber": 130,
					"TypeOfLine": {
						"from_instruction": [
							{
								"ColFrom": 0,
								"ColTo": 18
							}
						],
						"image_alias": [
							{
								"ColFrom": 14,
								"ColTo": 18
							}
						],
						"image_name": [
							{
								"ColFrom": 5,
								"ColTo": 10
							}
						],
						"image_spec": [
							{
								"ColFrom": 5,
								"ColTo": 10
							}
						],
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "COPY collect-info-test /collect-info-test",
					"LineNumber": 132,
					"TypeOfLine": {
						"copy_instruction": [
							{
								"ColFrom": 0,
								"ColTo": 41
							}
						],
						"path": [
							{
								"ColFrom": 5,
								"ColTo": 22
							},
							{
								"ColFrom": 23,
								"ColTo": 41
							}
						],
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "WORKDIR /collect-info-test",
					"LineNumber": 133,
					"TypeOfLine": {
						"path": [
							{
								"ColFrom": 8,
								"ColTo": 26
							}
						],
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						],
						"workdir_instruction": [
							{
								"ColFrom": 0,
								"ColTo": 26
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "RUN go test",
					"LineNumber": 134,
					"TypeOfLine": {
						"run_instruction": [
							{
								"ColFrom": 0,
								"ColTo": 11
							}
						],
						"shell_command": [
							{
								"ColFrom": 4,
								"ColTo": 11
							}
						],
						"shell_fragment": [
							{
								"ColFrom": 4,
								"ColTo": 11
							}
						],
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Dockerfile",
				"Ld": {
					"Operation": 1,
					"Line": "",
					"LineNumber": 135,
					"TypeOfLine": {
						"source_file": [
							{
								"ColFrom": 0,
								"ColTo": 4294967295
							}
						]
					}
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 1,
					"Line": ".PHONY: test",
					"LineNumber": 3,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 1,
					"Line": "test:",
					"LineNumber": 4,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": ".PHONY: test-collect-info test-openssh debug-docker-image-build",
					"LineNumber": 3,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "",
					"LineNumber": 4,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "debug-docker-image-build: Dockerfile",
					"LineNumber": 5,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 1,
					"Line": "\tdocker build --rm --target test .",
					"LineNumber": 6,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "\tdocker build --target build .",
					"LineNumber": 7,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "",
					"LineNumber": 8,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "test-collect-info: debug-docker-image-build",
					"LineNumber": 9,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "\tdocker run -w /collect-info-test -v $(realpath collect-info-test):/collect-info-test  -it $(shell docker build --target build -q .) go test",
					"LineNumber": 10,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "test-ssh: debug-docker-image-build",
					"LineNumber": 11,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "\tdocker run -w /usr/src/openssh -it $(shell docker build --target build -q .) make tests",
					"LineNumber": 12,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "",
					"LineNumber": 13,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "",
					"LineNumber": 14,
					"TypeOfLine": null
				}
			},
			{
				"Path": "pkg/debug/Makefile",
				"Ld": {
					"Operation": 0,
					"Line": "test: test-collect-info test-ssh",
					"LineNumber": 15,
					"TypeOfLine": null
				}
			}
		]
	}
}

This output can be imported into another tool, f.e. this way:

$ ./cmd/gc/gc actions/tests/debug/ssh.gce.lua
2025/10/05 12:40:18 Loading /home/christoph/projects/eve-3/tools/git-change-exec/actions/tests/debug/ssh.gce.lua ...
2025/10/05 12:40:26 Loading /home/christoph/projects/eve-3/tools/git-change-exec/actions/tests/debug/ssh.gce.lua ...
2025/10/05 12:40:26 Running ...
2025/10/05 12:40:33 would run lua:/home/christoph/projects/eve-3/tools/git-change-exec/actions/tests/debug/ssh.gce.lua, but running dry ...

BUT: the actions have to be written in a way so that no state is saved in the first step and will be accessed in the second step.

christoph-zededa and others added 7 commits July 31, 2026 14:54
also allow to use this tool directly with a path
as a parameter

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
The option is called `ignore-words` according to
https://github.com/codespell-project/codespell?tab=readme-ov-file#using-a-config-file

Also add `OptionAll` as is not a misspelled `optionally`

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
add this to have a somehow similar interface as other
parts in the repo to run the tests

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
This new tool detects if in your git tree changed files
(compared to master branch and local-only changed files)
and uses this information to run only the specified actions.

Here it is used to run pillar's go-tests only if something
changed there, same for this tool itself and the get-deps tool.

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
necessary after the recent changes

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
actions/checkout takes the PR head from the contributor's fork, leaving
origin/master stale, fix it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Christoph Ostarek <christoph@zededa.com>
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.

5 participants