layero 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -196,6 +196,13 @@ async function packAndUpload(api, cwd, project, prebuiltDir) {
196
196
  sha256: pack.sha256,
197
197
  ...(prebuiltDir ? { prebuilt_dir: prebuiltDir } : {}),
198
198
  });
199
+ // A gitignored lockfile is force-included so the build can do a frozen
200
+ // install; warn (to stderr, off the --json stdout stream) that the ignore
201
+ // rule was overridden for it.
202
+ if (pack.forcedLockfiles?.length) {
203
+ process.stderr.write(chalk.yellow(`! including gitignored lockfile(s) so the build uses a frozen install: ` +
204
+ `${pack.forcedLockfiles.join(", ")}\n`));
205
+ }
199
206
  const init = await api.initUpload(project.id);
200
207
  emit({ event: "uploading" });
201
208
  await uploadArchive(init, pack.archivePath);
package/dist/pack.js CHANGED
@@ -41,11 +41,26 @@ async function readIgnoreFile(filePath) {
41
41
  throw err;
42
42
  }
43
43
  }
44
+ // Lockfiles the server needs for a frozen/reproducible install. A gitignored
45
+ // (or .layeroignore'd) lockfile would otherwise be dropped from the upload →
46
+ // the builder falls back to a non-frozen install (re-resolves deps). Force them
47
+ // back in with negation patterns applied AFTER the ignore layers. They live at
48
+ // repo root, so no ignored parent dir can block the re-include.
49
+ const FORCE_INCLUDE_LOCKFILES = [
50
+ "bun.lockb",
51
+ "bun.lock",
52
+ "package-lock.json",
53
+ "yarn.lock",
54
+ "pnpm-lock.yaml",
55
+ "npm-shrinkwrap.json",
56
+ ];
44
57
  async function buildIgnore(cwd) {
45
58
  const ig = ignore();
46
59
  ig.add(DEFAULT_IGNORE);
47
60
  ig.add(await readIgnoreFile(path.join(cwd, ".gitignore")));
48
61
  ig.add(await readIgnoreFile(path.join(cwd, ".layeroignore")));
62
+ // Negations last so they win over any ignore rule above.
63
+ ig.add(FORCE_INCLUDE_LOCKFILES.map((f) => `!/${f}`));
49
64
  return ig;
50
65
  }
51
66
  async function walk(cwd, ig) {
@@ -80,6 +95,30 @@ async function sha256OfFile(p) {
80
95
  }
81
96
  return hash.digest("hex");
82
97
  }
98
+ /** Lockfiles present on disk that WOULD have been dropped by the ignore rules
99
+ * if not force-included. Built from the same ignore layers minus the lockfile
100
+ * negations, so it reflects exactly what the user's .gitignore/.layeroignore
101
+ * would have excluded. */
102
+ async function detectForcedLockfiles(cwd) {
103
+ const probe = ignore();
104
+ probe.add(DEFAULT_IGNORE);
105
+ probe.add(await readIgnoreFile(path.join(cwd, ".gitignore")));
106
+ probe.add(await readIgnoreFile(path.join(cwd, ".layeroignore")));
107
+ const forced = [];
108
+ for (const lf of FORCE_INCLUDE_LOCKFILES) {
109
+ if (!probe.ignores(lf))
110
+ continue; // not ignored → nothing forced
111
+ try {
112
+ const st = await fs.stat(path.join(cwd, lf));
113
+ if (st.isFile())
114
+ forced.push(lf);
115
+ }
116
+ catch {
117
+ // absent on disk — nothing to force-include
118
+ }
119
+ }
120
+ return forced;
121
+ }
83
122
  /** Pack only the contents of an already-built artifact directory.
84
123
  *
85
124
  * Used by `layero deploy --prebuilt`. We deliberately bypass DEFAULT_IGNORE
@@ -139,6 +178,7 @@ export async function packCwd(cwd, projectName) {
139
178
  throw new Error("no files to upload after applying .gitignore/.layeroignore — " +
140
179
  "check that you're in the right directory");
141
180
  }
181
+ const forcedLockfiles = await detectForcedLockfiles(cwd);
142
182
  const stamp = Date.now();
143
183
  const safeName = projectName.replace(/[^a-zA-Z0-9._-]/g, "_");
144
184
  const archivePath = path.join(tmpdir(), `layero-${safeName}-${stamp}.tgz`);
@@ -159,5 +199,11 @@ export async function packCwd(cwd, projectName) {
159
199
  "Add large directories to .layeroignore.");
160
200
  }
161
201
  const sha256 = await sha256OfFile(archivePath);
162
- return { archivePath, sha256, size: stat.size, fileCount: files.length };
202
+ return {
203
+ archivePath,
204
+ sha256,
205
+ size: stat.size,
206
+ fileCount: files.length,
207
+ ...(forcedLockfiles.length ? { forcedLockfiles } : {}),
208
+ };
163
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "layero",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Layero CLI — publish a local site with one command. No git, no GitHub, agent-friendly (Cursor, Claude Code).",
5
5
  "license": "MIT",
6
6
  "type": "module",