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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Newline can be used to terminate user name input.
- Add `overlap` command to find recipients with the most secret overlap, to help coordinate key refreshes.
- `passage refresh` now warns when secrets are skipped due to lack of access.
- Fix bug where in-line comments in .pub, .keys and .group files were not ignored ([#20](https://github.com/ahrefs/passage/pull/20))

## 0.3.3 (2026-02-13)
- Update os availability in opam. Make passage available in macos again
Expand Down
12 changes: 8 additions & 4 deletions lib/storage.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
open Printf

(** Read lines from a config file, filtering out comments and empty lines *)
let config_lines filename =
if not (Sys.file_exists filename) then []
else
Expand All @@ -12,8 +10,14 @@ let config_lines filename =
in
read_lines []
|> List.filter_map (fun line ->
let trimmed = String.trim line in
if trimmed = "" || String.starts_with ~prefix:"#" trimmed then None else Some trimmed))
let content =
match String.index_opt line '#' with
| None -> line
| Some i -> String.sub line 0 i
in
match String.trim content with
| "" -> None
| s -> Some s))

let save_as ?(mode = 0o644) ~path f =
let temp = Printf.sprintf "%s.save.%d.tmp" path (Unix.getpid ()) in
Expand Down
5 changes: 5 additions & 0 deletions lib/storage.mli
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
(** Read lines from a config file, filtering out comments and empty lines.

Based on Devkit's [Action.config_lines]. *)
val config_lines : string -> string list

(** File output protected with atomic rename.

[save_as path f] is similar to {!Out_channel.with_open_bin} except that writing is done to a temporary file that
Expand Down
74 changes: 74 additions & 0 deletions lib_test/config_lines_test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
open Passage

let test_config_lines content =
let tmpfile = Filename.temp_file "config_lines_test" ".txt" in
Fun.protect
(fun () ->
Out_channel.with_open_text tmpfile (fun oc -> output_string oc content);
let result = Storage.config_lines tmpfile in
List.iter (fun line -> Printf.printf "%S\n" line) result)
~finally:(fun () -> Sys.remove tmpfile)

let%expect_test "empty lines are stripped" =
test_config_lines "alice\n\nbob\n\n\ncharlie\n";
[%expect {|
"alice"
"bob"
"charlie" |}]

let%expect_test "full-line comments are stripped" =
test_config_lines "alice\n# this is a comment\nbob\n # indented comment\ncharlie\n";
[%expect {|
"alice"
"bob"
"charlie" |}]

let%expect_test "inline comments are stripped" =
test_config_lines "alice # team lead\nbob#backup\ncharlie # ops\n";
[%expect {|
"alice"
"bob"
"charlie" |}]

let%expect_test "leading and trailing whitespace is trimmed" =
test_config_lines " alice \n\tbob\t\n charlie \n";
[%expect {|
"alice"
"bob"
"charlie" |}]

let%expect_test "whitespace + inline comments combined" =
test_config_lines " alice # lead \n bob # backup \n";
[%expect {|
"alice"
"bob" |}]

let%expect_test "line that is only a comment after stripping" =
test_config_lines "# full comment\n # indented full comment\nalice\n";
[%expect {| "alice" |}]

let%expect_test "nonexistent file returns empty list" =
let result = Storage.config_lines "/nonexistent/path/file.txt" in
List.iter (fun line -> Printf.printf "%S\n" line) result;
[%expect {| |}]

let%expect_test "empty file returns empty list" =
test_config_lines "";
[%expect {| |}]

let%expect_test "file with only comments and whitespace" =
test_config_lines "# comment\n \n\n # another\n";
[%expect {| |}]

let%expect_test "mixed: all features together" =
test_config_lines {|# header comment
alice # admin
bob
# separator

charlie # ops
|};
[%expect {|
"alice"
"bob"
"charlie" |}]
Loading