• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nightconcept / almandine / 14748071543

30 Apr 2025 06:21AM UTC coverage: 68.293% (-12.0%) from 80.336%
14748071543

push

github

web-flow
fix: add (#13)

30 of 263 new or added lines in 4 files covered. (11.41%)

27 existing lines in 3 files now uncovered.

1344 of 1968 relevant lines covered (68.29%)

1.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.02
/src/modules/add.lua
1
--[[
2
  add
3
  @module add
4

5
  Provides functionality to add a dependency to the project manifest and download it to a designated directory.
6
]]
7

8
-- local filesystem_utils = require("utils.filesystem")
9

10
--- Normalize GitHub URLs by converting blob URLs to raw URLs.
11
--- @param url string The URL to normalize
12
--- @return string normalized_url The normalized URL
13
--- @return string download_url The URL to use for downloading
14
local function normalize_github_url(url)
15
  -- Check if this is a GitHub blob URL
16
  -- Correctly capture username, repo, commit, and path directly from the URL match
NEW
17
  local username, repo, commit, path = url:match("^https://github%.com/([^/]+)/([^/]+)/blob/([^/]+)/(.+)$")
×
NEW
18
  if username then -- Check if the match was successful and captured groups are not nil
×
19
    -- Convert to raw URL
NEW
20
    local raw_url = string.format("https://raw.githubusercontent.com/%s/%s/%s/%s", username, repo, commit, path)
×
NEW
21
    return url, raw_url
×
22
  end
23
  -- If not a GitHub blob URL, use as-is
NEW
24
  return url, url
×
25
end
26

27
---@class AddDeps
28
---@field load_manifest fun(): table, string?
29
---@field save_manifest fun(manifest: table): boolean, string?
30
---@field ensure_lib_dir fun(): nil
31
---@field downloader table
32
---@field downloader.download fun(url: string, path: string): boolean, string?
33
---@field hash_utils table
34
---@field hash_utils.hash_dependency fun(dep: string|table): string, string?
35
---@field lockfile table
36
---@field lockfile.generate_lockfile_table fun(deps: table): table
37
---@field lockfile.write_lockfile fun(table): boolean, string?
38

39
---@param dep_name string|nil Dependency name to add. If nil, inferred from source URL.
40
---@param dep_source string|table Dependency source string (URL) or table with url/path.
41
---@param dest_dir string|nil Optional destination directory for the installed file.
42
---@param deps AddDeps Table containing dependency injected functions.
43
---@return boolean success True if operation completed successfully.
44
---@return string? error Error message if operation failed.
45
local function add_dependency(dep_name, dep_source, _dest_dir, deps)
NEW
46
  deps.ensure_lib_dir()
×
NEW
47
  local manifest, err = deps.load_manifest()
×
UNCOV
48
  if not manifest then
×
UNCOV
49
    print(err)
×
NEW
50
    return false, err
×
51
  end
52

53
  -- If no dep_name provided, try to infer from URL
NEW
54
  if not dep_name then
×
NEW
55
    if type(dep_source) == "string" then
×
NEW
56
      local url = dep_source
×
57
      -- Try to extract name from URL
NEW
58
      local name = url:match("/([^/]+)%.lua$")
×
NEW
59
      if name then
×
NEW
60
        dep_name = name
×
61
      else
NEW
62
        return false, "Could not infer dependency name from URL"
×
63
      end
64
    else
NEW
65
      return false, "Dependency name must be provided when source is a table"
×
66
    end
67
  end
68

69
  -- If dep_source is a table, extract URL and path
70
  local url, out_path
NEW
71
  if type(dep_source) == "table" then
×
NEW
72
    url = dep_source.url
×
NEW
73
    out_path = dep_source.path or dep_name .. ".lua"
×
74
  else
NEW
75
    url = dep_source
×
NEW
76
    out_path = dep_name .. ".lua"
×
77
  end
78

79
  -- Normalize GitHub URLs
NEW
80
  local source_url, download_url = normalize_github_url(url)
×
NEW
81
  if not download_url then
×
NEW
82
    return false, "Failed to normalize URL: " .. (source_url or "")
×
83
  end
NEW
84
  manifest.dependencies[dep_name] = dep_source
×
85

NEW
86
  local ok, err2 = deps.save_manifest(manifest)
×
UNCOV
87
  if not ok then
×
UNCOV
88
    print(err2)
×
NEW
89
    return false, err2
×
90
  end
UNCOV
91
  print(string.format("Added dependency '%s' to project.lua.", dep_name))
×
92

93
  -- Download using the raw URL
NEW
94
  local ok3, err3 = deps.downloader.download(download_url, out_path)
×
UNCOV
95
  if ok3 then
×
UNCOV
96
    print(string.format("Downloaded %s to %s", dep_name, out_path))
×
97
  else
UNCOV
98
    print(string.format("Failed to download %s: %s", dep_name, err3))
×
NEW
99
    return false, err3
×
100
  end
101

102
  -- Generate and write lockfile after successful add
103
  -- Build resolved_deps table for lockfile with proper hashes
UNCOV
104
  local resolved_deps = {}
×
UNCOV
105
  for name, dep in pairs(manifest.dependencies or {}) do
×
UNCOV
106
    local lock_dep_entry = type(dep) == "table" and dep or { url = dep }
×
NEW
107
    local hash, hash_err = deps.hash_utils.hash_dependency(dep)
×
NEW
108
    if not hash then
×
NEW
109
      print("Warning: Could not generate hash for " .. name .. ": " .. tostring(hash_err))
×
NEW
110
      hash = "unknown" -- Don't use URL as fallback anymore
×
111
    end
NEW
112
    resolved_deps[name] = {
×
113
      hash = hash,
114
      source = lock_dep_entry.url or tostring(dep),
115
    }
116
  end
NEW
117
  local lockfile_table = deps.lockfile.generate_lockfile_table(resolved_deps)
×
NEW
118
  local ok_lock, err_lock = deps.lockfile.write_lockfile(lockfile_table)
×
UNCOV
119
  if ok_lock then
×
UNCOV
120
    print("Updated lockfile: almd-lock.lua")
×
121
  else
UNCOV
122
    print("Failed to update lockfile: " .. tostring(err_lock))
×
123
  end
NEW
124
  return true
×
125
end
126

127
---Prints usage/help information for the `add` command.
128
---Usage: almd add <source> [-d <dir>] [-n <dep_name>]
129
---@return string Usage string for the add command.
130
local function help_info()
NEW
131
  return [[
×
132
Usage: almd add <source> [-d <dir>] [-n <dep_name>]
133

134
Options:
135
  -d <dir>     Destination directory for the installed file
136
  -n <name>    Name of the dependency (optional, inferred from URL if not provided)
137

138
Example:
139
  almd add https://example.com/lib.lua
140
  almd add https://example.com/lib.lua -d src/lib/custom
141
  almd add https://example.com/lib.lua -n mylib
NEW
142
]]
×
143
end
144

145
return {
1✔
146
  add_dependency = add_dependency,
1✔
147
  help_info = help_info,
1✔
148
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc