exoframejs/exoframe

v7.0 rewrite checklist

yamalight opened this issue ยท 14 comments

This is an issue to track overall progress on v7.0 rewrite.
Plan is to rewrite / update code to use latest libs (not just versions) and ES modules (ES modules are not ready yet, dropping them?).
Additionally, I'm planning to implement exoframe-client library that provides simple API for interacting with server (would allow to create new client, e.g. dashboard as some people wanted).

All work is done on v7-rewrite branch.

Packages:

  • exoframe-client
  • exoframe-server
  • exoframe-cli
  • exoframe-recipe-ghost
  • exoframe-template-java
  • exoframe-website (incl. docs)

Planned major changes:

  • Drop yarn support
    Reasoning: yarn v1 is deprecated and v2 is too different from v1. npm v7 now does most of things yarn v1 did.
  • Drop plugins support
    Reasoning: swarm is deprecated. There are no other plugins and doesn't seem like they'd be useful ๐Ÿค”
  • Drop docker swarm support.
    Reasoning: support for it adds complexity, almost nobody uses swarm (k8s is a better solution for that problem), exoframe doesn't work well in those scenarios as a whole.
  • Drop docker-compose support.
    Reasoning: compose-based deployments don't work well with exoframe (users has to manually add traefik labels, etc), which causes a lot of issues. there's also an issue with tearing down and rebuilding / restarting all services when you only want to update one. IMO the best way would be to deprecate it and describe how to deploy multi-service projects using plain exoframe configs.
  • Drop exoframe-faas support.
    Reasoning: it's half-baked, rarely used but adds quite a bit of overhead to the server. Would be better to provide a guide to extra-slim docker containers (not quite FaaS, but should close enough for this project scope)
  • Drop old / unused recipes and templates (include one of each as an example)
    Reasoning: it's hard to maintain / fix all of them with time (I kinda ended up abandoning most of them), so it's better to just have a few simple examples.

Things to address in code / deps:

  • Fix update tests for exoframe-server (failing in CI)
  • Fix deprecation warnings in exoframe-server
  • Replace @zeit/ncc with @vercel/ncc. New ncc version doesn't work well, removed, will publish exoframe-server as-is.
  • Update to eslint 8.x and eslint-plugin-import 3.x (?) to enable top-level await support
    • Re-enable eslint in CI for exoframe-server once done
  • Refactor CLI config loading to be executed on every program creation, not just on load (helpful for tests)
  • Try using new @vercel/ncc -> doesn't work since some deps are still CJS and exoframe-server is ESM. Mixing those two doesn't work well when bundling code ๐Ÿ˜‘

