From 07a2e2af056011eab68b313d977cfc9f704ffe62 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:33:30 +0000 Subject: [PATCH 01/10] Add EXPLANATION.md with repository overview and key features Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- EXPLANATION.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 EXPLANATION.md diff --git a/EXPLANATION.md b/EXPLANATION.md new file mode 100644 index 0000000..6cb1eaf --- /dev/null +++ b/EXPLANATION.md @@ -0,0 +1,67 @@ +# Radpath Repository Explanation + +Radpath is an Elixir library designed for path manipulation, largely inspired by Python's `pathlib`. It provides a more idiomatic and convenient way to handle file system paths in Elixir compared to the standard `Path` and `File` modules alone. + +## Project Structure + +The codebase is organized into a main facade module and several specialized submodules: + +* **`Radpath` (`lib/radpath.ex`)**: The main entry point that aggregates functionalities from submodules and provides additional utilities like zipping, hashing, and symlink management. +* **`Radpath.Dirs` (`lib/Radpath/directory.ex`)**: Focuses on directory-related operations, such as listing directories with regex filtering. +* **`Radpath.Files` (`lib/Radpath/files.ex`)**: Handles file-specific operations, including listing files with extension-based filtering. +* **`Radpath.Tempfs` (`lib/Radpath/tempfs.ex`)**: Provides utilities for creating temporary files and directories. + +## Key Features and Functionalities + +### 1. File and Directory Listing +* `Radpath.files(path, ext \\ nil)`: Lists files in a given path, optionally filtered by extension. +* `Radpath.dirs(path, regex_dir \\ ".+")`: Lists directories in a given path, optionally filtered by a regex pattern. + +### 2. Archiving (Zip/Unzip) +* `Radpath.zip(paths, archive_name)`: Creates a zip archive from a list of paths or a single path. +* `Radpath.unzip(zip_file, unzip_dir \\ File.cwd!)`: Extracts a zip archive to a specified directory. + +### 3. File Hashing +* `Radpath.md5sum(path)`: Calculates the MD5 hash of a file. +* `Radpath.sha1sum(path)`: Calculates the SHA1 hash of a file. + +### 4. Path Utilities +* `Radpath.relative_path(base, file)`: Returns the relative path of a file with respect to a base directory. +* `Radpath.parent_path(path)`: Retrieves the parent directory of a given path. +* `Radpath.ensure(path, is_file \\ false)`: Ensures a directory or file exists (creates it if it doesn't). +* `Radpath.erusne(path)`: The opposite of `ensure`; removes a file or directory if it exists. + +### 5. Symlinks +* `Radpath.symlink(source, destination)`: Creates a symbolic link. +* `Radpath.islink?(path)`: Checks if a given path is a symbolic link. + +### 6. Temporary Filesystem +* `Radpath.mktempfile(ext \\ ".tmp", path)`: Creates a temporary file. +* `Radpath.mktempdir(path \\ "/tmp")`: Creates a temporary directory. + +## Usage Examples + +Based on the project's tests and documentation: + +```elixir +# List all PDF files in a directory +Radpath.files("/path/to/docs", "pdf") + +# Create a zip archive +Radpath.zip(["dir1", "file1.txt"], "my_archive.zip") + +# Get MD5 checksum of a file +hash = Radpath.md5sum("mix.exs") + +# Ensure a directory exists +Radpath.ensure("/tmp/my_new_dir") + +# Create a temporary log file +{status, fd, filepath} = Radpath.mktempfile(".log", "/tmp") +``` + +## Development and Testing + +* **Build Tool**: Mix +* **Dependencies**: Includes `ex_doc`, `finder`, `erlware_commons`, `pattern_tap`, and `temp`. +* **Testing**: The project uses `ExUnit`. Tests can be run via `make test` or `mix test` (requires Elixir and Erlang to be installed). From c4723c2e2e1a66c47f4330ae65484f1c4c73c3ac Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:36:20 +0000 Subject: [PATCH 02/10] Update GitHub Actions versions to fix CI failure and add repository explanation Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .github/workflows/elixir.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 8e5151f..82945b5 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -13,14 +13,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Elixir uses: actions/setup-elixir@v1 with: elixir-version: '1.10.3' # Define the elixir version [required] otp-version: '22.3' # Define the OTP version [required] - name: Restore dependencies cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: deps key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} From d78383b1588775fdebd89c19c84635e69b9a9072 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:38:11 +0000 Subject: [PATCH 03/10] Fix CI failure and add repository explanation - Update actions/checkout to v4 - Update actions/cache to v4 - Switch from deprecated actions/setup-elixir to erlef/setup-beam@v1 - Add EXPLANATION.md with repository overview Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .github/workflows/elixir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 82945b5..bda4ab0 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Elixir - uses: actions/setup-elixir@v1 + uses: erlef/setup-beam@v1 with: elixir-version: '1.10.3' # Define the elixir version [required] otp-version: '22.3' # Define the OTP version [required] From 22be39984c10c3b7a2f695cac69c6cff5179dee1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:40:04 +0000 Subject: [PATCH 04/10] Fix CI failure by pinning runner to ubuntu-20.04 and add repository explanation - Pin runner to ubuntu-20.04 to support older OTP 22.3 - Update actions/checkout to v4 - Update actions/cache to v4 - Switch from deprecated actions/setup-elixir to erlef/setup-beam@v1 - Add EXPLANATION.md with repository overview Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .github/workflows/elixir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index bda4ab0..edc492e 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -10,7 +10,7 @@ jobs: build: name: Build and test - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 From ce7e86151dce331d012877c89bc9634c21be89ef Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:55:35 +0000 Subject: [PATCH 05/10] Upgrade to Elixir 1.18.2, OTP 27.2 and provide repository explanation - Update mix.exs, .tools-versions, Makefile, CI configs (GitHub Actions, Travis, CircleCI) - Modernize mix.exs by removing legacy Hex start checks - Fix CI pipeline by updating actions and pinning modern runner - Add EXPLANATION.md with comprehensive library overview and examples Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .github/workflows/elixir.yml | 6 +++--- .tools-versions | 4 ++-- .travis.yml | 5 ++--- Makefile | 2 +- circle.yml | 4 ++-- mix.exs | 3 +-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index edc492e..b2db7a1 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -10,15 +10,15 @@ jobs: build: name: Build and test - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Elixir uses: erlef/setup-beam@v1 with: - elixir-version: '1.10.3' # Define the elixir version [required] - otp-version: '22.3' # Define the OTP version [required] + elixir-version: '1.18.2' # Define the elixir version [required] + otp-version: '27.2' # Define the OTP version [required] - name: Restore dependencies cache uses: actions/cache@v4 with: diff --git a/.tools-versions b/.tools-versions index f9936d3..b890e38 100644 --- a/.tools-versions +++ b/.tools-versions @@ -1,2 +1,2 @@ -erlang 18.0 -elixir 1.3.4 +erlang 27.2 +elixir 1.18.2 diff --git a/.travis.yml b/.travis.yml index 76f777c..28c1b18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,9 @@ language: elixir env: - MIX_ENV=test elixir: - - 1.5.0 + - 1.18.2 otp_release: - - 17.5 - - 18.0 + - 27.2 after_script: - mix deps.get --only docs - MIX_ENV=docs mix inch.report diff --git a/Makefile b/Makefile index 425e2f3..afc386d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VENDORED_ELIXIR=${PWD}/vendor/elixir/bin/elixir VENDORED_MIX=${PWD}/vendor/elixir/bin/mix RUN_VENDORED_MIX=${VENDORED_ELIXIR} ${VENDORED_MIX} #VERSION := $(strip $(shell cat VERSION)) -STABLE_ELIXIR_VERSION = 1.1.1 +STABLE_ELIXIR_VERSION = 1.18.2 .PHONY: all test diff --git a/circle.yml b/circle.yml index 6a3d3c4..d311051 100644 --- a/circle.yml +++ b/circle.yml @@ -1,8 +1,8 @@ machine: environment: PATH: "$HOME/.asdf/bin:$HOME/.asdf/shims:$PATH" - ERLANG_VERSION: "19.0" - ELIXIR_VERSION: "1.3.2" + ERLANG_VERSION: "27.2" + ELIXIR_VERSION: "1.18.2" dependencies: pre: diff --git a/mix.exs b/mix.exs index 311d27d..3b8f851 100644 --- a/mix.exs +++ b/mix.exs @@ -1,4 +1,3 @@ -Code.ensure_loaded?(Hex) and Hex.start defmodule Radpath.Mixfile do use Mix.Project @@ -7,7 +6,7 @@ defmodule Radpath.Mixfile do def project do [ app: :radpath, version: @version, - elixir: "~> 1.5.0 or ~> 1.4.0 or ~> 1.3.0 or ~> 1.0.2 or ~> 1.1.0", + elixir: "~> 1.11", description: description(), docs: [source_ref: "v#{@version}", main: "Radpath"], package: package(), From f51bb64f28088f5aa2c707d4f7036b386573dcb3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:04:00 +0000 Subject: [PATCH 06/10] Fix deprecated calls, bugs, and broken tests in Radpath - Replace deprecated String.to_char_list/1 with String.to_charlist/1 - Refactor parent_path/1 to be more robust using Path.dirname/1 - Correct mismatched and missing @spec annotations - Fix broken tests in test/radpath_test.exs (assertions, typos, cleanup) - Modernize project configurations for Elixir 1.18.2 and Erlang 27.2 - Add EXPLANATION.md with detailed repository overview Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- lib/Radpath/directory.ex | 2 +- lib/Radpath/files.ex | 2 +- lib/radpath.ex | 24 ++++++++++++------------ test/radpath_test.exs | 26 +++++++++++++------------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/Radpath/directory.ex b/lib/Radpath/directory.ex index 1cbcf56..28e7667 100644 --- a/lib/Radpath/directory.ex +++ b/lib/Radpath/directory.ex @@ -25,7 +25,7 @@ defmodule Radpath.Dirs do iex(3)> Radpath.dirs(["/home/lowks/src/elixir/radpath/lib", "/home/lowks/src/elixir/radpath/_build"], regex_dir) """ - @spec dirs(bitstring, bitstring) :: list + @spec dirs(bitstring | list, bitstring) :: list def dirs(path, regex_dir \\ ".+") when (is_bitstring(path) or is_list(path)) do path |> normalize_path diff --git a/lib/Radpath/files.ex b/lib/Radpath/files.ex index 7a81f95..dcaaa0a 100644 --- a/lib/Radpath/files.ex +++ b/lib/Radpath/files.ex @@ -44,7 +44,7 @@ defmodule Radpath.Files do """ - @spec files(bitstring, bitstring) :: list + @spec files(bitstring | list, bitstring | list) :: list def files(path, ext) when is_bitstring(path) and is_bitstring(ext) do file_ext = case String.valid? ext do true -> [ext] diff --git a/lib/radpath.ex b/lib/radpath.ex index 3a17d0c..29d286a 100644 --- a/lib/radpath.ex +++ b/lib/radpath.ex @@ -61,13 +61,13 @@ defmodule Radpath do """ - @spec parent_path(list) :: none + @spec zip(list, bitstring) :: any def zip(dirs, archive_name) when is_list(dirs) do dirs_list = dirs |> Enum.filter(fn(x) -> File.exists?(x) end) - |> Enum.map&(String.to_char_list(&1)) + |> Enum.map(&(String.to_charlist(&1))) unless Enum.empty? dirs_list do - Z.create(String.to_char_list(archive_name), dirs_list) + Z.create(String.to_charlist(archive_name), dirs_list) end end @@ -85,10 +85,10 @@ defmodule Radpath do Radpath.zip(dir1, archive_name) """ - @spec zip(bitstring, bitstring) :: none + @spec zip(bitstring, bitstring) :: any def zip(dir, archive_name) when is_bitstring(dir) do if File.exists?(dir) do - Z.create(String.to_char_list(archive_name), [String.to_char_list(dir)]) + Z.create(String.to_charlist(archive_name), [String.to_charlist(dir)]) end end @@ -110,10 +110,10 @@ defmodule Radpath do """ - @spec unzip(bitstring, bitstring) :: none + @spec unzip(bitstring, bitstring) :: any def unzip(zip_file, unzip_dir \\ File.cwd!) when is_bitstring(zip_file) do if File.exists?(zip_file) do - {:ok,ziphandler} = Z.openzip_open String.to_char_list(zip_file), [cwd: unzip_dir] + {:ok,ziphandler} = Z.openzip_open String.to_charlist(zip_file), [cwd: unzip_dir] Z.openzip_get(ziphandler) Z.openzip_close(ziphandler) end @@ -195,7 +195,7 @@ defmodule Radpath do iex(2)> Radpath.relative_path("/tmp/lowks/", "/tmp/lowks/hoho/iam.txt") "hoho/iam.txt" """ - @spec relative_path(bitstring, bitstring) :: none + @spec relative_path(bitstring, bitstring) :: bitstring def relative_path(base, file) do split = Path.split(base) case split == Path.split(file) || length(split) > length(Path.split(file)) do @@ -220,9 +220,9 @@ defmodule Radpath do @spec parent_path(bitstring) :: bitstring def parent_path(path) when is_bitstring(path) do path - |> Path.absname(path) - |> String.split(Path.basename(path)) - |> List.first + |> Path.absname() + |> Path.dirname() + |> make_into_path() end @doc """ @@ -245,7 +245,7 @@ defmodule Radpath do """ - @spec ensure(bitstring, bitstring) :: none + @spec ensure(bitstring, boolean) :: any def ensure(path, is_file \\ false) when is_bitstring(path) do unless File.exists?(path) do cond do diff --git a/test/radpath_test.exs b/test/radpath_test.exs index 89f2494..c99aed9 100644 --- a/test/radpath_test.exs +++ b/test/radpath_test.exs @@ -94,8 +94,9 @@ defmodule RadpathTests.RadpathFacts do try do Path.join(fixture_path(), "dome.csv") |> File.exists? |> refute Radpath.unzip(Path.join(fixture_path(), "dome.zip"), "/tmp") + assert Path.join("/tmp", "dome.csv") |> File.exists? after - Path.join("/tmp", "dome.csv") |> File.exists? + File.rm_rf(Path.join("/tmp", "dome.csv")) end end @@ -115,7 +116,7 @@ defmodule RadpathTests.RadpathFacts do defmodule FilteringTest do use ExUnit.Case - @dud_files ["testfile1.dud, file3.dud"] + @dud_files ["testfile1.dud", "file3.dud"] @test_files ["testdir3", "testdir2", "testdir1"] @file_list ["file1.txt", "file2.txt", "file3.log"] @file_ext ["txt", "log"] @@ -126,7 +127,6 @@ defmodule RadpathTests.RadpathFacts do test "File Filtering" do files = Radpath.files(fixture_path(), "txt") |> Enum.map(&Path.basename(&1)) Enum.map(@dud_files, fn(x) -> refute Enum.member?(files, x) end) - Enum.all?(@dud_files -- ["file3.log"], fn(x) -> files |> Enum.member? x end) end test "File Filtering returns empty if path does not exist" do @@ -135,14 +135,14 @@ defmodule RadpathTests.RadpathFacts do test "Directory filtering" do dirs = Radpath.dirs(fixture_path()) |> Enum.map(&Path.basename(&1)) - map(["testfile1.dud, file3.dud"], fn(x) -> refute Enum.member?(dirs, x) end) - Enum.all?(["testdir3", "testdir2", "testdir1"], fn(x) -> Enum.member?(dirs, x) end) + map(["testfile1.dud", "file3.dud"], fn(x) -> refute Enum.member?(dirs, x) end) + assert Enum.all?(["testdir3", "testdir2", "testdir1"], fn(x) -> Enum.member?(dirs, x) end) end test "Directory Filter: List" do - expected = @test_files ++ ["fixtures"] |> Enum.sort - actual = Radpath.dirs(["test", "lib"]) |> map(&basename(&1)) - assert actual == expected ++ ["Radpath"] + expected = (@test_files ++ ["fixtures", "Radpath"]) |> Enum.sort + actual = Radpath.dirs(["test", "lib"]) |> map(&basename(&1)) |> Enum.sort + assert actual == expected end test "Directory Filtering: Regex" do @@ -172,19 +172,19 @@ defmodule RadpathTests.RadpathFacts do test "Filtering Multiple filter for Files" do Radpath.files(fixture_path(), @file_ext) |> map(&basename(&1)) |> Enum.sort - |> (&(Enum.all?(@file_list, fn(x) -> Enum.member?(&1, x) end))).() + |> (&(assert Enum.all?(@file_list, fn(x) -> Enum.member?(&1, x) end))).() end test "Filtering long file list" do Radpath.files(fixture_path(), @file_ext) |> Enum.map(&Path.basename(&1)) |> Enum.sort - |> (&(Enum.all?(@file_list, fn(x) -> Enum.member?(&1, x) end))).() + |> (&(assert Enum.all?(@file_list, fn(x) -> Enum.member?(&1, x) end))).() end - test "Filtering Multiple Filter for Files" do - files = Radpath.files(['lib'], @file_ext) |> Enum.map(&Path.basename(&1)) - Enum.all?(@file_list, fn(x) -> Enum.member?(files, x) end) + test "Filtering Multiple Filter for Files with List" do + files = Radpath.files([fixture_path()], @file_ext) |> Enum.map(&Path.basename(&1)) + assert Enum.all?(@file_list, fn(x) -> Enum.member?(files, x) end) end end From a7aa46433f2c6dba6744ade3ae5346ad48097bd4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:14:36 +0000 Subject: [PATCH 07/10] Add EXPLANATION.md, autobuild.yml, and update README badges - Create EXPLANATION.md with comprehensive repository overview - Add .github/workflows/autobuild.yml for modern GitHub Actions CI - Update README.md to use GitHub Actions CI badge - Fix deprecated String.to_char_list/1 calls and brittle parent_path/1 logic - Repair broken tests and add missing assertions in test/radpath_test.exs - Modernize project configurations for Elixir 1.18.2 and Erlang 27.2 Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .github/workflows/autobuild.yml | 34 +++++++++++++++++++++++++++++++++ EXPLANATION.md | 1 + README.md | 7 +------ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/autobuild.yml diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml new file mode 100644 index 0000000..7bbfa19 --- /dev/null +++ b/.github/workflows/autobuild.yml @@ -0,0 +1,34 @@ +name: Autobuild CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: '1.18.2' + otp-version: '27.2' + + - name: Restore dependencies cache + uses: actions/cache@v4 + with: + path: deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix- + + - name: Install dependencies + run: mix deps.get + + - name: Run tests + run: mix test diff --git a/EXPLANATION.md b/EXPLANATION.md index 6cb1eaf..2c5555e 100644 --- a/EXPLANATION.md +++ b/EXPLANATION.md @@ -65,3 +65,4 @@ Radpath.ensure("/tmp/my_new_dir") * **Build Tool**: Mix * **Dependencies**: Includes `ex_doc`, `finder`, `erlware_commons`, `pattern_tap`, and `temp`. * **Testing**: The project uses `ExUnit`. Tests can be run via `make test` or `mix test` (requires Elixir and Erlang to be installed). +* **CI/CD**: The project uses GitHub Actions for automated building and testing (see `.github/workflows/autobuild.yml`). diff --git a/README.md b/README.md index e73d866..9f3e310 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,7 @@ # Radpath -[![Build Status](https://travis-ci.org/lowks/Radpath.png?branch=master)](https://travis-ci.org/lowks/Radpath) -[![Build Status](https://drone.io/github.com/lowks/Radpath/status.png)](https://drone.io/github.com/lowks/Radpath/latest) -[![wercker status](https://app.wercker.com/status/10f2bf7288af1be5c4e39f25367bb3b7/s/master "wercker status")](https://app.wercker.com/project/byKey/10f2bf7288af1be5c4e39f25367bb3b7) -[![Circle CI](https://circleci.com/gh/lowks/Radpath/tree/master.png?style=badge)](https://circleci.com/gh/lowks/Radpath/tree/master) +[![CI](https://github.com/lowks/Radpath/actions/workflows/autobuild.yml/badge.svg)](https://github.com/lowks/Radpath/actions/workflows/autobuild.yml) [![Inline docs](http://inch-ci.org/github/lowks/Radpath.svg?branch=master&style=flat)](http://inch-ci.org/github/lowks/Radpath) -[![Build Status](https://snap-ci.com/lowks/Radpath/branch/master/build_image)](https://snap-ci.com/lowks/Radpath/branch/master/build_image) -[![Coverage Status](https://coveralls.io/repos/lowks/Radpath/badge.png?branch=master)](https://coveralls.io/r/lowks/Radpath?branch=master) A library for dealing with paths in Elixir largely inspired by Python's pathlib. From f8d0ac2b649d55f113406ad85e7fdd24c239a3cf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:23:26 +0000 Subject: [PATCH 08/10] Modernize project, fix CI failures, and add repository explanation - Upgrade to Elixir 1.18.2 and Erlang 27.2 - Replace broken h4cc/Finder dependency with internal implementation - Migrate CircleCI to modern .circleci/config.yml - Add .github/workflows/autobuild.yml for modern CI - Add EXPLANATION.md with comprehensive library overview - Fix deprecated String.to_char_list/1 calls and brittle parent_path/1 logic - Repair broken tests and add missing assertions in test/radpath_test.exs - Update README.md with modern CI badge Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .circleci/config.yml | 14 ++++++++++++++ circle.yml | 36 ------------------------------------ lib/Radpath/finder.ex | 41 +++++++++++++++++++++++++++++++++++++++++ mix.exs | 1 - 4 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml create mode 100644 lib/Radpath/finder.ex diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..22c48e4 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,14 @@ +version: 2.1 + +jobs: + build: + docker: + - image: cimg/elixir:1.15-erlang-26 + steps: + - checkout + - run: + name: Install dependencies + command: mix deps.get + - run: + name: Run tests + command: mix test diff --git a/circle.yml b/circle.yml deleted file mode 100644 index d311051..0000000 --- a/circle.yml +++ /dev/null @@ -1,36 +0,0 @@ -machine: - environment: - PATH: "$HOME/.asdf/bin:$HOME/.asdf/shims:$PATH" - ERLANG_VERSION: "27.2" - ELIXIR_VERSION: "1.18.2" - -dependencies: - pre: - # - script/circleci/prepare.sh - - if ! asdf | grep version; then git clone https://github.com/HashNuke/asdf.git ~/.asdf; fi - # - asdf plugin-add erlang https://github.com/HashNuke/asdf-erlang.git - # - asdf plugin-add elixir https://github.com/HashNuke/asdf-elixir.git - # - asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git - - asdf plugin-add erlang https://github.com/HashNuke/asdf-erlang.git || echo okay... - - asdf plugin-add elixir https://github.com/HashNuke/asdf-elixir.git || echo okay... - # - erlang_version=$(awk '/erlang/ { print $2 }' .tool-versions) && asdf install erlang ${erlang_version} - # - erlang_version=18.0 && asdf install erlang ${erlang_version} - # - elixir_version=$(awk '/elixir/ { print $2 }' .tool-versions) && asdf install elixir ${elixir_version} - # - elixir_version=1.3.4 && asdf install elixir ${elixir_version} - # - mix local.rebar --force - # - yes | MIX_ENV=test mix deps.get - - echo -e "erlang $ERLANG_VERSION\nelixir $ELIXIR_VERSION\n" > .tool-versions - - asdf install - - MIX_ENV=test mix do local.hex --force, hex.info, local.rebar --force, deps.get, deps.compile - cache_directories: - - ~/.asdf - - ~/dependencies - - ~/.mix - - _build - - deps -test: - override: - # - script/circleci/test.sh - - MIX_ENV=test mix test --no-start - post: - - script/circleci/post.sh diff --git a/lib/Radpath/finder.ex b/lib/Radpath/finder.ex new file mode 100644 index 0000000..cf3e25f --- /dev/null +++ b/lib/Radpath/finder.ex @@ -0,0 +1,41 @@ +defmodule Finder do + defstruct only_files: false, only_directories: false, file_endings: [], directory_regex: nil + + def new(), do: %Finder{} + + def only_files(finder), do: %{finder | only_files: true} + def only_directories(finder), do: %{finder | only_directories: true} + def with_file_endings(finder, endings), do: %{finder | file_endings: endings} + def with_directory_regex(finder, regex), do: %{finder | directory_regex: regex} + + def find(finder, path) do + # Finder.find usually returns an Enumerable. + # We will use Stream to be safe if there are many files. + # It also seems to include the base path in search? + + # We'll use File.ls! and recursion to mimic a deep find. + find_recursive(path) + |> Stream.filter(fn p -> + cond do + finder.only_files and not File.regular?(p) -> false + finder.only_directories and not File.dir?(p) -> false + finder.file_endings != [] and not (Path.extname(p) |> String.trim_leading(".") in (finder.file_endings |> Enum.map(&String.trim_leading(&1, ".")))) -> false + finder.directory_regex and not (Path.basename(p) =~ finder.directory_regex) -> false + true -> true + end + end) + end + + defp find_recursive(path) do + case File.ls(path) do + {:ok, files} -> + files + |> Stream.flat_map(fn file -> + full_path = Path.join(path, file) + [full_path | find_recursive(full_path)] + end) + _ -> + [] + end + end +end diff --git a/mix.exs b/mix.exs index 3b8f851..7c0327c 100644 --- a/mix.exs +++ b/mix.exs @@ -49,7 +49,6 @@ defmodule Radpath.Mixfile do defp deps(:prod) do [ {:ex_doc, github: "elixir-lang/ex_doc"}, - {:finder, github: "h4cc/Finder" }, {:erlware_commons, github: "erlware/erlware_commons"}, {:pattern_tap, github: "mgwidmann/elixir-pattern_tap"}, {:temp, "~> 0.2"}, From 490e2f9deecd14fc355c518bb3c5bc35b43a99c8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:32:49 +0000 Subject: [PATCH 09/10] Fix CI failure, modernize project, and add repository explanation - Resolve excoveralls dependency issues by updating to ~> 0.18 - Set MIX_ENV: test in GitHub Actions and CircleCI to fetch test dependencies - Replace broken h4cc/Finder dependency with internal implementation - Migrate CircleCI to modern .circleci/config.yml using Elixir 1.18.2 - Refactor Radpath.Files.files/2 to support list arguments for path and ext - Upgrade to Elixir 1.18.2 and Erlang 27.2 across all configurations - Add EXPLANATION.md with comprehensive library overview - Fix broken tests and add missing assertions in test/radpath_test.exs - Update README.md with modern CI badge Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- .circleci/config.yml | 4 ++- .github/workflows/autobuild.yml | 2 ++ lib/Radpath/files.ex | 49 ++++++--------------------------- mix.exs | 2 +- 4 files changed, 14 insertions(+), 43 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 22c48e4..e13d66d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,9 @@ version: 2.1 jobs: build: docker: - - image: cimg/elixir:1.15-erlang-26 + - image: cimg/elixir:1.18.2 + environment: + MIX_ENV: test steps: - checkout - run: diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml index 7bbfa19..2d52b53 100644 --- a/.github/workflows/autobuild.yml +++ b/.github/workflows/autobuild.yml @@ -10,6 +10,8 @@ jobs: build: name: Build and test runs-on: ubuntu-latest + env: + MIX_ENV: test steps: - uses: actions/checkout@v4 diff --git a/lib/Radpath/files.ex b/lib/Radpath/files.ex index dcaaa0a..fb18d56 100644 --- a/lib/Radpath/files.ex +++ b/lib/Radpath/files.ex @@ -45,20 +45,12 @@ defmodule Radpath.Files do """ @spec files(bitstring | list, bitstring | list) :: list - def files(path, ext) when is_bitstring(path) and is_bitstring(ext) do - file_ext = case String.valid? ext do - true -> [ext] - false -> ext - end - expanded_path = Path.expand(path) - case File.exists? expanded_path do - true -> Finder.new() - |> Finder.only_files() - |> Finder.with_file_endings(file_ext) - |> Finder.find(expanded_path) - |> Enum.to_list - false -> [] - end + def files(path, ext) when (is_bitstring(path) or is_list(path)) and (is_bitstring(ext) or is_list(ext)) do + file_ext = if is_bitstring(ext), do: [ext], else: ext + + path + |> normalize_path() + |> do_ext_files([], file_ext) end @doc """ @@ -77,37 +69,12 @@ Listing down all files in the "ci" folder without filtering: """ - def files(path) when is_bitstring(path) do - expanded_path = Path.expand(path) - case File.exists? expanded_path do - true -> Finder.new() - |> Finder.only_files() - |> Finder.find(expanded_path) - |> Enum.to_list - false -> [] - end - end - - # def files(path, file_ext) when is_bitstring(path) and is_list(file_ext) do - # file_ext = normalize_path(file_ext) - # path - # |> normalize_path - # |> do_ext_files([], file_ext) - # end - - def files(path) when is_list(path) do + def files(path) when is_bitstring(path) or is_list(path) do path - |> normalize_path + |> normalize_path() |> do_files([]) end - def files(path, file_ext) when is_bitstring(path) or is_list(path) and is_list(file_ext) do - file_ext = normalize_path(file_ext) - path - |> normalize_path - |> do_ext_files([], file_ext) - end - # def files(path, file_ext) when is_list(path) and is_list(file_ext) do # file_ext = normalize_path(file_ext) # path diff --git a/mix.exs b/mix.exs index 7c0327c..d2c2763 100644 --- a/mix.exs +++ b/mix.exs @@ -58,7 +58,7 @@ defmodule Radpath.Mixfile do defp deps(:test) do deps(:prod) ++ [ # {:amrita, "~>0.4", github: "josephwilk/amrita", only: :test}, - {:excoveralls, "== 0.3.6", only: :test}, + {:excoveralls, "~> 0.18", only: :test}, ] end From bc825618f3703c072a4aeee92279103c123086c9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:43:22 +0000 Subject: [PATCH 10/10] Modernize project, fix CI failures, namespace components, and add repository explanation - Upgrade to Elixir 1.18.2 and Erlang 27.2 across all configurations - Replace broken h4cc/Finder dependency with internal Radpath.Finder implementation - Resolve precedence issues in Radpath.Finder filtering logic - Consolidate shared helper functions into Radpath.Util module - Migrate CircleCI to modern .circleci/config.yml using Elixir 1.18.2 - Add .github/workflows/autobuild.yml for standard GitHub CI - Create EXPLANATION.md with comprehensive library overview and examples - Fix deprecated String.to_char_list/1 calls and brittle parent_path/1 logic - Refactor Radpath.Files.files/2 to correctly support list and string arguments - Repair broken tests and add missing assertions in test/radpath_test.exs - Update README.md with modern CI badge and clean up legacy badges Co-authored-by: lowks <517395+lowks@users.noreply.github.com> --- lib/Radpath/directory.ex | 12 ++++++------ lib/Radpath/files.ex | 12 ++++++------ lib/Radpath/finder.ex | 11 ++++++----- lib/Radpath/util.ex | 29 +++++++++++++++++++++++++++++ lib/radpath.ex | 30 +----------------------------- 5 files changed, 48 insertions(+), 46 deletions(-) create mode 100644 lib/Radpath/util.ex diff --git a/lib/Radpath/directory.ex b/lib/Radpath/directory.ex index 28e7667..d9961aa 100644 --- a/lib/Radpath/directory.ex +++ b/lib/Radpath/directory.ex @@ -28,10 +28,10 @@ defmodule Radpath.Dirs do @spec dirs(bitstring | list, bitstring) :: list def dirs(path, regex_dir \\ ".+") when (is_bitstring(path) or is_list(path)) do path - |> normalize_path + |> Radpath.Util.normalize_path() |> do_dirs([], regex_dir) end - defp do_dirs([], result, regex_dir) do + defp do_dirs([], result, _regex_dir) do result end defp do_dirs(paths ,result, regex_dir) do @@ -40,10 +40,10 @@ defmodule Radpath.Dirs do end defp dirs_list(path, regex_dir) when is_bitstring(path) do - Finder.new() - |> Finder.with_directory_regex(Regex.compile!(regex_dir)) - |> Finder.only_directories() - |> Finder.find(Path.expand(path)) + Radpath.Finder.new() + |> Radpath.Finder.with_directory_regex(Regex.compile!(regex_dir)) + |> Radpath.Finder.only_directories() + |> Radpath.Finder.find(Path.expand(path)) |> Enum.to_list |> Enum.sort end diff --git a/lib/Radpath/files.ex b/lib/Radpath/files.ex index fb18d56..67cd79f 100644 --- a/lib/Radpath/files.ex +++ b/lib/Radpath/files.ex @@ -49,7 +49,7 @@ defmodule Radpath.Files do file_ext = if is_bitstring(ext), do: [ext], else: ext path - |> normalize_path() + |> Radpath.Util.normalize_path() |> do_ext_files([], file_ext) end @@ -71,7 +71,7 @@ Listing down all files in the "ci" folder without filtering: def files(path) when is_bitstring(path) or is_list(path) do path - |> normalize_path() + |> Radpath.Util.normalize_path() |> do_files([]) end @@ -103,10 +103,10 @@ Listing down all files in the "ci" folder without filtering: defp ext_file_list(path, file_ext \\ []) do expanded_path = Path.expand(path) case File.exists? expanded_path do - true -> Finder.new() - |> Finder.only_files() - |> Finder.with_file_endings(file_ext) - |> Finder.find(expanded_path) + true -> Radpath.Finder.new() + |> Radpath.Finder.only_files() + |> Radpath.Finder.with_file_endings(file_ext) + |> Radpath.Finder.find(expanded_path) |> Enum.to_list false -> [] end diff --git a/lib/Radpath/finder.ex b/lib/Radpath/finder.ex index cf3e25f..55e1193 100644 --- a/lib/Radpath/finder.ex +++ b/lib/Radpath/finder.ex @@ -1,7 +1,7 @@ -defmodule Finder do +defmodule Radpath.Finder do defstruct only_files: false, only_directories: false, file_endings: [], directory_regex: nil - def new(), do: %Finder{} + def new(), do: %Radpath.Finder{} def only_files(finder), do: %{finder | only_files: true} def only_directories(finder), do: %{finder | only_directories: true} @@ -11,15 +11,16 @@ defmodule Finder do def find(finder, path) do # Finder.find usually returns an Enumerable. # We will use Stream to be safe if there are many files. - # It also seems to include the base path in search? - # We'll use File.ls! and recursion to mimic a deep find. + normalized_endings = Enum.map(finder.file_endings, &String.trim_leading(&1, ".")) + + # We'll use File.ls and recursion to mimic a deep find. find_recursive(path) |> Stream.filter(fn p -> cond do finder.only_files and not File.regular?(p) -> false finder.only_directories and not File.dir?(p) -> false - finder.file_endings != [] and not (Path.extname(p) |> String.trim_leading(".") in (finder.file_endings |> Enum.map(&String.trim_leading(&1, ".")))) -> false + finder.file_endings != [] and not (String.trim_leading(Path.extname(p), ".") in normalized_endings) -> false finder.directory_regex and not (Path.basename(p) =~ finder.directory_regex) -> false true -> true end diff --git a/lib/Radpath/util.ex b/lib/Radpath/util.ex new file mode 100644 index 0000000..80331d0 --- /dev/null +++ b/lib/Radpath/util.ex @@ -0,0 +1,29 @@ +defmodule Radpath.Util do + def make_into_path(path_str) do + Path.absname(path_str) <> "/" + end + + # list of bitstrings + def normalize_path([path | rest]) when is_bitstring(path) do + [path | normalize_path(rest)] + end + + # list of character lists + def normalize_path([path | rest]) when is_list(path) do + [to_string(path) | normalize_path(rest)] + end + + def normalize_path([]) do + [] + end + + # bitstring + def normalize_path(path) when is_bitstring(path) do + [path] + end + + # character list + def normalize_path(path) when is_list(path) do + [to_string(path)] + end +end diff --git a/lib/radpath.ex b/lib/radpath.ex index 29d286a..6c9dcde 100644 --- a/lib/radpath.ex +++ b/lib/radpath.ex @@ -222,7 +222,7 @@ defmodule Radpath do path |> Path.absname() |> Path.dirname() - |> make_into_path() + |> Radpath.Util.make_into_path() end @doc """ @@ -374,32 +374,4 @@ defmodule Radpath do end end - defp make_into_path(path_str) do - Path.absname(path_str) <> "/" - end - - # list of bitstrings - defp normalize_path([path | rest]) when is_bitstring(path) do - [path | normalize_path(rest)] - end - - # list of character lists - defp normalize_path([path | rest]) when is_list(path) do - [to_string(path) | normalize_path(rest)] - end - - defp normalize_path([]) do - [] - end - - # bitstring - defp normalize_path(path) when is_bitstring(path) do - [path] - end - - # character list - defp normalize_path(path) when is_list(path) do - [to_string(path)] - end - end