Skip to content

Instantly share code, notes, and snippets.

@adtac
Last active September 26, 2024 09:47
Show Gist options
  • Save adtac/595b5823ef73b329167b815757bbce9f to your computer and use it in GitHub Desktop.
Save adtac/595b5823ef73b329167b815757bbce9f to your computer and use it in GitHub Desktop.
#!/usr/bin/env docker run
#!/usr/bin/env -S bash -c "docker run -p 8080:8080 -it --rm \$(docker build --progress plain -f \$0 . 2>&1 | tee /dev/stderr | grep -oP 'sha256:[0-9a-f]*')"
# syntax = docker/dockerfile:1.4.0
FROM node:20
WORKDIR /root
RUN npm install sqlite3
RUN <<EOF cat >/root/schema.sql
CREATE TABLE IF NOT EXISTS clicks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
time INTEGER NOT NULL
);
EOF
RUN <<EOF cat >/root/server.js
const fs = require("fs");
const http = require("http");
const sqlite3 = require("sqlite3");
const db = new sqlite3.Database(":memory:");
db.run(fs.readFileSync("/root/schema.sql", "utf8"));
const html = fs.readFileSync("/root/index.html", "utf8");
const server = http.createServer((req, res) => {
db.run("INSERT INTO clicks(time) VALUES(unixepoch())");
const data = [];
db.each(
"SELECT time as t, COUNT(*) as n FROM clicks WHERE t > unixepoch()-4*60*60 GROUP BY t-t%60",
(_, { t, n }) => data.push([Math.floor(t/60), n]),
() => {
res.writeHead(200, { "content-type": "text/html" });
res.end(html.replace("__DATA__", JSON.stringify(data)));
},
);
});
server.listen(8080, "", () => console.log("serving :8080..."));
EOF
RUN <<EOF cat >/root/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#!/usr/bin/env docker run</title>
</head>
<body style="font-family: monospace; font-size; 12px; ">
<div style="position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; background-size: 5vh 5vh; background-image: linear-gradient(to right, #f0f0f0 1px, transparent 1px), linear-gradient(to bottom, #f0f0f0 1px, transparent 1px); "></div>
<span style="position: absolute; top: 1vh; left: 5vh;">Page loads over time (last 4 hours)</span>
<span id="max" style="position: absolute; top: 5vh; left: 1vh;"></span>
<span id="min" style="position: absolute; top: 95vh; left: 1vh;">0</span>
<canvas id="canvas" style="position: absolute; top: 5vh; left: 5vw; "></canvas>
<script>
(() => {
const el = document.getElementById("canvas"), ctx = el.getContext("2d");
el.width = 0.9 * window.innerWidth * window.devicePixelRatio;
el.height = 0.9 * window.innerHeight * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
const data = __DATA__;
const max = data.reduce((prev, [_, n]) => (n > prev ? n : prev), 0);
document.getElementById("max").innerText = max;
ctx.beginPath();
ctx.moveTo(0, el.height);
const draw = (t, n) => {
const [x, y] = [el.width * (t-data[0][0])/240, el.height * (1 - n/max)];
ctx.lineTo(x, y);
ctx.moveTo(x, y);
}
let last = -1;
for (const [t, n] of data) {
if (last != -1 && t > last + 1) {
draw(last + 0.1, 0);
draw(t - 0.1, 0);
}
draw(t, n);
last = t;
}
ctx.stroke();
})();
</script>
</body>
</html>
EOF
CMD node /root/server.js

What is this?

#! (pronounced shebang) is a Unix convention that's typically used for scripting languages like Python and Bash. This abuses it to let you package applications in a cross-distro and cross-platform way. This example Dockerfile is a fullstack server that includes a backend, a database and a UI, all in a single file.

It's kinda like Cosmopolitan Libc but for packaging applications.

Why?

Why not?

Is it safe?

Probably not.

Should I use it?

If you want.

Is it performant?

Maybe.

Is it maintanable?

