Forge monorepo apps

Since the introduction of Custom UI, I found the dev loop rather painful. Most Custom UI samples instruct devs to start a dev server from each and every custom UI project before running Forge tunnel. Similarly, to deploy each project has to be built manually followed by a Forge deploy. Yes, you could write some scripts to automate this but why should each and every partner re-invent the weel?

I have spent some time trying to find an efficient Forge project setup and ended up with a solution based on Turborepo, or more generally with a monorepo project structure. The goal was to find a project setup that allows deploying with a single command (build all custom UI apps and deploy the Forge app) and starting custom UI apps in dev mode and tunneling the Forge app in one go. Additionally, I was looking for a solution that allows me to easily extract logic shared between the Forge app (FAAS backend) and custom UI into local shared packages.

Turborepo struck me as a simple, lightweight solution that addressed my concerns very well.

You can find my Forge sample project below:

The example is rather simple and everything but complete. But more importantly, starting the dev loop still fails. Seems to be a docker issue when calling forge tunnel.

@atlassian Does anyone have some input/ideas on how the current issues with starting the dev loop could be ironed out?

I thought to share my thoughts and findings here and maybe start a discussion around this topic. Right now Forge seems to have certain preconceptions that make it difficult or even impossible to work with a monorepo project structure and the available tools (e.g. seems custom UI projects have to live within the Forge app folder - at least for tunneling). Also, the current Forge project structure does not really scale in my opinion. I think for a simple Forge UI app this was fine, however, with custom UI things are more complicated.
I think it is crucial for the Forge adoption to have a scalable project structure and clear guidance that all partners can benefit from. After all, project setup is the first step in every Forge project and what is the point of each and every partner re-inventing the wheel on how to configure an efficient, scalable, and easy-to-use Forge Custom UI project?

8 Likes

The development loop with Forge is definitively painful with custom UI. It feels like it was bolted on the last minute. I have yet to be able to get the tunneling to work on my end without having to hack my default web packdev server’s configuration to disable compression.

That said - we do most of our UI development in storybook and abstract out the forge communication layer at that point. This allows us to build out the screens without having to have forge running on our local machines.

We still end up with a monorepo since the Forge CLI doesn’t have the niceties like firebase cli where you can deploy the front end separately from the backend.

2 Likes

It would be so super awesome if Forge CLI was modelled after Firebase CLI. The Firebase CLI is one of the best developer experiences I’ve ever had, both in terms of local development (with emulators for all sorts of services, incl. PubSub) and deployment. And also community support: it is open source, and actively maintained, with PR’s often reviewed within 1-2 business days.

3 Likes

I haven’t worked in production mode with forge yet, so I may not understand the need to have multiple CustomUI.
What I did is to have a switch/if condition in my only CustomUI Entry Point using the {view} Bridge. If you use LazyLoading in addition, only the needed Resources are loaded.

Maybe helpful for some, maybe not: you might not actually need to use a dev server and setup tunnelling and everything.

I can give some more details on my setup if anyone is interested, but I’m basically just having one “custom” directory in my project that has one subdirectory for each custom UI. All of that is just next to the other Forge code in the project, which makes it easy to reuse code on front- and back-end.

Then I’m just using a simple webpack config to build one JS bundle for each of those subdirectories and output those bundles to a “static” output directory along with one index.html each. If your manifest references these bundles in the “static” directory then forge will pick those up, even when they change while forge tunnel is running.

With that setup you can do both full builds and live-reloading (not hot-reload though) pretty easily:

  • Working on custom UI: webpack --watch & forge tunnel
  • Working on backend but have custom UI that needs to be built once: webpack | forge tunnel
  • Full deployment: webpack && forge deploy

There might be situations where this setup does not work, but so far I released multiple Forge apps on the marketplace and it always felt pretty simple and easy to run. :man_shrugging:

4 Likes

Thanks for everyone’s input so far. From what I read in these first few posts it seems partners are already building their own solutions and that they really like the Firebase CLI :slight_smile:

I am wondering if you think that this is something that needs more guidance/support from Atlassian, or if you are ok with the way it is?

Like Daniel said, the Custom UI setup implemented in most of Atlassian’s sample apps is rather rudimentary and probably the most basic approach - individual, independent Custom UI projects and doing everything (starting the build, running dev server) manually for each project. I would consider that as no guidance/figure out your own.

The setup suggested by @thomas2 sounds simple and makes sense to me. I am not 100% sure how well it scales. For example, if you try to keep dependencies separated. I assume in the webpack-based setup you have a single package.json including all frontend, backend, and shared dependencies. This is certainly subjective and even the monorepo tools I have tried (Nx and Turborepo) go different directions when it comes to dependency management.

@m.herrmann if you build a Forge app with multiple integration points using Custom UI (e.g. a Jira issue panel and a Jira issue glance) each of these integration points will likely be its own artifact (HTML, React app). So the question is how you manage dependencies, build and dev loop for those artifacts. In Thomas’ approach, it’s basically a single project (with multiple entry points) all handled by webpack. In my monorepo approach, the idea was to have it decomposed into smaller independent projects that are being built on their own. In this case, a monorepo tool (Turborepo, Nx, etc.) helps to coordinate the build process and local dependencies between projects.

More sophisticated dev tooling would certainly be nice. Having emulators for async events, storage API, etc. that allow developing and building apps locally would help a great deal with developer productivity.

Ultimately, I think this needs a vision from Atlassian on what building a Forge app should look like. If there is no vision, it will be up to each and every partner to figure out their own best way to wrestle with the existing tooling.

1 Like

Hey everyone :wave: I’m Adam, a product manager who recently joined the Forge build team.

Thanks for starting the discussion @tbinna, all this feedback is gold for us. You’re absolutely right, we don’t want every partner re-inventing the wheel just to get their projects set up.

I’m still new here :sweat_smile: so I’ll take this feedback to the team, see what we can do, and come back to the thread soon with a proper response.

9 Likes

We’re also “reinventing the wheel” and are building our solutions around a Forge project setup.

Right now Forge seems to have certain preconceptions that make it difficult or even impossible to work with a monorepo project structure and the available tools (e.g. seems custom UI projects have to live within the Forge app folder - at least for tunneling). Also, the current Forge project structure does not really scale in my opinion. I think for a simple Forge UI app this was fine, however, with custom UI things are more complicated.

I agree with @tbinna. It would be nice to have a more customizable and scaleable project setup.

The example is rather simple and everything but complete. But more importantly, starting the dev loop still fails. Seems to be a docker issue when calling forge tunnel .

The actual problem that @tbinna has already mentioned here is, that the Forge backend must be at root level of the project and cannot be moved to a subdirectory. If that is done (as shown in the Github example), the forge tunnel complains about missing files, when you reference files in the manifest.yml outside the forge backend directory. This is because the forge tunnel command mounts the directory into the Docker container from where the command was executed. You can verify this by starting the forge tunnel and then running docker inspect forge-tunnel-docker. The subdirectory is mounted in the /app mount point of the container.

"Mounts": [{
    "Type": "bind",
    "Source": "/Users/user/my-forge-project/forge",
    "Destination": "/app",
    "Mode": "cached",
    "RW": true,
    "Propagation": "rprivate"
}],

Therefore, anything outside the forge subdirectory is not mounted in the container. This could be easily fixed by providing a way to configure the forge tunnel e.g. setting the project root by parameter or defining it in a .forge-tunnel.json file.

1 Like

@dom.b @AdamMoore interested to know what you think about this: Would you want to develop Forge apps like this?