Unnamed repository; edit this file 'description' to name the repository.
feat(helix-loader): add support for SHA256 git repos for grammars (#15466)
The helix-loader is currently running a `git init` prior to fetching objects. This initialize the git repo with the default object format, which currently is SHA1. A subsequent `fetch` will fail if the repo instead uses SHA256. The same issue will pop up when `git init` will change the default to SHA256: A fetch from a SHA1 repo will then fail, as the init was down with SHA256. This adds an explicit object format argument to the rust functions, and extends the TOML format in a backward compatible way: 1. If the revision is prefixed `sha256:`: use SHA256 2. If the revision is prefixed `sha1:`: use SHA1 3. If the revision is a valid SHA256 hash (which is longer than a valid SHA1 hash): use SHA256 4. In any other case: Use SHA1
Marian Buschsieweke 4 weeks ago
parent c1e14a8 · commit 9c61d58
-rw-r--r--helix-loader/src/grammar.rs56
1 files changed, 47 insertions, 9 deletions
diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs
index 4e9f2156..501e9b47 100644
--- a/helix-loader/src/grammar.rs
+++ b/helix-loader/src/grammar.rs
@@ -298,6 +298,37 @@ enum FetchStatus {
NonGit,
}
+#[derive(Copy, Clone)]
+enum GitObjectFormat {
+ Sha1,
+ Sha256,
+}
+
+impl GitObjectFormat {
+ fn as_str(&self) -> &'static str {
+ match self {
+ Self::Sha1 => "sha1",
+ Self::Sha256 => "sha256",
+ }
+ }
+}
+
+fn extract_object_format_from_revision(rev: &str) -> (GitObjectFormat, &str) {
+ if let Some(stripped) = rev.strip_prefix("sha1:") {
+ return (GitObjectFormat::Sha1, stripped);
+ }
+
+ if let Some(stripped) = rev.strip_prefix("sha256:") {
+ return (GitObjectFormat::Sha256, stripped);
+ }
+
+ if rev.len() == 64 && rev.bytes().all(|b| b.is_ascii_hexdigit()) {
+ return (GitObjectFormat::Sha256, rev);
+ }
+
+ (GitObjectFormat::Sha1, rev)
+}
+
struct VendoredGrammar {
dir: PathBuf,
}
@@ -323,8 +354,8 @@ impl VendoredGrammar {
///
/// To ensure clean state, existing grammar directory is removed and re-inited
/// before fetch operation.
- fn fetch(&self, remote: &str, rev: &str) -> Result<()> {
- self.reinit(remote)?;
+ fn fetch(&self, remote: &str, rev: &str, object_format: GitObjectFormat) -> Result<()> {
+ self.reinit(remote, object_format)?;
git(&self.dir, ["fetch", "--depth", "1", REMOTE_NAME, rev])?;
git(&self.dir, ["checkout", rev])?;
@@ -335,7 +366,7 @@ impl VendoredGrammar {
/// Initializes the grammar directory.
///
/// Creates directory and sets it up as a git repo, with remote set correctly.
- fn init(&self, remote: &str) -> Result<()> {
+ fn init(&self, remote: &str, object_format: GitObjectFormat) -> Result<()> {
// Create the grammar directory if needed.
fs::create_dir_all(&self.dir).context(format!(
"Could not create grammar directory {:?}",
@@ -344,7 +375,10 @@ impl VendoredGrammar {
// Ensure directory is git initialized.
if !self.dir.join(".git").exists() {
- git(&self.dir, ["init"])?;
+ git(
+ &self.dir,
+ ["init", "--object-format", object_format.as_str()],
+ )?;
}
// Ensure the remote matches the configured remote, setting if needed.
@@ -356,9 +390,9 @@ impl VendoredGrammar {
}
/// Removes the grammar directory before initializing again.
- fn reinit(&self, remote: &str) -> Result<()> {
+ fn reinit(&self, remote: &str, object_format: GitObjectFormat) -> Result<()> {
fs::remove_dir_all(&self.dir)?;
- self.init(remote)?;
+ self.init(remote, object_format)?;
Ok(())
}
@@ -385,17 +419,21 @@ fn fetch_grammar(grammar: GrammarConfiguration) -> Result<FetchStatus> {
let repo = VendoredGrammar::new(&grammar.grammar_id);
+ let (object_format, revision) = extract_object_format_from_revision(&revision);
+
// WARN: Must init before other operations are done.
- repo.init(&remote)?;
+ repo.init(&remote, object_format)?;
if repo.revision().is_some_and(|rev| rev == revision) {
return Ok(FetchStatus::GitUpToDate);
}
// Fetch the grammar if the revision doesn't match.
- repo.fetch(&remote, &revision)?;
+ repo.fetch(&remote, revision, object_format)?;
- Ok(FetchStatus::GitUpdated { revision })
+ Ok(FetchStatus::GitUpdated {
+ revision: revision.to_string(),
+ })
}
// A wrapper around 'git' commands which returns stdout in success and a