No.

Does it work?

Yes.

How do I run this?

chmod +x ./Dockerfile
./Dockerfile

Then go to http://127.0.0.1:8080

@ikbenkous
Copy link

image

@santiagobasulto
Copy link

The report abuse image killed me 😂. We need reactions in gists

@GabiGrin
Copy link

The report abuse image killed me 😂. We need reactions in gists

+100

@seisdr
Copy link

seisdr commented Jan 14, 2024

Average docker enjoyer

@Welding-Torch
Copy link

chaotic evil

@ble
Copy link

ble commented Jan 14, 2024

"Daddy, what is that man doing to his Dockerfile?"
"Don't look sweetie."

@brentcogolson
Copy link

Glad to see the comments above echoing the sentiment I had reading this file.

@thaarrtt
Copy link

finally pocketbase alternative

@erickguan
Copy link

erickguan commented Jan 14, 2024

Many ways to build Docker image nowadays. This is a fun trick. Though I am really hoping a "wow this is better moment"...

@markstos
Copy link

I've used podman run on a shebang line to run a containerized version of node instead of the system node. For a specific app where you know the right paths to mount into the container-- it worked fine for a specific case.

Otherwise, container images already run everywhere that Docker and Podman run and already a single file, leaving abusing Dockerfiles as a fun exercise.

@codenoid
Copy link

nice, I made a program to automate this, embed all your project files into single Dockerfile file

https://github.com/codenoid/docker-script

@adtac
Copy link
Author

adtac commented Jan 14, 2024

@markstos's comment made me wonder if it was possible to accept arbitrary arguments at runtime like any other CLI executable:

@@ -1,4 +1,4 @@
-#!/usr/bin/env -S bash -c "docker run -p 8080:8080 -it --rm \$(docker build --progress plain -f \$0 . 2>&1 | tee /dev/stderr | grep -oP 'sha256:[0-9a-f]*')"
+#!/usr/bin/env -S bash -c "docker run -p 8080:8080 -it --rm \$(docker build \$(for arg in \$*; do echo --build-arg \$arg; done) --progress plain -f \$0 . 2>&1 | tee /dev/stderr | grep -oP 'sha256:[0-9a-f]*')"
 
 # syntax = docker/dockerfile:1.4.0
 
@@ -15,6 +15,9 @@ RUN <<EOF cat >/root/schema.sql
   );
 EOF
 
+# time range of the graph in minutes
+ARG time_range=240
+
 RUN <<EOF cat >/root/server.js
   const fs = require("fs");
   const http = require("http");
