percher 0.3.0 → 0.4.1

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.
Files changed (178) hide show
  1. package/dist/bin/percher.js +111952 -18543
  2. package/dist/package.json +15 -6
  3. package/dist/src/cli-utils.d.ts +9 -0
  4. package/dist/src/cli-utils.d.ts.map +1 -1
  5. package/dist/src/cli-utils.js +18 -0
  6. package/dist/src/cli-utils.js.map +1 -1
  7. package/dist/src/commands/account.d.ts.map +1 -1
  8. package/dist/src/commands/account.js +63 -7
  9. package/dist/src/commands/account.js.map +1 -1
  10. package/dist/src/commands/admin.d.ts +17 -0
  11. package/dist/src/commands/admin.d.ts.map +1 -0
  12. package/dist/src/commands/admin.js +92 -0
  13. package/dist/src/commands/admin.js.map +1 -0
  14. package/dist/src/commands/apps.d.ts.map +1 -1
  15. package/dist/src/commands/apps.js +75 -7
  16. package/dist/src/commands/apps.js.map +1 -1
  17. package/dist/src/commands/deploys.d.ts.map +1 -1
  18. package/dist/src/commands/deploys.js +12 -2
  19. package/dist/src/commands/deploys.js.map +1 -1
  20. package/dist/src/commands/migrate.d.ts +4 -0
  21. package/dist/src/commands/migrate.d.ts.map +1 -0
  22. package/dist/src/commands/migrate.js +142 -0
  23. package/dist/src/commands/migrate.js.map +1 -0
  24. package/dist/src/commands/sharing.d.ts +23 -0
  25. package/dist/src/commands/sharing.d.ts.map +1 -0
  26. package/dist/src/commands/sharing.js +162 -0
  27. package/dist/src/commands/sharing.js.map +1 -0
  28. package/dist/src/commands/status.d.ts +4 -0
  29. package/dist/src/commands/status.d.ts.map +1 -0
  30. package/dist/src/commands/status.js +53 -0
  31. package/dist/src/commands/status.js.map +1 -0
  32. package/dist/src/commands/transfers.d.ts +20 -0
  33. package/dist/src/commands/transfers.d.ts.map +1 -0
  34. package/dist/src/commands/transfers.js +117 -0
  35. package/dist/src/commands/transfers.js.map +1 -0
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +44 -4
  38. package/dist/src/index.js.map +1 -1
  39. package/dist/templates/astro/Caddyfile +6 -0
  40. package/dist/templates/astro/Dockerfile +34 -0
  41. package/dist/templates/astro/astro.config.mjs +3 -0
  42. package/dist/templates/astro/package.json +14 -0
  43. package/dist/templates/astro/percher.toml +11 -0
  44. package/dist/templates/astro/src/pages/index.astro +15 -0
  45. package/dist/templates/bun/index.ts +23 -0
  46. package/dist/templates/bun/package.json +8 -0
  47. package/dist/templates/bun/percher.toml +10 -0
  48. package/dist/templates/django/Procfile +1 -0
  49. package/dist/templates/django/app/__init__.py +0 -0
  50. package/dist/templates/django/app/settings.py +44 -0
  51. package/dist/templates/django/app/urls.py +21 -0
  52. package/dist/templates/django/app/wsgi.py +5 -0
  53. package/dist/templates/django/manage.py +19 -0
  54. package/dist/templates/django/percher.toml +11 -0
  55. package/dist/templates/django/requirements.txt +2 -0
  56. package/dist/templates/dotnet/Dockerfile +18 -0
  57. package/dist/templates/dotnet/Program.cs +23 -0
  58. package/dist/templates/dotnet/app.csproj +9 -0
  59. package/dist/templates/dotnet/percher.toml +11 -0
  60. package/dist/templates/eleventy/.eleventy.js +12 -0
  61. package/dist/templates/eleventy/Caddyfile +6 -0
  62. package/dist/templates/eleventy/Dockerfile +34 -0
  63. package/dist/templates/eleventy/package.json +12 -0
  64. package/dist/templates/eleventy/percher.toml +11 -0
  65. package/dist/templates/eleventy/src/_includes/base.njk +12 -0
  66. package/dist/templates/eleventy/src/assets/style.css +12 -0
  67. package/dist/templates/eleventy/src/index.md +10 -0
  68. package/dist/templates/elysia/index.ts +8 -0
  69. package/dist/templates/elysia/package.json +12 -0
  70. package/dist/templates/elysia/percher.toml +11 -0
  71. package/dist/templates/express/index.ts +16 -0
  72. package/dist/templates/express/package.json +11 -0
  73. package/dist/templates/express/percher.toml +11 -0
  74. package/dist/templates/fastify/index.ts +13 -0
  75. package/dist/templates/fastify/package.json +11 -0
  76. package/dist/templates/fastify/percher.toml +11 -0
  77. package/dist/templates/flask/main.py +23 -0
  78. package/dist/templates/flask/percher.toml +11 -0
  79. package/dist/templates/flask/requirements.txt +1 -0
  80. package/dist/templates/go/Dockerfile +11 -0
  81. package/dist/templates/go/go.mod +3 -0
  82. package/dist/templates/go/main.go +53 -0
  83. package/dist/templates/go/percher.toml +11 -0
  84. package/dist/templates/hono/index.ts +12 -0
  85. package/dist/templates/hono/package.json +11 -0
  86. package/dist/templates/hono/percher.toml +11 -0
  87. package/dist/templates/htmx/index.ts +32 -0
  88. package/dist/templates/htmx/package.json +15 -0
  89. package/dist/templates/htmx/percher.toml +11 -0
  90. package/dist/templates/hugo/Caddyfile +6 -0
  91. package/dist/templates/hugo/Dockerfile +32 -0
  92. package/dist/templates/hugo/content/_index.md +7 -0
  93. package/dist/templates/hugo/hugo.toml +11 -0
  94. package/dist/templates/hugo/layouts/index.html +22 -0
  95. package/dist/templates/hugo/percher.toml +11 -0
  96. package/dist/templates/java/Dockerfile +15 -0
  97. package/dist/templates/java/Main.java +77 -0
  98. package/dist/templates/java/percher.toml +11 -0
  99. package/dist/templates/laravel/Dockerfile +15 -0
  100. package/dist/templates/laravel/composer.json +15 -0
  101. package/dist/templates/laravel/percher.toml +11 -0
  102. package/dist/templates/laravel/public/index.php +37 -0
  103. package/dist/templates/nextjs/app/api/health/route.ts +3 -0
  104. package/dist/templates/nextjs/app/layout.tsx +11 -0
  105. package/dist/templates/nextjs/app/page.tsx +10 -0
  106. package/dist/templates/nextjs/next.config.ts +5 -0
  107. package/dist/templates/nextjs/package.json +15 -0
  108. package/dist/templates/nextjs/percher.toml +15 -0
  109. package/dist/templates/node/index.js +26 -0
  110. package/dist/templates/node/index.ts +12 -0
  111. package/dist/templates/node/package.json +8 -0
  112. package/dist/templates/node/percher.toml +10 -0
  113. package/dist/templates/nuxt/app.vue +6 -0
  114. package/dist/templates/nuxt/nuxt.config.ts +4 -0
  115. package/dist/templates/nuxt/package.json +15 -0
  116. package/dist/templates/nuxt/percher.toml +15 -0
  117. package/dist/templates/nuxt/server/api/health.ts +1 -0
  118. package/dist/templates/phoenix/Dockerfile +15 -0
  119. package/dist/templates/phoenix/lib/application.ex +15 -0
  120. package/dist/templates/phoenix/lib/router.ex +39 -0
  121. package/dist/templates/phoenix/mix.exs +30 -0
  122. package/dist/templates/phoenix/percher.toml +11 -0
  123. package/dist/templates/preact/Caddyfile +7 -0
  124. package/dist/templates/preact/Dockerfile +34 -0
  125. package/dist/templates/preact/index.html +12 -0
  126. package/dist/templates/preact/package.json +19 -0
  127. package/dist/templates/preact/percher.toml +11 -0
  128. package/dist/templates/preact/src/main.tsx +13 -0
  129. package/dist/templates/preact/vite.config.ts +6 -0
  130. package/dist/templates/python/main.py +13 -0
  131. package/dist/templates/python/percher.toml +11 -0
  132. package/dist/templates/python/requirements.txt +2 -0
  133. package/dist/templates/rails/Dockerfile +11 -0
  134. package/dist/templates/rails/Gemfile +12 -0
  135. package/dist/templates/rails/config.ru +32 -0
  136. package/dist/templates/rails/percher.toml +11 -0
  137. package/dist/templates/remix/app/root.tsx +19 -0
  138. package/dist/templates/remix/app/routes/_index.tsx +10 -0
  139. package/dist/templates/remix/app/routes/health.tsx +3 -0
  140. package/dist/templates/remix/package.json +30 -0
  141. package/dist/templates/remix/percher.toml +15 -0
  142. package/dist/templates/remix/vite.config.ts +7 -0
  143. package/dist/templates/rust/Cargo.toml +19 -0
  144. package/dist/templates/rust/Dockerfile +14 -0
  145. package/dist/templates/rust/percher.toml +11 -0
  146. package/dist/templates/rust/src/main.rs +45 -0
  147. package/dist/templates/solid/Caddyfile +7 -0
  148. package/dist/templates/solid/Dockerfile +34 -0
  149. package/dist/templates/solid/index.html +12 -0
  150. package/dist/templates/solid/package.json +19 -0
  151. package/dist/templates/solid/percher.toml +11 -0
  152. package/dist/templates/solid/src/main.tsx +13 -0
  153. package/dist/templates/solid/vite.config.ts +6 -0
  154. package/dist/templates/static/index.html +15 -0
  155. package/dist/templates/static/percher.toml +6 -0
  156. package/dist/templates/static/style.css +32 -0
  157. package/dist/templates/sveltekit/package.json +19 -0
  158. package/dist/templates/sveltekit/percher.toml +15 -0
  159. package/dist/templates/sveltekit/src/app.html +12 -0
  160. package/dist/templates/sveltekit/src/routes/+page.svelte +2 -0
  161. package/dist/templates/sveltekit/src/routes/health/+server.ts +5 -0
  162. package/dist/templates/sveltekit/svelte.config.js +8 -0
  163. package/dist/templates/sveltekit/vite.config.js +6 -0
  164. package/dist/templates/vite-react/Caddyfile +7 -0
  165. package/dist/templates/vite-react/Dockerfile +34 -0
  166. package/dist/templates/vite-react/index.html +12 -0
  167. package/dist/templates/vite-react/package.json +22 -0
  168. package/dist/templates/vite-react/percher.toml +11 -0
  169. package/dist/templates/vite-react/src/main.tsx +20 -0
  170. package/dist/templates/vite-react/vite.config.ts +6 -0
  171. package/dist/templates/vite-vue/Caddyfile +7 -0
  172. package/dist/templates/vite-vue/Dockerfile +34 -0
  173. package/dist/templates/vite-vue/index.html +12 -0
  174. package/dist/templates/vite-vue/package.json +19 -0
  175. package/dist/templates/vite-vue/percher.toml +11 -0
  176. package/dist/templates/vite-vue/src/main.ts +12 -0
  177. package/dist/templates/vite-vue/vite.config.ts +6 -0
  178. package/package.json +15 -6
