garnix-io/garn

Allow to easily deploy to `gh_pages`

Closed this issue · 7 comments

If I have a javascript project with a Package that builds the frontend bundle, it should be really easy to add an Executable that will deploy that built frontend bundle to the gh-pages branch on github.

It should not modify your local git checkout state in any relevant way.

Possible api:

export const website = garn.javascript
  .mkNpmProject({
    description: "The garn.io website",
    nodeVersion: "18",
    src: ".",
  })
  .addPackage("build", "npm run build ; mv build/* $out/")
  .addGhPagesDeploy("build");

  // or:
  .add(ghPagesDeploy("build", "npm run build ; mv build/* $out/"))

  // or: (@soenkehahn's personal favorite)
  .add(ghPagesDeploy("build"))
jkarni commented

Maybe even .add(ghPagesDeploy()) and the name defaults to something like deploy-gh-pages, since most people probably don't care about customizing that, and it's not immediately obvious what the argument represents.

So the ghPagesDeploy function needs to somehow know, which Package to deploy to the gh-pages branch. That's what the "build" argument does.

jkarni commented

Ah! It'd be nice if the argument were somehow of type Package then. Though unclear how to do that nicely (cf. #396)

Yeah we spoke about something like this maybe:

.add(ghPagesDeploy(self => self.build))

But ideally ghPagesDeploy could be made to work with all of these alternatives:

.add(ghPagesDeploy("build"))
.add(ghPagesDeploy(self => self.build))
.add(self => ghPagesDeploy(self.build)(self))

Yeah, exactly. So one option that @alexdavid and I were thinking of is that

  1. Project.add doesn't do much to make these things super convenient. Its type would just be something like <T extends Project, U extends Project>(p: T) => U.
  2. When you write a plugin, you can tweak this as much as you want. For example ghPagesDeploy could be overloaded and allow parameters of type string (the Package name on the Project), Package (if you happen to have a Package handy), but also (p: T) => Package (so that you could write .add(ghPagesDeploy(self => self.build))). Or it could be much simpler.
  3. We could provide some helper functions that make it easy to write these plugins. For example if you just want to add a field to a project a helper could give you all the above options I think.

But yeah, this needs some api design work. :)

jkarni commented

But ideally ghPagesDeploy could be made to work with all of these alternatives

Yeah, though whenever you have multiple ways of calling things, types and documentation get worse.

.add(self => ghPagesDeploy(self.build)(self))

Why the second self application?

Maybe also:

.add({self} => ghPagesDeploy(self.build))

For backwards-compatibility.

This problem happens for addPackage etc too - a unified solution would be nice.

Yeah, though whenever you have multiple ways of calling things, types and documentation get worse.

Yeah, although we'd probably only document a simple case like .add(ghPagesDeploy(self => self.build)), but under the hood .add is simple enough to grok for anyone to use the other syntaxes easily.

Why the second self application?

We were assuming the type of .add is <T extends Project, U extends Project>(p: T) => U, so a garn plugin implementation that adds a foo helper would look something like this:

.add(self => ({
  ...self,
  foo: () => console.log("foo"),
}))

If we want to support add(self => ghPagesDeploy(self.build)) then the type of add will artificially add another self parameter. E.g. here is a noop plugin:

.add(self => self => ({ ...self }))

This is analogous to nodejs http server middleware where most of the time it just works out of the box like

app.use(someMiddleware())

But occasionally you see simple wrappers like this:

app.use((req, res, next) => {
  if (req.url === '/') someMiddleware()(req, res, next)
  else otherMiddleware()(req, res, next)
})