Skip to content

Commit 6ff163f

Browse files
authored
Merge pull request #6 from AstraBert/fix/windows-compat
fix: windows compatibility
2 parents 69eddaa + b6c7c59 commit 6ff163f

6 files changed

Lines changed: 152 additions & 32 deletions

File tree

.github/workflows/test.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,28 @@ jobs:
3838
3939
- name: Run tests
4040
run: jake test
41+
42+
test-windows:
43+
name: Test on windows-latest
44+
runs-on: windows-latest
45+
46+
steps:
47+
- name: Checkout code
48+
uses: actions/checkout@v4
49+
50+
- name: Set up Rust
51+
uses: dtolnay/rust-toolchain@stable
52+
53+
- name: Cache cargo registry
54+
uses: actions/cache@v4
55+
with:
56+
path: |
57+
${{ runner.os == 'Windows' && '~\\.cargo\\registry' || '~/.cargo/registry' }}
58+
${{ runner.os == 'Windows' && '~\\.cargo\\git' || '~/.cargo/git' }}
59+
target
60+
key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }}
61+
restore-keys: |
62+
${{ runner.os }}-cargo-
63+
64+
- name: Run tests
65+
run: cargo test

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aggit"
3-
version = "0.2.0-alpha"
3+
version = "0.3.0-alpha"
44
edition = "2024"
55
license-file = "LICENSE"
66
readme = "README.md"

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,9 @@ S3-backed, git-versioned object storage for agents.
1111
## Installation
1212

1313
```bash
14-
cargo install aggit@0.2.0-alpha
14+
cargo install aggit@0.3.0-alpha
1515
```
1616

17-
18-
> [!NOTE]
19-
>
20-
> `aggit` is not yet compatible with Windows.
21-
2217
### As an Agent Skill
2318

2419
You can use `aggit` as an agent skill, downloading it with the `skills` CLI tool:

src/gitops/mod.rs