@@ -0,0 +1,19 @@
1
+ [package]
2
+ name = "app"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "__APP_NAME__ on Percher"
6
+
7
+ [[bin]]
8
+ name = "app"
9
+ path = "src/main.rs"
10
+
11
+ [dependencies]
12
+ axum = "0.7"
13
+ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
14
+
15
+ [profile.release]
16
+ opt-level = "z"
17
+ lto = true
18
+ codegen-units = 1
19
+ strip = true
@@ -0,0 +1,14 @@
1
+ FROM rust:1.82-alpine AS build
2
+ RUN apk add --no-cache musl-dev
3
+ WORKDIR /src
4
+ COPY Cargo.toml Cargo.lock* ./
5
+ COPY src ./src
6
+ RUN cargo build --release --bin app
7
+
8
+ FROM alpine:3.20
9
+ RUN apk add --no-cache ca-certificates \
10
+ && addgroup -S app && adduser -S app -G app
11
+ COPY --from=build --chown=app:app /src/target/release/app /app
12
+ USER app
13
+ EXPOSE 8080
14
+ CMD ["/app"]
@@ -0,0 +1,11 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "docker"
4
+ framework = "rust"
5
+
6
+ [web]
7
+ port = 8080
8
+ health = "/health"
9
+
10
+ [data]
11
+ mode = "none"
@@ -0,0 +1,45 @@
1
+ use axum::{http::header, response::Html, response::IntoResponse, routing::get, Router};
2
+ use std::env;
3
+
4
+ #[tokio::main]
5
+ async fn main() {
6
+ let app = Router::new()
7
+ .route("/", get(root))
8
+ .route("/health", get(health));
9
+
10
+ let port: u16 = env::var("PORT")
11
+ .ok()
12
+ .and_then(|s| s.parse().ok())
13
+ .unwrap_or(8080);
14
+
15
+ let listener = tokio::net::TcpListener::bind(("0.0.0.0", port))
16
+ .await
17
+ .expect("bind");
18
+ println!("listening on :{port}");
19
+ axum::serve(listener, app).await.expect("serve");
20
+ }
21
+
22
+ async fn root() -> Html<&'static str> {
23
+ Html(
24
+ r#"<!doctype html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="utf-8" />
28
+ <title>__APP_NAME__</title>
29
+ <style>
30
+ body { font-family: system-ui, sans-serif; max-width: 640px; margin: 48px auto; padding: 0 24px; line-height: 1.6; }
31
+ h1 { letter-spacing: -0.5px; }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <h1>__APP_NAME__</h1>
36
+ <p>A Rust app deployed on Percher.</p>
37
+ <p>Add routes in <code>src/main.rs</code> and push to redeploy.</p>
38
+ </body>
39
+ </html>"#,
40
+ )
41
+ }
42
+
43
+ async fn health() -> impl IntoResponse {
44
+ ([(header::CONTENT_TYPE, "application/json")], r#"{"ok":true}"#)
45
+ }
@@ -0,0 +1,7 @@
1
+ :8080 {
2
+ bind 0.0.0.0
3
+ root * /srv
4
+ encode gzip zstd
5
+ try_files {path} /index.html
6
+ file_server
7
+ }
@@ -0,0 +1,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 \
25
+ && apk del libcap \
26
+ && rm -rf /var/cache/apk/*
27
+ COPY Caddyfile /etc/caddy/Caddyfile
28
+ COPY --from=build /app/dist /srv
29
+ # HOME points at the writable tmpfs so non-root Caddy can persist its
30
+ # XDG state (storage-clean lock, config autosave) under Percher's
31
+ # read-only root filesystem.
32
+ ENV HOME=/tmp
33
+ EXPOSE 8080
34
+ CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__APP_NAME__</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "solid-js": "^1.8.0"
13
+ },
14
+ "devDependencies": {
15
+ "vite": "^5.3.0",
16
+ "vite-plugin-solid": "^2.10.0",
17
+ "typescript": "^5.5.0"
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "docker"
4
+ framework = "solid"
5
+
6
+ [web]
7
+ port = 8080
8
+ health = "/"
9
+
10
+ [data]
11
+ mode = "none"
@@ -0,0 +1,13 @@
1
+ import { render } from "solid-js/web";
2
+
3
+ function App() {
4
+ return (
5
+ <main style={{ "font-family": "system-ui", padding: "40px" }}>
6
+ <h1>Hello from __APP_NAME__</h1>
7
+ <p>Solid + Vite, served as static from Percher.</p>
8
+ </main>
9
+ );
10
+ }
11
+
12
+ const root = document.getElementById("app");
13
+ if (root) render(() => <App />, root);
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from "vite";
2
+ import solid from "vite-plugin-solid";
3
+
4
+ export default defineConfig({
5
+ plugins: [solid()],
6
+ });
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>__APP_NAME__</title>
7
+ <link rel="stylesheet" href="style.css" />
8
+ </head>
9
+ <body>
10
+ <main>
11
+ <h1>__APP_NAME__</h1>
12
+ <p>Edit <code>index.html</code> and run <code>bunx percher push</code> to deploy.</p>
13
+ </main>
14
+ </body>
15
+ </html>
@@ -0,0 +1,6 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "static"
4
+
5
+ [data]
6
+ mode = "none"
@@ -0,0 +1,32 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: system-ui, -apple-system, sans-serif;
9
+ min-height: 100vh;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ background: #fafafa;
14
+ color: #111;
15
+ }
16
+
17
+ main {
18
+ text-align: center;
19
+ padding: 2rem;
20
+ }
21
+
22
+ h1 {
23
+ font-size: 2rem;
24
+ margin-bottom: 0.5rem;
25
+ }
26
+
27
+ code {
28
+ background: #e8e8e8;
29
+ padding: 0.15em 0.4em;
30
+ border-radius: 4px;
31
+ font-size: 0.9em;
32
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "start": "node build/index.js"
11
+ },
12
+ "devDependencies": {
13
+ "@sveltejs/adapter-node": "^5.2.0",
14
+ "@sveltejs/kit": "^2.5.0",
15
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
16
+ "svelte": "^5.0.0",
17
+ "vite": "^5.4.0"
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "node"
4
+ framework = "sveltekit"
5
+
6
+ [build]
7
+ command = "bun run build"
8
+ output = "build"
9
+
10
+ [web]
11
+ port = 3000
12
+ health = "/health"
13
+
14
+ [data]
15
+ mode = "none"
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>__APP_NAME__</title>
7
+ %sveltekit.head%
8
+ </head>
9
+ <body>
10
+ <div>%sveltekit.body%</div>
11
+ </body>
12
+ </html>
@@ -0,0 +1,2 @@
1
+ <h1>Hello from __APP_NAME__!</h1>
2
+ <p>Built with SvelteKit on Percher.</p>
@@ -0,0 +1,5 @@
1
+ export function GET() {
2
+ return new Response(JSON.stringify({ status: "ok" }), {
3
+ headers: { "content-type": "application/json" },
4
+ });
5
+ }
@@ -0,0 +1,8 @@
1
+ import adapter from "@sveltejs/adapter-node";
2
+
3
+ /** @type {import('@sveltejs/kit').Config} */
4
+ export default {
5
+ kit: {
6
+ adapter: adapter(),
7
+ },
8
+ };
@@ -0,0 +1,6 @@
1
+ import { sveltekit } from "@sveltejs/kit/vite";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ plugins: [sveltekit()],
6
+ });
@@ -0,0 +1,7 @@
1
+ :8080 {
2
+ bind 0.0.0.0
3
+ root * /srv
4
+ encode gzip zstd
5
+ try_files {path} /index.html
6
+ file_server
7
+ }
@@ -0,0 +1,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 \
25
+ && apk del libcap \
26
+ && rm -rf /var/cache/apk/*
27
+ COPY Caddyfile /etc/caddy/Caddyfile
28
+ COPY --from=build /app/dist /srv
29
+ # HOME points at the writable tmpfs so non-root Caddy can persist its
30
+ # XDG state (storage-clean lock, config autosave) under Percher's
31
+ # read-only root filesystem.
32
+ ENV HOME=/tmp
33
+ EXPOSE 8080
34
+ CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__APP_NAME__</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^18.3.0",
13
+ "react-dom": "^18.3.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/react": "^18.3.0",
17
+ "@types/react-dom": "^18.3.0",
18
+ "@vitejs/plugin-react": "^4.3.0",
19
+ "typescript": "^5.5.0",
20
+ "vite": "^5.3.0"
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "docker"
4
+ framework = "vite"
5
+
6
+ [web]
7
+ port = 8080
8
+ health = "/"
9
+
10
+ [data]
11
+ mode = "none"
@@ -0,0 +1,20 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+
4
+ function App() {
5
+ return (
6
+ <main style={{ fontFamily: "system-ui", padding: 40 }}>
7
+ <h1>Hello from __APP_NAME__</h1>
8
+ <p>React + Vite, served as static from Percher.</p>
9
+ </main>
10
+ );
11
+ }
12
+
13
+ const root = document.getElementById("app");
14
+ if (root) {
15
+ createRoot(root).render(
16
+ <StrictMode>
17
+ <App />
18
+ </StrictMode>,
19
+ );
20
+ }
@@ -0,0 +1,6 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ });
@@ -0,0 +1,7 @@
1
+ :8080 {
2
+ bind 0.0.0.0
3
+ root * /srv
4
+ encode gzip zstd
5
+ try_files {path} /index.html
6
+ file_server
7
+ }
@@ -0,0 +1,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 \
25
+ && apk del libcap \
26
+ && rm -rf /var/cache/apk/*
27
+ COPY Caddyfile /etc/caddy/Caddyfile
28
+ COPY --from=build /app/dist /srv
29
+ # HOME points at the writable tmpfs so non-root Caddy can persist its
30
+ # XDG state (storage-clean lock, config autosave) under Percher's
31
+ # read-only root filesystem.
32
+ ENV HOME=/tmp
33
+ EXPOSE 8080
34
+ CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__APP_NAME__</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "vue": "^3.4.0"
13
+ },
14
+ "devDependencies": {
15
+ "@vitejs/plugin-vue": "^5.0.0",
16
+ "typescript": "^5.5.0",
17
+ "vite": "^5.3.0"
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ [app]
2
+ name = "__APP_NAME__"
3
+ runtime = "docker"
4
+ framework = "vite"
5
+
6
+ [web]
7
+ port = 8080
8
+ health = "/"
9
+
10
+ [data]
11
+ mode = "none"
@@ -0,0 +1,12 @@
1
+ import { createApp, h } from "vue";
2
+
3
+ const App = {
4
+ render() {
5
+ return h("main", { style: "font-family: system-ui; padding: 40px;" }, [
6
+ h("h1", "Hello from __APP_NAME__"),
7
+ h("p", "Vue + Vite, served as static from Percher."),
8
+ ]);
9
+ },
10
+ };
11
+
12
+ createApp(App).mount("#app");
@@ -0,0 +1,6 @@
1
+ import vue from "@vitejs/plugin-vue";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "percher",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI for the Percher hosting platform — publish apps with one command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,15 +15,17 @@
15
15
  "default": "./dist/src/index.js"
16
16
  }
17
17
  },
18
- "files": ["dist"],
18
+ "files": [
19
+ "dist"
20
+ ],
19
21
  "scripts": {
20
- "build": "tsc -p tsconfig.json && bun build bin/percher.ts --outfile dist/bin/percher.js --target node --format esm && node -e \"require('fs').chmodSync('dist/bin/percher.js',0o755)\"",
22
+ "build": "tsc -p tsconfig.json && bun build bin/percher.ts --outfile dist/bin/percher.js --target node --format esm && node scripts/copy-templates.mjs && node -e \"require('fs').chmodSync('dist/bin/percher.js',0o755)\"",
21
23
  "test": "bun test",
22
24
  "typecheck": "tsc --noEmit"
23
25
  },
24
26
  "dependencies": {
25
27
  "@percher/client": "^0.2.7",
26
- "@percher/core": "^0.3.0",
28
+ "@percher/core": "^0.4.0",
27
29
  "@percher/toml": "^0.1.3",
28
30
  "cac": "^7.0.0",
29
31
  "picocolors": "^1.1.1"
@@ -33,7 +35,7 @@
33
35
  },
34
36
  "engines": {
35
37
  "bun": ">=1.0.0",
36
- "node": ">=20.19.0"
38
+ "node": ">=22.0.0"
37
39
  },
38
40
  "license": "MIT",
39
41
  "author": "Mattias Malmborg",
@@ -41,5 +43,12 @@
41
43
  "type": "git",
42
44
  "url": "https://github.com/mattiasmalmborg/percher"
43
45
  },
44
- "keywords": ["hosting", "deploy", "cli", "paas", "bun", "ai"]
46
+ "keywords": [
47
+ "hosting",
48
+ "deploy",
49
+ "cli",
50
+ "paas",
51
+ "bun",
52
+ "ai"
53
+ ]
45
54
  }