@@ -29,7 +32,7 @@ RUN <<EOF cat >/root/server.js
 
     const data = [];
     db.each(
-      "SELECT time as t, COUNT(*) as n FROM clicks WHERE t > unixepoch()-4*60*60 GROUP BY t-t%60",
+      "SELECT time as t, COUNT(*) as n FROM clicks WHERE t > unixepoch()-${time_range}*60 GROUP BY t-t%60",

So now you can optionally specify:

./Dockerfile time_range=60

to configure the behaviour of your app at runtime.

@karolba
Copy link

karolba commented Jan 14, 2024

This uses the fact that Linux doesn't split the shebang arguments by spaces. It won't work on macOS, or any of the BSDs.

@Fusion
Copy link

Fusion commented Jan 15, 2024

If you do not perform a first grep for writing image, two sha256: rows may be spewed by the script; the first one telling us about the base image.

@Dich0tomy
Copy link

People will hate on nix and then do this

@vluz
Copy link

vluz commented Jan 16, 2024

I'm shocked. Shocked and dismayed. Shocked, dismayed, and alarmed.

@Sanix-Darker
Copy link

EWWWWWWWW

love it

@PleahMaCaka
Copy link

I almost died after seeing this

@stevekrouse
Copy link

stevekrouse commented Jan 16, 2024

I ported it over to Val Town!

Code: https://www.val.town/v/stevekrouse/backend_in_a_file
Live demo: https://stevekrouse-backend_in_a_file.web.val.run

I think there may be a frontend / css bug - would love any help debugging it or a PR on my val.

import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { html } from "https://esm.town/v/stevekrouse/html";

sqlite.execute(`CREATE TABLE IF NOT EXISTS backend_in_a_file_clicks (
  id   INTEGER PRIMARY KEY AUTOINCREMENT,
  time INTEGER NOT NULL
)`);

export default async function(req: Request): Promise<Response> {
  await sqlite.execute("INSERT INTO backend_in_a_file_clicks(time) VALUES(unixepoch())");
  const results = await sqlite.execute(
    "SELECT time as t, COUNT(*) as n FROM backend_in_a_file_clicks WHERE t > unixepoch()-4*60*60 GROUP BY t-t%60",
  );
  const data = (results.rows as any as [number, number][]).map(([t, n]) => [Math.floor(t / 60), n]);
  return html(`
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>#!/usr/bin/env docker run</title>
  </head>
  <body style="font-family: monospace; font-size; 12px; ">
    <div style="position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; background-size: 5vh 5vh; background-image: linear-gradient(to right, #f0f0f0 1px, transparent 1px), linear-gradient(to bottom, #f0f0f0 1px, transparent 1px); "></div>
    <span style="position: absolute; top: 1vh; left: 5vh;">Page loads over time (last 4 hours)</span>
    <span id="max" style="position: absolute; top: 5vh; left: 1vh;"></span>
    <span id="min" style="position: absolute; top: 95vh; left: 1vh;">0</span>
    <canvas id="canvas" style="position: absolute; top: 5vh; left: 5vw; "></canvas>
    <script>
      (() => {
        const el = document.getElementById("canvas"), ctx = el.getContext("2d");
        el.width = 0.9 * window.innerWidth * window.devicePixelRatio;
        el.height = 0.9 * window.innerHeight * window.devicePixelRatio;
        ctx.scale(window.devicePixelRatio, window.devicePixelRatio);

        const data = ${JSON.stringify(data)};
        const max = data.reduce((prev, [_, n]) => (n > prev ? n : prev), 0);
        document.getElementById("max").innerText = max;

        ctx.beginPath();
        ctx.moveTo(0, el.height);

        const draw = (t, n) => {
          const [x, y] = [el.width * (t-data[0][0])/240, el.height * (1 - n/max)];
          ctx.lineTo(x, y);
          ctx.moveTo(x, y);
        }

        let last = -1;
        for (const [t, n] of data) {
          if (last != -1 && t > last + 1) {
            draw(last + 0.1, 0);
            draw(t - 0.1, 0);
          }
          draw(t, n);
          last = t;
        }
        ctx.stroke();
      })();
    </script>
  </body>
</html>`);
}

@nhumrich
Copy link

There was a bug in the she-bang line:

#11 writing image sha256:72b14c1cfde038ddccb0e7b6c600c0c23cd48acf5a37ae3cffbd224f629ccb6e done
#11 DONE 0.0s
docker: Error response from daemon: No such image: >sha256:ffebb4405810c92d267a764b21975fb2d96772e41877248a37bf3abaa0d3b590.
See 'docker run --help'.

grep is reading the wrong line. (needs to be the last line), so i fixed the shebang line by using tac to reverse the file, and then grep -m 1 so if stops on first match. Final she-bang line:

#!/usr/bin/env -S bash -c "docker run -p 8080:8080 -it --rm \$(docker build --progress plain -f \$0 . 2>&1 | tee /dev/stderr | tac | grep -m 1 -oP 'sha256:[0-9a-f]*')"

@MuhammadSawalhy
Copy link

I got this error:

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

unknown flag: --progress
See 'docker build --help'.
"docker run" requires at least 1 argument.
See 'docker run --help'.

Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Create and run a new container from an image

@rennokki
Copy link

rennokki commented Aug 9, 2024

I had a
image
while seeing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment