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

nightconcept / almandine / 14740114043

29 Apr 2025 07:54PM UTC coverage: 82.069% (-0.7%) from 82.759%
14740114043

push

github

web-flow
fix: Lock file generation and Mac/Linux installs (#9)

84 of 96 new or added lines in 5 files covered. (87.5%)

4 existing lines in 2 files now uncovered.

1579 of 1924 relevant lines covered (82.07%)

2.38 hits per line

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

87.88
/src/utils/lockfile.lua
1
--[[
2
  Lockfile Utility Module
3

4
  Centralizes all lockfile operations for Almandine. Provides functions to generate, serialize, write,
5
  and modify the lockfile (almd-lock.lua) in a consistent, reusable way.
6
]]--
7

8
--- Lockfile schema version (increment if schema changes)
9
local API_VERSION = "1"
3✔
10

11
local lockfile = {}
3✔
12
local io = io
3✔
13
local tostring = tostring
3✔
14

15
---
16
-- Generates a lockfile table from resolved dependencies.
17
-- @param resolved_deps table Table of resolved dependencies. Each key is a package name, value is a table with fields:
18
--                          - version (string, optional)
19
--                          - hash (string, required)
20
--                          - source (string, optional)
21
-- @return table Lockfile table matching the schema
22
function lockfile.generate_lockfile_table(resolved_deps)
3✔
23
  assert(type(resolved_deps) == "table", "resolved_deps must be a table")
9✔
24
  local pkgs = {}
8✔
25
  for name, dep in pairs(resolved_deps) do
17✔
26
    assert(type(dep) == "table", "Dependency entry must be a table")
11✔
27
    assert(dep.hash, "Dependency '" .. name .. "' must have a hash")
10✔
28
    local entry = { hash = dep.hash }
9✔
29
    if dep.version then entry.version = dep.version end
9✔
30
    if dep.source then entry.source = dep.source end
9✔
31
    pkgs[name] = entry
9✔
32
  end
33
  return {
6✔
34
    api_version = API_VERSION,
6✔
35
    package = pkgs,
6✔
36
  }
6✔
37
end
38

39
---
40
-- Serializes a lockfile table to a string (Lua syntax).
41
-- @param lockfile_table table Lockfile table
42
-- @return string Lua code as string
43
function lockfile.serialize_lockfile(lockfile_table)
3✔
44
  assert(type(lockfile_table) == "table", "lockfile_table must be a table")
9✔
45
  local function serialize(tbl, indent)
46
    indent = indent or 0
26✔
47
    local pad = string.rep("  ", indent)
26✔
48
    local lines = { "{" }
26✔
49
    for k, v in pairs(tbl) do
69✔
50
      local key = (type(k) == "string" and string.format("%s = ", k)) or ("[" .. tostring(k) .. "] = ")
43✔
51
      if type(v) == "table" then
43✔
52
        local serialized = serialize(v, indent + 1)
18✔
53
        table.insert(lines, pad .. "  " .. key .. serialized .. ",")
18✔
54
      elseif type(v) == "string" then
25✔
55
        table.insert(lines, pad .. "  " .. key .. string.format('"%s"', v) .. ",")
25✔
56
      else
NEW
57
        table.insert(lines, pad .. "  " .. key .. tostring(v) .. ",")
×
58
      end
59
    end
60
    table.insert(lines, pad .. "}")
26✔
61
    return table.concat(lines, "\n")
26✔
62
  end
63
  local result = "return " .. serialize(lockfile_table, 0) .. "\n"
8✔
64
  return result
8✔
65
end
66

67
---
68
-- Writes the lockfile to disk as `almd-lock.lua`.
69
-- @param lockfile_table table Lockfile table
70
-- @param path string (optional) Path to write to (default: "almd-lock.lua" in project root)
71
-- @return boolean, string True and path if successful, false and error message otherwise
72
function lockfile.write_lockfile(lockfile_table, path)
3✔
73
  path = path or "almd-lock.lua"
7✔
74
  local content = lockfile.serialize_lockfile(lockfile_table)
7✔
75
  local file, err = io.open(path, "w")
7✔
76
  if not file then return false, err end
7✔
77
  file:write(content)
6✔
78
  file:close()
6✔
79
  return true, path
6✔
80
end
81

82
---
83
-- Removes a dependency from the lockfile on disk.
84
-- @param dep_name string Name of dependency to remove
85
-- @param path string (optional) Path to lockfile (default: "almd-lock.lua")
86
-- @return boolean, string True if successful, false and error message otherwise
87
function lockfile.remove_dep_from_lockfile(dep_name, path)
3✔
88
  path = path or "almd-lock.lua"
4✔
89
  local chunk = loadfile(path)
4✔
90
  if not chunk then return false, "Lockfile not found" end
4✔
NEW
91
  local ok, lock = pcall(chunk)
×
NEW
92
  if not ok or type(lock) ~= "table" or type(lock.package) ~= "table" then
×
NEW
93
    return false, "Malformed lockfile"
×
94
  end
NEW
95
  if lock.package[dep_name] then
×
NEW
96
    lock.package[dep_name] = nil
×
NEW
97
    return lockfile.write_lockfile(lock, path)
×
98
  end
NEW
99
  return true, path -- No-op if dep not present
×
100
end
101

102
---
103
-- Updates the lockfile from a manifest loader function.
104
-- @param load_manifest function Function to load the manifest
105
-- @return boolean, string True if successful, false and error message otherwise
106
function lockfile.update_lockfile_from_manifest(load_manifest)
3✔
107
  local manifest, err = load_manifest()
3✔
108
  if not manifest then
3✔
109
    return false, err or "Could not load manifest"
1✔
110
  end
111
  local resolved_deps = {}
2✔
112
  for name, dep in pairs(manifest.dependencies or {}) do
6✔
113
    local dep_entry = type(dep) == "table" and dep or { url = dep }
4✔
114
    -- Compute hash (placeholder: use URL as hash; replace with real hash logic if available)
115
    local hash = dep_entry.url or tostring(dep)
4✔
116
    resolved_deps[name] = { hash = hash, source = dep_entry.url or tostring(dep) }
4✔
117
  end
118
  local lockfile_table = lockfile.generate_lockfile_table(resolved_deps)
2✔
119
  return lockfile.write_lockfile(lockfile_table)
2✔
120
end
121

122
return lockfile
3✔
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