Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# EditorConfig is awesome: http://EditorConfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[{*.md,*.py,*.rs}]
indent_size = 4
15 changes: 10 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ jobs:
if: ${{ !matrix.cross }}
run: |
RUSTFLAGS='-C target-feature=+crt-static' \
cargo build --release -p liyi --target ${{ matrix.target }}
cargo build --release -p liyi-cli --target ${{ matrix.target }}
ls -alF target/${{ matrix.target }}/release/

- name: Build release binary (cross)
if: matrix.cross
run: |
cross build --release -p liyi --target ${{ matrix.target }}
cross build --release -p liyi-cli --target ${{ matrix.target }}
ls -alF target/${{ matrix.target }}/release/

- name: Strip binary
run: |
Expand All @@ -85,12 +87,12 @@ jobs:
cp target/${{ matrix.target }}/release/liyi dist/
cp LICENSE-*.txt dist/
cp README.md dist/
tar -C dist -czf ../liyi-${{ github.ref_name }}-${{ matrix.target }}.tar.gz .
tar -C dist -czf ./liyi-${{ github.ref_name }}-${{ matrix.target }}.tar.gz .

- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: liyi-${{ matrix.target }}
name: dist-artifact-${{ matrix.target }}
path: liyi-${{ github.ref_name }}-${{ matrix.target }}.tar.gz

release:
Expand All @@ -104,10 +106,13 @@ jobs:
uses: actions/download-artifact@v8
with:
path: artifacts
pattern: liyi-*
pattern: dist-artifact-*
merge-multiple: true

- run: ls -alF artifacts

- name: Create Release
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: softprops/action-gh-release@v2
with:
files: artifacts/*.tar.gz
Expand Down
136 changes: 127 additions & 9 deletions .github/workflows/release.yml.liyi.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
"item": "on",
"reviewed": true,
"intent": "=trivial",
"intent": "Trigger this workflow only for pushed Git tags. Ordinary branch pushes and pull requests must not run the release pipeline or publish release assets.",
"source_span": [
5,
8
Expand All @@ -18,7 +18,7 @@
{
"item": "permissions",
"reviewed": true,
"intent": "Grant the workflow GITHUB_TOKEN write access to repository contents so the release job can create and publish a GitHub release. Must not request broader repository permissions than contents: write.",
"intent": "Grant the workflow token write access to repository contents so the release job can create or update a GitHub release and attach assets. Must not request broader repository permissions than contents: write.",
"source_span": [
10,
11
Expand All @@ -30,26 +30,144 @@
{
"item": "jobs::build",
"reviewed": true,
"intent": "Build one statically linked release tarball per configured musl target. Must install the appropriate Rust target and build tool, use native cargo for non-cross targets and cross for cross targets, strip the produced binary when possible, package the binary together with the license files and README, and upload a per-target artifact for the release job.",
"intent": "Build one release tarball per configured musl target and publish each tarball as an artifact for the downstream release job. The job must choose native or cross compilation per matrix entry, produce the `liyi` CLI binary from package `liyi-cli`, and package the binary together with licenses and README.",
"source_span": [
17,
94
96
],
"tree_path": "key.jobs::key.build",
"source_hash": "sha256:d91d63ed004a32d04cdcd4e2aa7f081a1590bc53b732a8cbbe511d9c6b677e87",
"source_hash": "sha256:71f4a0f6d299fbf42ee8ba8eb3da5a4c46a157deb550e9c7cced6e9f55e4e537",
"source_anchor": " build:"
},
{
"item": "build::strategy",
"reviewed": true,
"intent": "Enumerate the supported musl release targets and annotate each target with the architecture name and whether it requires cross compilation. The `cross` flag and `arch` value must stay aligned with the later install, build, and strip steps.",
"source_span": [
20,
34
],
"tree_path": "key.jobs::key.build::key.strategy",
"source_hash": "sha256:e23b623cd0cbd9ee9e51e3eef73fa8b0a33f988486d8d89c1541032def51daab",
"source_anchor": " strategy:"
},
{
"item": "build::install-native-musl-tools",
"reviewed": true,
"intent": "For native builds only, install the host musl toolchain packages before compilation so cargo can link the static musl binary without cross. This step must not run for matrix entries that use cross.",
"source_span": [
44,
48
],
"tree_path": "key.jobs::key.build::key.steps[2]::key.run",
"source_hash": "sha256:53d3f3776031cd2a1de9c69d47a04405a586d455b4816654453563aee662c0b4",
"source_anchor": " - name: Install native musl tools"
},
{
"item": "build::install-cross",
"reviewed": true,
"intent": "For cross-compiled targets only, install `cross` from the upstream Git repository instead of the pinned marketplace installer because the packaged action version is too old for the loongarch64 and riscv64 musl targets used in this matrix. This step must not run for native builds.",
"source_span": [
50,
56
],
"tree_path": "key.jobs::key.build::key.steps[3]::key.run",
"source_hash": "sha256:f8258de5e2a775f28b6837a034c23142c802e840a645aae82feea016592e14c9",
"source_anchor": " - name: Install cross"
},
{
"item": "build::build-native-binary",
"reviewed": true,
"intent": "For non-cross targets, build package `liyi-cli` in release mode for the selected target with `crt-static` enabled so the produced `liyi` binary is statically linked. The post-build directory listing acts as a sanity check that the expected release artifact exists at `target/<target>/release/`.",
"source_span": [
62,
67
],
"tree_path": "key.jobs::key.build::key.steps[5]::key.run",
"source_hash": "sha256:ad6da2aaa86f294126892da476be8e7255abd010ab6a7f1d3e1ac696cbab923e",
"source_anchor": " - name: Build release binary (native)"
},
{
"item": "build::build-cross-binary",
"reviewed": true,
"intent": "For cross targets, build package `liyi-cli` in release mode through `cross` for the selected target triple. The post-build directory listing must verify that the expected target release directory was populated before later packaging steps run.",
"source_span": [
69,
73
],
"tree_path": "key.jobs::key.build::key.steps[6]::key.run",
"source_hash": "sha256:f0516755de82a5ca1b26380cc0157a046918dc7a99005c5e2a8954ecfef9084d",
"source_anchor": " - name: Build release binary (cross)"
},
{
"item": "build::strip-binary",
"reviewed": true,
"intent": "Attempt to strip the produced `liyi` binary after compilation to reduce release artifact size. Native builds must use the host `strip`; cross builds must try the target-specific musl strip tool but tolerate its absence so packaging can still proceed.",
"source_span": [
75,
82
],
"tree_path": "key.jobs::key.build::key.steps[7]::key.run",
"source_hash": "sha256:3e21374960929ccbaed4d37fc5b285dc0b4b77cf5c75bddfda431871b20374d1",
"source_anchor": " - name: Strip binary"
},
{
"item": "build::package-binary",
"reviewed": true,
"intent": "Assemble the release tarball for the current target by copying the built `liyi` binary, the repository license files, and `README.md` into `dist/`, then creating `liyi-<tag>-<target>.tar.gz` in the workspace root. The tarball name must stay stable because the upload and release steps consume that exact filename pattern.",
"source_span": [
84,
90
],
"tree_path": "key.jobs::key.build::key.steps[8]::key.run",
"source_hash": "sha256:3e07ffc6ef6e4854235d2668749ade3536be6eece60e8c0f592244f701e506d5",
"source_anchor": " - name: Package binary"
},
{
"item": "build::upload-artifact",
"reviewed": true,
"intent": "Upload the packaged tarball as a GitHub Actions artifact named `dist-artifact-<target>`. The artifact naming convention must remain consistent with the release job's download pattern so every target tarball is collected for publication.",
"source_span": [
92,
96
],
"tree_path": "key.jobs::key.build::key.steps[9]::key.with",
"source_hash": "sha256:b53367295c966328cd1ed89adb45574227d5a1b28a929e9442f7fd7e86d994a5",
"source_anchor": " - name: Upload artifact"
},
{
"item": "jobs::release",
"reviewed": true,
"intent": "After all build-matrix artifacts are available, download every packaged tarball and publish them on the GitHub release for the pushed tag. Must attach all artifacts, generate release notes, and authenticate with the repository GITHUB_TOKEN.",
"intent": "Wait for all build-matrix artifacts, gather the packaged tarballs into one directory, and publish them on the GitHub release corresponding to the pushed tag. The job must not try to publish before the build job succeeds.",
"source_span": [
96,
118
98,
123
],
"tree_path": "key.jobs::key.release",
"source_hash": "sha256:bfc7fd802dbded85b5ee694007432677d288376e5f46f0c74a39335ce123c1d5",
"source_hash": "sha256:1c505f0c2dda23075de3f2b6265714e95dad26cf4b7840c3ce3fe070bc540eda",
"source_anchor": " release:"
},
{
"item": "release::download-artifacts",
"reviewed": true,
"intent": "Download every target tarball artifact emitted by the build matrix into the local `artifacts/` directory. The `dist-artifact-*` pattern and `merge-multiple: true` setting must gather all per-target uploads into one flat directory so the release step can attach them with a single glob.",
"source_span": [
105,
110
],
"source_hash": "sha256:5cb97468bcd3e1686454133b658486b7359407a75637bd46c408dc622c757ea8",
"source_anchor": " - name: Download all artifacts"
},
{
"item": "release::create-release",
"reviewed": true,
"intent": "Create or update the GitHub release only when the event is a tag push, then attach every `artifacts/*.tar.gz` asset, keep the release non-draft and non-prerelease, generate release notes, and authenticate with `GITHUB_TOKEN`. The explicit `if` guard must prevent accidental release publication from any future non-tag trigger added to this workflow.",
"source_span": [
114,
123
],
"source_hash": "sha256:216ea36572f4dc28e5641bc4cc64b64e769140ce0517f9cd84a7483b914891c4",
"source_anchor": " - name: Create Release"
}
]
}
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Follow Conventional Commits: `<type>(<scope>): <summary>`
- Types: `feat`, `fix`, `docs`, `refactor`, `build`, `ci`, `style`. Do **not** use `chore`.
- Scopes: `linter`, `design`, `template`, `docs`, `policy`, `meta`.
- One logical change per commit — do not combine unrelated changes.
- For bug-fix or debugging commits, include a short rationale and root cause analysis in the commit body so reviewers can see why the change was necessary and what failure mode it corrects.
- AIGC trailers required: `Original prompt:`, blank line, Markdown block quote of user prompts verbatim, blank line, `AI-assisted-by: Your model identity (Name of client used to interact with you)`.
- Do **not** add `Signed-off-by` on behalf of the user.

Expand Down
8 changes: 4 additions & 4 deletions crates/liyi-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ ratatui = "0.29"
serde_json = "1.0"
similar = "2.7.0"
syntect = { version = "5", default-features = false, features = [
"default-syntaxes",
"default-themes",
"parsing",
"regex-fancy",
"default-syntaxes",
"default-themes",
"parsing",
"regex-fancy",
] }

liyi = { path = "../liyi" }
40 changes: 40 additions & 0 deletions crates/liyi/src/tree_path/lang_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ mod tests {
assert_eq!(path, "key.nested::key.deep::key.value");
}

#[test]
fn compute_json_indexed_array_element() {
let sample = r#"{
"specs": [
{
"item": "foo",
"intent": "do foo"
},
{
"item": "bar",
"intent": "do bar"
}
]
}"#;
let span = resolve_tree_path(sample, "key.specs[1]::key.item", Language::Json).unwrap();
let path = compute_tree_path(sample, span, Language::Json);
assert_eq!(path, "key.specs[1]::key.item");
}

#[test]
fn roundtrip_json_top_level() {
let span = resolve_tree_path(SAMPLE_JSON, "key.name", Language::Json).unwrap();
Expand All @@ -158,6 +177,27 @@ mod tests {
assert_eq!(re_resolved, span);
}

#[test]
fn roundtrip_json_indexed_array_element() {
let sample = r#"{
"specs": [
{
"item": "foo",
"intent": "do foo"
},
{
"item": "bar",
"intent": "do bar"
}
]
}"#;
let span = resolve_tree_path(sample, "key.specs[1]::key.item", Language::Json).unwrap();
let path = compute_tree_path(sample, span, Language::Json);
assert_eq!(path, "key.specs[1]::key.item");
let re_resolved = resolve_tree_path(sample, &path, Language::Json).unwrap();
assert_eq!(re_resolved, span);
}

#[test]
fn detect_json_extension() {
assert_eq!(
Expand Down
26 changes: 26 additions & 0 deletions crates/liyi/src/tree_path/lang_yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,18 @@ metadata:
assert_eq!(path, "key.jobs::key.build::key.\"runs-on\"");
}

#[test]
fn compute_yaml_indexed_sequence_item() {
let span = resolve_tree_path(
SAMPLE_YAML,
"key.jobs::key.build::key.steps[1]::key.name",
Language::Yaml,
)
.unwrap();
let path = compute_tree_path(SAMPLE_YAML, span, Language::Yaml);
assert_eq!(path, "key.jobs::key.build::key.steps[1]::key.name");
}

#[test]
fn roundtrip_yaml_top_level() {
let span = resolve_tree_path(SAMPLE_YAML, "key.name", Language::Yaml).unwrap();
Expand All @@ -215,6 +227,20 @@ metadata:
assert_eq!(re_resolved, span);
}

#[test]
fn roundtrip_yaml_indexed_sequence_item() {
let span = resolve_tree_path(
SAMPLE_YAML,
"key.jobs::key.build::key.steps[2]::key.run",
Language::Yaml,
)
.unwrap();
let path = compute_tree_path(SAMPLE_YAML, span, Language::Yaml);
assert_eq!(path, "key.jobs::key.build::key.steps[2]::key.run");
let re_resolved = resolve_tree_path(SAMPLE_YAML, &path, Language::Yaml).unwrap();
assert_eq!(re_resolved, span);
}

#[test]
fn detect_yaml_extensions() {
assert_eq!(
Expand Down
Loading
Loading