Lines changed: 124 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ use flate2::{
99
use ignore::WalkBuilder;
1010
use serde::{Deserialize, Serialize};
1111
use sha1_checked::{Digest, Sha1};
12-
use std::{
13-
collections::HashMap,
14-
fs::Permissions,
15-
os::unix::fs::{MetadataExt, PermissionsExt},
16-
};
12+
use std::collections::HashMap;
13+
#[cfg(unix)]
14+
use std::os::unix::fs::{MetadataExt, PermissionsExt};
1715
use std::{collections::HashSet, time::SystemTime};
1816
use std::{fmt, fs, path::PathBuf, str::FromStr};
1917
use std::{
@@ -626,18 +624,69 @@ pub fn add(paths: Vec<String>) -> anyhow::Result<()> {
626624
.try_into()
627625
.map_err(|_| anyhow::anyhow!("SHA1 hash must be 20 bytes"))?;
628626
let entry = IndexEntry {
627+
sha1: sha1_bytes,
628+
flags,
629+
size: st.len() as u32,
630+
631+
#[cfg(unix)]
629632
ctime_s: st.ctime() as u32,
633+
#[cfg(not(unix))]
634+
ctime_s: st
635+
.created()
636+
.ok()
637+
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
638+
.map(|d| d.as_secs() as u32)
639+
.unwrap_or(0),
640+
641+
#[cfg(unix)]
630642
ctime_n: st.ctime_nsec() as u32,
631-
mtime_n: st.mtime() as u32,
632-
mtime_s: st.mtime_nsec() as u32,
643+
#[cfg(not(unix))]
644+
ctime_n: 0,
645+
646+
#[cfg(unix)]
647+
mtime_s: st.mtime() as u32,
648+
#[cfg(not(unix))]
649+
mtime_s: st
650+
.modified()
651+
.ok()
652+
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
653+
.map(|d| d.as_secs() as u32)
654+
.unwrap_or(0),
655+
656+
#[cfg(unix)]
657+
mtime_n: st.mtime_nsec() as u32,
658+
#[cfg(not(unix))]
659+
mtime_n: 0,
660+
661+
#[cfg(unix)]
633662
dev: st.dev() as u32,
663+
#[cfg(not(unix))]
664+
dev: 0,
665+
666+
#[cfg(unix)]
634667
ino: st.ino() as u32,
635-
size: st.size() as u32,
636-
gid: st.gid(),
637-
uid: st.uid(),
668+
#[cfg(not(unix))]
669+
ino: 0,
670+
671+
#[cfg(unix)]
638672
mode: st.mode(),
639-
flags,
640-
sha1: sha1_bytes,
673+
#[cfg(not(unix))]
674+
mode: if st.permissions().readonly() {
675+
0o100444
676+
} else {
677+
0o100644
678+
},
679+
680+
#[cfg(unix)]
681+
uid: st.uid(),
682+
#[cfg(not(unix))]
683+
uid: 0,
684+
685+
#[cfg(unix)]
686+
gid: st.gid(),
687+
#[cfg(not(unix))]
688+
gid: 0,
689+
641690
path,
642691
};
643692
entries.push(entry);
@@ -1005,7 +1054,16 @@ pub fn restore_working_tree(commit_hash: String) -> anyhow::Result<()> {
10051054
fs::create_dir_all(par)?;
10061055
}
10071056
fs::write(path, &file_data)?;
1008-
fs::set_permissions(path, Permissions::from_mode(*mode))?;
1057+
#[cfg(unix)]
1058+
fs::set_permissions(&path, fs::Permissions::from_mode(*mode))?;
1059+
1060+
#[cfg(not(unix))]
1061+
{
1062+
let mut perms = fs::metadata(&path)?.permissions();
1063+
let readonly = (*mode & 0o200) == 0; // owner write bit unset = readonly
1064+
perms.set_readonly(readonly);
1065+
fs::set_permissions(&path, perms)?;
1066+
}
10091067
}
10101068
}
10111069

@@ -1017,21 +1075,63 @@ pub fn restore_working_tree(commit_hash: String) -> anyhow::Result<()> {
10171075
if flags >= (1 << 12) {
10181076
return Err(anyhow!("Invalid flags"));
10191077
}
1020-
let meta = fs::metadata(path)?; // stat the just-written file
1078+
let st = fs::metadata(path)?; // stat the just-written file
10211079
Ok(IndexEntry {
10221080
mode: *mode,
10231081
path: path.clone(),
10241082
sha1: hex::decode(sha1)?.try_into().unwrap(),
1025-
ctime_s: meta.ctime() as u32,
1026-
ctime_n: meta.ctime_nsec() as u32,
1027-
mtime_s: meta.mtime() as u32,
1028-
mtime_n: meta.mtime_nsec() as u32,
1029-
size: meta.size() as u32,
1030-
ino: meta.ino() as u32,
1031-
dev: meta.dev() as u32,
1032-
uid: meta.uid(),
1033-
gid: meta.gid(),
1083+
size: st.len() as u32,
10341084
flags,
1085+
1086+
#[cfg(unix)]
1087+
ctime_s: st.ctime() as u32,
1088+
#[cfg(not(unix))]
1089+
ctime_s: st
1090+
.created()
1091+
.ok()
1092+
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
1093+
.map(|d| d.as_secs() as u32)
1094+
.unwrap_or(0),
1095+
1096+
#[cfg(unix)]
1097+
ctime_n: st.ctime_nsec() as u32,
1098+
#[cfg(not(unix))]
1099+
ctime_n: 0,
1100+
1101+
#[cfg(unix)]
1102+
mtime_s: st.mtime() as u32,
1103+
#[cfg(not(unix))]
1104+
mtime_s: st
1105+
.modified()
1106+
.ok()
1107+
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
1108+
.map(|d| d.as_secs() as u32)
1109+
.unwrap_or(0),
1110+
1111+
#[cfg(unix)]
1112+
mtime_n: st.mtime_nsec() as u32,
1113+
#[cfg(not(unix))]
1114+
mtime_n: 0,
1115+
1116+
#[cfg(unix)]
1117+
dev: st.dev() as u32,
1118+
#[cfg(not(unix))]
1119+
dev: 0,
1120+
1121+
#[cfg(unix)]
1122+
ino: st.ino() as u32,
1123+
#[cfg(not(unix))]
1124+
ino: 0,
1125+
1126+
#[cfg(unix)]
1127+
uid: st.uid(),
1128+
#[cfg(not(unix))]
1129+
uid: 0,
1130+
1131+
#[cfg(unix)]
1132+
gid: st.gid(),
1133+
#[cfg(not(unix))]
1134+
gid: 0,
10351135
})
10361136
})
10371137
.collect::<anyhow::Result<Vec<_>>>()?;
@@ -1210,6 +1310,7 @@ mod tests {
12101310
}
12111311

12121312
#[test]
1313+
#[serial_test::serial]
12131314
fn test_read_object_roundtrip() {
12141315
let data = b"roundtrip data".to_vec();
12151316
let hash = hash_object(&mut data.clone(), ObjectType::Blob, true).unwrap();

src/repository/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,3 @@ mod tests {
135135
cleanup_aggit_dir();
136136
}
137137
}
138-

0 commit comments

Comments
 (0)