percher 0.5.9 → 0.5.10
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.
- package/dist/bin/percher.js +52 -18
- package/dist/package.json +2 -2
- package/dist/templates/astro/Dockerfile +35 -34
- package/dist/templates/astro/src/pages/index.astro +1 -1
- package/dist/templates/eleventy/Dockerfile +35 -34
- package/dist/templates/hugo/Dockerfile +33 -32
- package/dist/templates/laravel/Dockerfile +5 -1
- package/dist/templates/nextjs/next.config.mjs +8 -0
- package/dist/templates/nextjs/package.json +6 -0
- package/dist/templates/preact/Dockerfile +35 -34
- package/dist/templates/python/nixpacks.toml +8 -0
- package/dist/templates/python/percher.toml +1 -0
- package/dist/templates/rust/Dockerfile +5 -1
- package/dist/templates/solid/Dockerfile +35 -34
- package/dist/templates/vite-react/Dockerfile +35 -34
- package/dist/templates/vite-vue/Dockerfile +35 -34
- package/package.json +2 -2
- package/dist/templates/nextjs/next.config.ts +0 -5
- package/dist/templates/node/index.ts +0 -12
package/dist/bin/percher.js
CHANGED
|
@@ -90873,10 +90873,6 @@ var CAC = class extends EventTarget {
|
|
|
90873
90873
|
};
|
|
90874
90874
|
var cac = (name = "") => new CAC(name);
|
|
90875
90875
|
|
|
90876
|
-
// ../core/dist/app-name.js
|
|
90877
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
90878
|
-
import { join } from "node:path";
|
|
90879
|
-
|
|
90880
90876
|
// ../toml/dist/errors.js
|
|
90881
90877
|
class PercherTomlError extends Error {
|
|
90882
90878
|
code;
|
|
@@ -104801,6 +104797,8 @@ function serialize(config2) {
|
|
|
104801
104797
|
return import_toml2.default.stringify(config2);
|
|
104802
104798
|
}
|
|
104803
104799
|
// ../core/dist/app-name.js
|
|
104800
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
104801
|
+
import { join } from "node:path";
|
|
104804
104802
|
function readPercherTomlAppName(cwd) {
|
|
104805
104803
|
const p = join(cwd, "percher.toml");
|
|
104806
104804
|
if (!existsSync(p))
|
|
@@ -105526,7 +105524,7 @@ async function appstoreKit(ctx, input = {}) {
|
|
|
105526
105524
|
const themeHex = themeColorForToken(null);
|
|
105527
105525
|
const base = resolve3(ctx.cwd, input.outDir ?? ".");
|
|
105528
105526
|
const baseRel = relative(ctx.cwd, base);
|
|
105529
|
-
if (baseRel.startsWith(
|
|
105527
|
+
if (baseRel === ".." || baseRel.startsWith(`..${sep}`) || isAbsolute(baseRel)) {
|
|
105530
105528
|
throw new PercherCoreError("Output directory must stay inside the project", {
|
|
105531
105529
|
code: "appstore-kit.bad-out-dir",
|
|
105532
105530
|
hint: "Pass a path inside the current project (e.g. --out public), not an absolute or parent path."
|
|
@@ -109566,7 +109564,8 @@ FROM caddy:2-alpine AS caddy-source
|
|
|
109566
109564
|
FROM alpine:3.21
|
|
109567
109565
|
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
109568
109566
|
RUN apk add --no-cache libcap \\
|
|
109569
|
-
&& setcap -r /usr/bin/caddy \\
|
|
109567
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \\
|
|
109568
|
+
&& test -z "$(getcap /usr/bin/caddy)" \\
|
|
109570
109569
|
&& apk del libcap \\
|
|
109571
109570
|
&& rm -rf /var/cache/apk/*
|
|
109572
109571
|
COPY Caddyfile /etc/caddy/Caddyfile
|
|
@@ -114449,6 +114448,17 @@ async function runDeployWithRetry(opts) {
|
|
|
114449
114448
|
throw lastErr;
|
|
114450
114449
|
}
|
|
114451
114450
|
|
|
114451
|
+
// ../core/dist/toml-recovery.js
|
|
114452
|
+
function tomlErrorToRecoveryProblems(err) {
|
|
114453
|
+
if (err.issues?.length) {
|
|
114454
|
+
return err.issues.map((i) => ({
|
|
114455
|
+
file: "percher.toml",
|
|
114456
|
+
message: `${i.path.join(".") || "(root)"}: ${i.message}`
|
|
114457
|
+
}));
|
|
114458
|
+
}
|
|
114459
|
+
return [{ file: "percher.toml", message: err.message }];
|
|
114460
|
+
}
|
|
114461
|
+
|
|
114452
114462
|
// ../core/dist/commands/env-scan.js
|
|
114453
114463
|
import { readdirSync as readdirSync3, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
|
|
114454
114464
|
import { join as join22 } from "node:path";
|
|
@@ -114964,9 +114974,9 @@ function buildLiveSummary(opts) {
|
|
|
114964
114974
|
const replaced = opts.replacedPreview ? " — replaced previous preview" : "";
|
|
114965
114975
|
return `${opts.appName} ${verb}${fwSuffix} → ${opts.url}${replaced}`;
|
|
114966
114976
|
}
|
|
114967
|
-
async function publish(ctx, input = {}) {
|
|
114977
|
+
async function publish(ctx, input = {}, preParsedConfig) {
|
|
114968
114978
|
try {
|
|
114969
|
-
return await publishInner(ctx, input);
|
|
114979
|
+
return await publishInner(ctx, input, preParsedConfig);
|
|
114970
114980
|
} catch (err) {
|
|
114971
114981
|
const classified = classifyPublishApiError(err, {
|
|
114972
114982
|
configPath: tomlPathFor2(ctx.cwd),
|
|
@@ -114995,7 +115005,7 @@ async function publish(ctx, input = {}) {
|
|
|
114995
115005
|
};
|
|
114996
115006
|
}
|
|
114997
115007
|
}
|
|
114998
|
-
async function publishInner(ctx, input) {
|
|
115008
|
+
async function publishInner(ctx, input, preParsedConfig) {
|
|
114999
115009
|
const t0 = Date.now();
|
|
115000
115010
|
if (!input.dryRun) {
|
|
115001
115011
|
try {
|
|
@@ -115006,7 +115016,7 @@ async function publishInner(ctx, input) {
|
|
|
115006
115016
|
ctx.status("[1/4] Reading config...");
|
|
115007
115017
|
let config2;
|
|
115008
115018
|
try {
|
|
115009
|
-
config2 = await loadOrGenerate(ctx);
|
|
115019
|
+
config2 = await loadOrGenerate(ctx, preParsedConfig);
|
|
115010
115020
|
} catch (err) {
|
|
115011
115021
|
if (err instanceof PercherTomlError) {
|
|
115012
115022
|
if (err.code === "PARSE_ERROR") {
|
|
@@ -115020,7 +115030,7 @@ async function publishInner(ctx, input) {
|
|
|
115020
115030
|
suggestion: "Fix the syntax error and retry publish."
|
|
115021
115031
|
},
|
|
115022
115032
|
recovery: recoveryFixConfig({
|
|
115023
|
-
problems:
|
|
115033
|
+
problems: tomlErrorToRecoveryProblems(err),
|
|
115024
115034
|
reasonCode: "config_invalid"
|
|
115025
115035
|
}),
|
|
115026
115036
|
summary: "percher.toml has invalid TOML syntax — fix and retry.",
|
|
@@ -115029,10 +115039,7 @@ async function publishInner(ctx, input) {
|
|
|
115029
115039
|
};
|
|
115030
115040
|
}
|
|
115031
115041
|
if (err.code === "VALIDATION_ERROR") {
|
|
115032
|
-
const problems = (err
|
|
115033
|
-
file: "percher.toml",
|
|
115034
|
-
message: `${i.path.join(".") || "(root)"}: ${i.message}`
|
|
115035
|
-
}));
|
|
115042
|
+
const problems = tomlErrorToRecoveryProblems(err);
|
|
115036
115043
|
const bullets = problems.map((p) => ` - ${p.message}`).join(`
|
|
115037
115044
|
`);
|
|
115038
115045
|
const count = problems.length;
|
|
@@ -115076,6 +115083,30 @@ ${bullets}`,
|
|
|
115076
115083
|
bundle: { fileCount: 0, bytes: 0 }
|
|
115077
115084
|
};
|
|
115078
115085
|
}
|
|
115086
|
+
if (preParsedConfig && !input.dryRun && !existsSync15(join24(ctx.cwd, "percher.toml"))) {
|
|
115087
|
+
return {
|
|
115088
|
+
status: "failed",
|
|
115089
|
+
fileCount: 0,
|
|
115090
|
+
bytes: 0,
|
|
115091
|
+
error: {
|
|
115092
|
+
title: "percher.toml missing from the project",
|
|
115093
|
+
explanation: "A pre-parsed config was supplied, but no percher.toml exists in the project directory. The file is packaged into the upload, so it must be present on disk for a real deploy.",
|
|
115094
|
+
suggestion: "Run `percher init` (or write percher.toml) before publishing."
|
|
115095
|
+
},
|
|
115096
|
+
recovery: recoveryFixConfig({
|
|
115097
|
+
problems: [
|
|
115098
|
+
{
|
|
115099
|
+
file: "percher.toml",
|
|
115100
|
+
message: "percher.toml must exist on disk for a real upload (it ships in the tarball)."
|
|
115101
|
+
}
|
|
115102
|
+
],
|
|
115103
|
+
reasonCode: "config_missing"
|
|
115104
|
+
}),
|
|
115105
|
+
summary: "percher.toml missing — required on disk for a real upload.",
|
|
115106
|
+
configPath: tomlPathFor2(ctx.cwd),
|
|
115107
|
+
bundle: { fileCount: 0, bytes: 0 }
|
|
115108
|
+
};
|
|
115109
|
+
}
|
|
115079
115110
|
ctx.status("[2/4] Packaging files...");
|
|
115080
115111
|
const tarball = await createTarball({ cwd: ctx.cwd, config: config2 });
|
|
115081
115112
|
const packageMs = Date.now() - t0;
|
|
@@ -115581,11 +115612,14 @@ async function handleAnonymousPublish(ctx, config2, input, tarball) {
|
|
|
115581
115612
|
]
|
|
115582
115613
|
};
|
|
115583
115614
|
}
|
|
115584
|
-
async function loadOrGenerate(ctx) {
|
|
115615
|
+
async function loadOrGenerate(ctx, preParsed) {
|
|
115616
|
+
if (preParsed)
|
|
115617
|
+
return structuredClone(preParsed);
|
|
115585
115618
|
const tomlPath = join24(ctx.cwd, "percher.toml");
|
|
115586
115619
|
if (!existsSync15(tomlPath)) {
|
|
115587
115620
|
ctx.status("No percher.toml found — detecting framework...");
|
|
115588
|
-
await init(ctx);
|
|
115621
|
+
const generated = await init(ctx);
|
|
115622
|
+
return validate(generated.config);
|
|
115589
115623
|
}
|
|
115590
115624
|
return parseFile(tomlPath);
|
|
115591
115625
|
}
|
|
@@ -119014,7 +119048,7 @@ async function renderError(stderr, error48) {
|
|
|
119014
119048
|
// package.json
|
|
119015
119049
|
var package_default = {
|
|
119016
119050
|
name: "percher",
|
|
119017
|
-
version: "0.5.
|
|
119051
|
+
version: "0.5.10",
|
|
119018
119052
|
description: "CLI for the Percher hosting platform — publish apps with one command",
|
|
119019
119053
|
type: "module",
|
|
119020
119054
|
bin: {
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "percher",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "CLI for the Percher hosting platform
|
|
3
|
+
"version": "0.5.10",
|
|
4
|
+
"description": "CLI for the Percher hosting platform \u2014 publish apps with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"percher": "./dist/bin/percher.js"
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/dist /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/_site /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
# Multi-stage build: Hugo compiles the site, then a minimal Caddy
|
|
2
|
-
# image serves it. runtime = "docker" because Percher's static runtime
|
|
3
|
-
# serves pre-built files only — it does not run Hugo. Edit and own freely.
|
|
4
|
-
|
|
5
|
-
# --- Build stage: compile the Hugo site ------------------------------
|
|
6
|
-
FROM hugomods/hugo:exts-0.139.4 AS build
|
|
7
|
-
WORKDIR /site
|
|
8
|
-
COPY . .
|
|
9
|
-
RUN hugo --minify
|
|
10
|
-
|
|
11
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
12
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
13
|
-
# caps. Percher runs every container with --security-opt
|
|
14
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
15
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
16
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
17
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
18
|
-
FROM caddy:2-alpine AS caddy-source
|
|
19
|
-
FROM alpine:3.21
|
|
20
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
21
|
-
RUN apk add --no-cache libcap \
|
|
22
|
-
&& setcap -r /usr/bin/caddy \
|
|
23
|
-
&&
|
|
24
|
-
&&
|
|
25
|
-
|
|
26
|
-
COPY
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
# Multi-stage build: Hugo compiles the site, then a minimal Caddy
|
|
2
|
+
# image serves it. runtime = "docker" because Percher's static runtime
|
|
3
|
+
# serves pre-built files only — it does not run Hugo. Edit and own freely.
|
|
4
|
+
|
|
5
|
+
# --- Build stage: compile the Hugo site ------------------------------
|
|
6
|
+
FROM hugomods/hugo:exts-0.139.4 AS build
|
|
7
|
+
WORKDIR /site
|
|
8
|
+
COPY . .
|
|
9
|
+
RUN hugo --minify
|
|
10
|
+
|
|
11
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
12
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
13
|
+
# caps. Percher runs every container with --security-opt
|
|
14
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
15
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
16
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
17
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
18
|
+
FROM caddy:2-alpine AS caddy-source
|
|
19
|
+
FROM alpine:3.21
|
|
20
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
21
|
+
RUN apk add --no-cache libcap \
|
|
22
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
23
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
24
|
+
&& apk del libcap \
|
|
25
|
+
&& rm -rf /var/cache/apk/*
|
|
26
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
27
|
+
COPY --from=build /site/public /srv
|
|
28
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
29
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
30
|
+
# read-only root filesystem.
|
|
31
|
+
ENV HOME=/tmp
|
|
32
|
+
EXPOSE 8080
|
|
33
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -5,11 +5,15 @@ RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
|
|
|
5
5
|
|
|
6
6
|
FROM php:8.3-cli-alpine
|
|
7
7
|
WORKDIR /app
|
|
8
|
-
|
|
8
|
+
# tini as PID 1 forwards SIGTERM to php; without it `php -S` as PID 1 ignores
|
|
9
|
+
# SIGTERM (Linux PID-1 special case) and is SIGKILLed on every stop/redeploy.
|
|
10
|
+
RUN apk add --no-cache tini \
|
|
11
|
+
&& addgroup -S app && adduser -S app -G app
|
|
9
12
|
COPY --from=vendor --chown=app:app /app/vendor ./vendor
|
|
10
13
|
COPY --chown=app:app . .
|
|
11
14
|
RUN composer dump-autoload --no-dev --optimize 2>/dev/null || true \
|
|
12
15
|
&& chown -R app:app /app
|
|
13
16
|
USER app
|
|
14
17
|
EXPOSE 8000
|
|
18
|
+
ENTRYPOINT ["/sbin/tini", "--"]
|
|
15
19
|
CMD ["php", "-S", "0.0.0.0:8000", "-t", "public", "public/index.php"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Plain ESM (not next.config.ts) on purpose: `next start` loads the config at
|
|
2
|
+
// RUNTIME, and a .ts config would require the `typescript` package in the slim
|
|
3
|
+
// runtime image — but typescript is a build-only devDependency (pruned from the
|
|
4
|
+
// runtime stage). A .mjs config loads with no typescript at run time.
|
|
5
|
+
/** @type {import('next').NextConfig} */
|
|
6
|
+
const nextConfig = {};
|
|
7
|
+
|
|
8
|
+
export default nextConfig;
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/dist /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Pin the start command for the default nixpacks build path. Without this,
|
|
2
|
+
# nixpacks' Python provider can default to `python main.py`, which imports the
|
|
3
|
+
# FastAPI app and exits without serving — the container exits immediately and
|
|
4
|
+
# the health check fails. `python -m uvicorn` doesn't depend on `uvicorn` being
|
|
5
|
+
# on PATH. Mirrors `[web].command` in percher.toml (used by the Percher-owned
|
|
6
|
+
# python build bypass).
|
|
7
|
+
[start]
|
|
8
|
+
cmd = "python -m uvicorn main:app --host 0.0.0.0 --port 8000"
|
|
@@ -6,9 +6,13 @@ COPY src ./src
|
|
|
6
6
|
RUN cargo build --release --bin app
|
|
7
7
|
|
|
8
8
|
FROM alpine:3.20
|
|
9
|
-
|
|
9
|
+
# tini as PID 1 forwards SIGTERM to the app; without it the bare binary as PID 1
|
|
10
|
+
# ignores SIGTERM (Linux PID-1 special case) and the container is SIGKILLed on
|
|
11
|
+
# every stop/redeploy instead of shutting down cleanly.
|
|
12
|
+
RUN apk add --no-cache ca-certificates tini \
|
|
10
13
|
&& addgroup -S app && adduser -S app -G app
|
|
11
14
|
COPY --from=build --chown=app:app /src/target/release/app /app
|
|
12
15
|
USER app
|
|
13
16
|
EXPOSE 8080
|
|
17
|
+
ENTRYPOINT ["/sbin/tini", "--"]
|
|
14
18
|
CMD ["/app"]
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/dist /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/dist /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
-
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
-
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
-
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
-
# safe to edit and own.
|
|
6
|
-
|
|
7
|
-
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
-
FROM oven/bun:1-alpine AS build
|
|
9
|
-
WORKDIR /app
|
|
10
|
-
COPY . .
|
|
11
|
-
RUN bun install && bun run build
|
|
12
|
-
|
|
13
|
-
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
-
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
-
# caps. Percher runs every container with --security-opt
|
|
16
|
-
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
-
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
-
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
-
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
-
FROM caddy:2-alpine AS caddy-source
|
|
21
|
-
FROM alpine:3.21
|
|
22
|
-
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
-
RUN apk add --no-cache libcap \
|
|
24
|
-
&& setcap -r /usr/bin/caddy \
|
|
25
|
-
&&
|
|
26
|
-
&&
|
|
27
|
-
|
|
28
|
-
COPY
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# Multi-stage build: a Bun stage compiles the static bundle, then a
|
|
2
|
+
# minimal Caddy image serves it. This project uses runtime = "docker"
|
|
3
|
+
# (not "static") because Percher's static runtime serves pre-built
|
|
4
|
+
# files only — it does not run a build command. Generated by Percher;
|
|
5
|
+
# safe to edit and own.
|
|
6
|
+
|
|
7
|
+
# --- Build stage: install deps and build the static bundle -----------
|
|
8
|
+
FROM oven/bun:1-alpine AS build
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN bun install && bun run build
|
|
12
|
+
|
|
13
|
+
# --- Serve stage: minimal Caddy image --------------------------------
|
|
14
|
+
# caddy:2-alpine ships /usr/bin/caddy with cap_net_bind_service file
|
|
15
|
+
# caps. Percher runs every container with --security-opt
|
|
16
|
+
# no-new-privileges, which makes execve refuse a cap-bearing binary
|
|
17
|
+
# (an "operation not permitted" crash-loop). Copy Caddy onto a plain
|
|
18
|
+
# alpine base and strip the caps with setcap -r — Caddy never needs
|
|
19
|
+
# the cap here, it listens on :8080 (>= 1024).
|
|
20
|
+
FROM caddy:2-alpine AS caddy-source
|
|
21
|
+
FROM alpine:3.21
|
|
22
|
+
COPY --from=caddy-source /usr/bin/caddy /usr/bin/caddy
|
|
23
|
+
RUN apk add --no-cache libcap \
|
|
24
|
+
&& (setcap -r /usr/bin/caddy 2>/dev/null || true) \
|
|
25
|
+
&& test -z "$(getcap /usr/bin/caddy)" \
|
|
26
|
+
&& apk del libcap \
|
|
27
|
+
&& rm -rf /var/cache/apk/*
|
|
28
|
+
COPY Caddyfile /etc/caddy/Caddyfile
|
|
29
|
+
COPY --from=build /app/dist /srv
|
|
30
|
+
# HOME points at the writable tmpfs so non-root Caddy can persist its
|
|
31
|
+
# XDG state (storage-clean lock, config autosave) under Percher's
|
|
32
|
+
# read-only root filesystem.
|
|
33
|
+
ENV HOME=/tmp
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "percher",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "CLI for the Percher hosting platform
|
|
3
|
+
"version": "0.5.10",
|
|
4
|
+
"description": "CLI for the Percher hosting platform \u2014 publish apps with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"percher": "./dist/bin/percher.js"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const server = Bun.serve({
|
|
2
|
-
port: Number(process.env.PORT ?? 3000),
|
|
3
|
-
fetch(req) {
|
|
4
|
-
const url = new URL(req.url);
|
|
5
|
-
if (url.pathname === "/health") {
|
|
6
|
-
return Response.json({ status: "ok" });
|
|
7
|
-
}
|
|
8
|
-
return new Response("Hello from __APP_NAME__!");
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
console.log(`Listening on http://localhost:${server.port}`);
|