Skip to content

Instantly share code, notes, and snippets.

@tinder-maxwellelliott
Last active April 30, 2024 12:06
Show Gist options
  • Save tinder-maxwellelliott/f223d4892e27d43a95038da5af4a4294 to your computer and use it in GitHub Desktop.
Save tinder-maxwellelliott/f223d4892e27d43a95038da5af4a4294 to your computer and use it in GitHub Desktop.
A pseudo code example of an approach to doing Bazel Target Diffing
func main() {
// convertToBazelSourceTargets should take a filepath in your repo and return a bazel source target path
// Use bazel query to do this
let modifiedFileSourceTargets = convertToBazelSourceTargets(system("git diff --name-only HEAD^ HEAD"))
let initialTargetShas = createTargetShas()
system("git checkout HEAD^")
let finalTargetShas = createTargetShas()
system("git checkout -")
var modifiedTargets: [String] = []
for target, value in finalTargetShas {
if initialTargetShas[target] != value || initialTargetShas[target] == nil {
modifiedTargets.append(target)
}
}
var targetsToTest: Set<String> = .init()
for modifiedTarget in modifiedTargets {
// queryRDepsForTarget: Run a bazel rdeps query on the target and return an array of targets
targetsToTest = targetsToTest.union(queryRDepsForTarget(modifiedTarget))
}
let query = targetsToTest.joined(" ")
system("bazel test \(query)")
}
private func createTargetShas(modifiedFileSourceTargets: [String]) -> [String: String] {
let protoFileName = UUID() + ".proto"
let queryCommand = "bazel query \"'//external:all-targets' + '//...:all-targets'\" --output proto > \(protoFileName)"
system(queryCommand)
// You will need Proto models for this, find them here https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/build.proto
let bazelQuery = BlazeQuery_Rule(serializedData: Data(protoFileName))
var targetShas[String: String] = [:]
let targets = bazelQuery.targets.filter{ $0.hasRule }
for target in targets {
targetShas[target.rule.name] = createShaForRule(target.rule,
modifiedFileSourceTargets: modifiedFileSourceTargets).hexString
}
defer { system("rm \(protoFileName)") }
return targetShas
}
private func createShaForRule(rule: Blaze_Rule,
modifiedFileSourceTargets: [String]) -> SHA256.Digest {
var sha: SHA256 = .init()
sha.update(rule.ruleClass.data(using: .utf8))
sha.update(rule.name.data(using: .utf8))
sha.update(rule.skylarkEnvironmentHashCode.data(using: .utf8))
for attribute in rule.attribute {
sha.update(attribute.serializedData)
}
for ruleInput in rule.ruleInputs {
// Use hashmaps to implement isRule
if ruleInput.isRule {
sha.update(createShaForRule(rule: ruleInput).asData)
} else if let modifiedPath = modifiedFileSourceTargets.first?(ruleInput) {
// RuleInputs always contain the set of source file targets needed for a rule, we will
// use this to cross reference the modified files from Git and read the data to create
// Shas for the modified file targets
sha.update(Data(modifiedPath))
}
}
sha.finalize()
}
@thundergolfer
Copy link

Thanks for posting this. I've only taken a quick look through the code (and don't know Swift), but I'm wondering if this supports the case where a file/target has been deleted and thus won't match to anything in a bazel query?

@tinder-maxwellelliott
Copy link
Author

Thanks for posting this. I've only taken a quick look through the code (and don't know Swift), but I'm wondering if this supports the case where a file/target has been deleted and thus won't match to anything in a bazel query?

Yes this will work for files that are deleted or moved, I just tested it now

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