Planned issues:

  • use new standard crypto.randomUUID API for UUID generation
  • Use XDG_CONFIG_HOME for config location (#309)
    • Server
    • Client
  • More private key types (#191)
    • Server
    • Client
  • Removal of multiple deployments (#291) (after some more thinking - bad idea, disregarding)
  • Docker images hosted on github (#302)
  • Allow other types of mounts (#319)
  • Allow passing build args from config to docker during build (#323)
  • Deterministic deployment IDs (#275)
  • New website (#259)
  • Setup E2E tests in CI for that run basic tests for all packages

ToDos:

  • Go through Dockerfile best practices repo (and this) and apply all that make sense to default dockerfiles used by exoframe
  • Combine all repos to monorepo
  • Archive all old repos

Things to document:

  • Migration guide
  • Docker-compose (why it's no longer supported, how to replace it with individual deployments)
  • Docs for typical deployment cases (#235)
  • Docs for usage in CI / github action (#260)
  • Docs for different types of mounts (#319)
  • Docs for new baseDomain based deployment URLs (#275)
  • Docs for more private key types (#191)
  • Docs for install script (#307)
  • Docs for new traefik config (#139, #303)
  • Docs for enabling traefik dashboard (#147)
  • Docs for build args config (#323)

Breaking changes (to keep track of them somehow):

  • Node 18+ as minimum requirement (due to ESM)
  • Removal of yarn
  • Removal of plugins
  • Removal of swarm
  • Removal of compose
  • Removal of exoframe-faas
  • Config folder change (using XDG_CONFIG_HOME now)
  • [todo]

Any feedback welcome.

Apparently, jest currently doesn't support ES module mocking (see jestjs/jest#10025), so server rewrite is blocked on that feature.

Upd 1: Possible solution - use esm package as transformer? Doesn't work well with jest :(

Upd 2: Tried using babel-jest to compile modules - that doesn't work because import.meta is not compiled (for some reason), so you can't have 100% compatible code. Seems like waiting for jest ES compat is the best way?

Upd 3: jest just added basic unstable ESM support. gotta give it a shot.

Upd 4: And it works! ๐Ÿฅณ

I wanted to use Ink for new cli, but it seems like it also has issues with ES modules support (import-jsx package specifically). So CLI is also blocked ๐Ÿ™ƒ

Possible solution - use esm package? - doesn't work because import-jsx uses require to load modules, while esm expects import for all ES modules.

Update: Managed to make it work using htm package! ๐ŸŽ‰๐Ÿฅณ

Update 2: Not sure I like how the resulting code with html tagged template looks. Lack of syntax highlighting makes it quite hard to read ๐Ÿค”

Update 3: Tried to use swc and esbuild to transpile jsx. both don't seem to play well with ESM (at least from the first basic attempt)

Update 4: Found a bug in swc, send a PR to fix it. This might fix most of issues(?)

Update 5: Using @babel/preset-env with custom config that emits ESM seems to work for now! Migrating to swc/esbuild would be nice for build speed, but for now it's fine.

Update 6: came to a conclusion that React/Ink is waaaay to complex for what exoframe CLI does. Dropped it. Tried using enquirer instead - it has some nice features, but some of the things are not quite what we need here. Seems like sticking with inquirer and building a custom prompt for config might be the best / easiest way to go ๐Ÿค”

Great collection of CLI websites on HN, ones I like most:

Frameworks for docs that looks nice:

Alright, few months later - doesn't seem like native ESM support in Node.js ecosystem is going to happen anytime soon. Will probably scrap the plan to use pure ESM and use babel (with e.g. rollup) to compile to cjs.
Well, was worth a try I guess ๐Ÿ™ƒ

Initial server rewrite to ESM is finished ๐ŸŽ‰

Things to keep in mind:

  • Current implementation uses --experimental-json-modules to import JSON (can be easily refactored)
  • Tests require --experimental-vm-modules flag (shouldn't pose a problem)
  • I haven't tried compiling ESM code with ncc yet

I'm starting to think it might be easier to rewrite whole thing to typescript at this stage. So many tools are still not quite there yet with ESM support. The pain ๐Ÿ™ƒ

Yes please ๐Ÿ˜Š

I've tried doing that yesterday and as just a solution to ESM issues - it's no longer viable. There already are ESM-only modules that won't work in CJS envs, so you have to compile ts to ESM. Which just means you have ts on top of all existing issues ๐Ÿ˜…

I can try to make my hands dirty ๐Ÿ˜‰

@FDiskas I'd prefer to first finish ESM version and then do anything else. There's too many issues as it is to add yet another thing on top ๐Ÿ˜…

Hi @yamalight! I'm really sad to see "Drop docker-compose support." on the list. This is basically how 90% of my exoframe deployments are done.

I really love this feature and it would be really sad to see it go, as I'll have to either keep using the old version or create a fork. At this point I'd be willing to even contribute a small amount of cash to support for docker-compose, or help by contributing to maintaining that feature myself.

I've previously contributed a PR related to this feature, and as you can see I've been using exoframe for over 5 years now.

I really don't mind the fact that I need to specify labels manually, in fact, I prefer it, because it gives me more flexibility as to how the deployments should be routed via traefik.

I really hope you reconsider. Let me know if you'd like to know more about how my use cases. Using exoframe over the years has been fantastic!

@niieani I still think it's one of the worst ways to deploy things via exoframe - you make your server tear-down, re-build and re-start all containers defined in the compose file on every deployment. it's incredibly wasteful in all sorts of ways. vast majority of the time you want to update just one (app) container, and keep all the others unchanged (database, message queue, etc).

this is the main reason I think "exoframe way" (tm) is just a better approach.
and it's not like doing it "exoframe way" is a lot of work - it takes a few minutes more than deploying compose, but only once.

here's an example. Imagine you have following compose file:

version: '3.6'
services:
  postgres:
    image: postgres:15
    ports:
      - 5432:5432
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
  redis:
    image: redis:7
    ports:
      - '6379:6379'
  app:
    build: .
    ports:
      - 8080:8080
    depends_on:
      - postgres
      - redis
    environment:
      DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      REDIS_HOST: redis

volumes:
  db_data:

This work perfectly fine for local testing / dev, but deploying it via exoframe 6 will tear-down and re-start postgres and redis every time you deploy. Why waste that time?

Here's how you would deploy it using exoframe configs:
First, you define config for postgres:

{
  "name": "my-postgres",
  "domain": false,
  "image": "postgres:15",
  "volumes": ["db_data:/var/lib/postgresql/data"],
  "hostname": "mypostgres",
  "env": {
    "POSTGRES_PASSWORD": "postgrespassword"
  }
}

Next, you define exoframe config for redis:

{
  "name": "my-redis",
  "domain": false,
  "image": "redis:7",
  "hostname": "myredis"
}

And finally, you define config for your app:

{
  "name": "my-app",
  "domain": "my-app.com",
  "env": {
    "DATABASE_URL": "postgres://postgres:postgrespassword@postgres:5432/postgres",
    "REDIS_HOST": "askaredis"
  }
}

That's it, that's all the work it takes and you do it once.
After doing that - you can individually (re-)deploy any of the containers without touching the other ones.

I think it's a fairly simple procedure that does make deployments a lot faster / simpler.
It obviously has to be documented a lot better than it is right now - but that should come along with a new website.

I was also thinking about a possibility of writing a CLI script that would auto-convert compose files into sub-deployments like this.

Would love to know your thoughts on this - is that easy enough? Would you still want compose?

@yamalight hi, sorry about offtopic - I noticed that repository with a website is archived. Do you need a help on building the new one? Thinking about astrojs

@FDiskas website will also be part of current monorepo (old repo was folded into it as-is).
appreciate the offer, but haven't gotten around to that part yet! ๐Ÿ˜