Conversation
44431a8 to
88a0891
Compare
There was a problem hiding this comment.
Pull request overview
Adds an HTML resume builder demo to the Lunary project, along with a new kernel helper to load raw file contents for embedding CSS/SVG assets in generated output.
Changes:
- Added
@kernel.load_rawfor reading raw file contents from disk. - Introduced a complete resume demo (
examples/resume) including Lunary source, CSS, icons, and generated outputs. - Expanded unit tests and updated README quickstart/build instructions.
Reviewed changes
Copilot reviewed 14 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
lib/lunary.ex |
Adds @kernel.load_raw implementation for reading asset files. |
test/unit/kernel_test.exs |
Adds test coverage for loading a raw fixture file. |
test/unit/atom_test.exs |
Adds test ensuring atoms can contain underscores and numbers. |
test/fixtures/raw.txt |
Adds fixture file used by load_raw test. |
src/lunary_parser.yrl |
Removes commented-out grammar rules (cleanup). |
examples/resume/resume.lun |
Adds Lunary resume builder demo script generating HTML. |
examples/resume/style.css |
Adds demo stylesheet used by generated resume output. |
examples/resume/icons/github.svg |
Adds icon asset loaded via load_raw. |
examples/resume/icons/linkedin.svg |
Adds icon asset loaded via load_raw. |
examples/resume/icons/globe.svg |
Adds icon asset (demo). |
examples/resume/resume.html |
Adds generated HTML output for the demo. |
examples/resume/resume.pdf |
Adds generated PDF output for the demo. |
README.md |
Documents requirements, demo build instructions, and test command. |
projects/resume/main.lun |
Removes old resume demo implementation. |
html_builder.lun |
Removes old scratch/demo file. |
example.lun |
Removes old scratch/demo file. |
test/ideas.lun |
Updates scratch ideas file with additional notes/examples. |
lib/main.ex |
Replaces old TODO block with a header/TODO section at top of file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| root_path = opts[:path] || "" | ||
| {:string, _, file_path} = Enum.at(args, 0) | ||
| fullpath = Path.join(root_path, file_path) | ||
| case File.read(fullpath) do | ||
| {:ok, content} -> | ||
| {content, scope} | ||
| {:error, _} -> raise "File #{file_path} (#{fullpath}) not found" | ||
| end | ||
| _ -> | ||
| raise "Kernel function #{name} is not defined" | ||
| end |
There was a problem hiding this comment.
@kernel.load_raw reads a file path provided by the Lunary program and joins it with opts[:path], but it doesn’t prevent .. path traversal. That allows reading files outside the intended root when running untrusted scripts. Consider expanding/canonicalizing both paths and rejecting any resolved path that is not within the expanded root directory (or otherwise explicitly documenting/guarding that load_raw is intentionally unrestricted).
| root_path = opts[:path] || "" | |
| {:string, _, file_path} = Enum.at(args, 0) | |
| fullpath = Path.join(root_path, file_path) | |
| case File.read(fullpath) do | |
| {:ok, content} -> | |
| {content, scope} | |
| {:error, _} -> raise "File #{file_path} (#{fullpath}) not found" | |
| end | |
| _ -> | |
| raise "Kernel function #{name} is not defined" | |
| end | |
| root_path = opts[:path] | |
| {:string, _, file_path} = Enum.at(args, 0) | |
| # Determine the full path to read, preventing traversal outside root_path if provided. | |
| fullpath = | |
| case root_path do | |
| root when is_binary(root) and root != "" -> | |
| base_dir = Path.expand(root) | |
| expanded = Path.expand(Path.join(base_dir, file_path)) | |
| if expanded == base_dir or String.starts_with?(expanded, base_dir <> Path.sep()) do | |
| expanded | |
| else | |
| raise "Access to file #{file_path} is outside of the allowed root directory" | |
| end | |
| _ -> | |
| # No root configured; fall back to expanded file_path (current behavior, but canonicalized). | |
| Path.expand(file_path) | |
| end | |
| case File.read(fullpath) do | |
| {:ok, content} -> | |
| {content, scope} | |
| {:error, _} -> | |
| raise "File #{file_path} (#{fullpath}) not found" | |
| end | |
| _ -> | |
| raise "Kernel function #{name} is not defined" | |
| end |
| {:string, _, file_path} = Enum.at(args, 0) | ||
| fullpath = Path.join(root_path, file_path) | ||
| case File.read(fullpath) do | ||
| {:ok, content} -> | ||
| {content, scope} | ||
| {:error, _} -> raise "File #{file_path} (#{fullpath}) not found" |
There was a problem hiding this comment.
{:string, _, file_path} = Enum.at(args, 0) will raise a MatchError (or crash on nil) if load_raw is called with a non-string argument or with no args. Prefer validating arity/type and raising a clear runtime error like "load_raw expects a string path".
| {:string, _, file_path} = Enum.at(args, 0) | |
| fullpath = Path.join(root_path, file_path) | |
| case File.read(fullpath) do | |
| {:ok, content} -> | |
| {content, scope} | |
| {:error, _} -> raise "File #{file_path} (#{fullpath}) not found" | |
| arg0 = Enum.at(args, 0) | |
| case arg0 do | |
| {:string, _, file_path} when is_binary(file_path) -> | |
| fullpath = Path.join(root_path, file_path) | |
| case File.read(fullpath) do | |
| {:ok, content} -> | |
| {content, scope} | |
| {:error, _} -> | |
| raise "File #{file_path} (#{fullpath}) not found" | |
| end | |
| nil -> | |
| raise ArgumentError, "load_raw expects a string path, got no arguments" | |
| other -> | |
| raise ArgumentError, "load_raw expects a string path, got: #{inspect(other)}" |
| test "can load raw files" do | ||
| assert " | ||
| res = @kernel.load_raw(\"test/fixtures/raw.txt\") | ||
| res | ||
| " |> Lunary.Main.eval() == "this is a raw file...\n" |
There was a problem hiding this comment.
This test only covers the happy path of @kernel.load_raw. Since the implementation also raises on missing/unreadable files (and can fail on non-string args), add an assertion that the appropriate error is raised for those cases to cover the failure behavior.
| Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) | ||
| and published on [HexDocs](https://hexdocs.pm). Once published, the docs can | ||
| be found at <https://hexdocs.pm/lunary>. | ||
| ./lunary examples/resume/resume.lun > examples/resume/resume.html | weasyprint - examples/resume/resume.pdf --media-type print --encoding utf-8 |
There was a problem hiding this comment.
The shell command here won’t do what it looks like: > examples/resume/resume.html | weasyprint - ... redirects stdout to the file, so the pipe receives nothing. Split into two commands, or use tee to both write the HTML file and pipe it into weasyprint.
| ./lunary examples/resume/resume.lun > examples/resume/resume.html | weasyprint - examples/resume/resume.pdf --media-type print --encoding utf-8 | |
| ./lunary examples/resume/resume.lun | tee examples/resume/resume.html | weasyprint - examples/resume/resume.pdf --media-type print --encoding utf-8 |
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <meta charset="UTF-8"> | ||
| <head> | ||
| <title>Lunary Resume!</title> | ||
| <style>@font-face { |
There was a problem hiding this comment.
This HTML file appears to be generated output of the demo. Committing generated artifacts makes PRs noisy and increases maintenance burden; consider excluding it via .gitignore and regenerating it during demo/build steps instead.
No description provided.