redwoodjs / redwood

The App Framework for Startups
https://redwoodjs.com
MIT License
17.33k stars 993 forks source link

[RFC]: Add ability to show messages from Redwood during next CLI invocation to inform users of important stuff #6113

Open cannikin opened 2 years ago

cannikin commented 2 years ago

Summary

It would be great if we could somehow communicate important Redwood notifications/announcements to users. The easiest way (for the users) would be to notify them on their next CLI command. At the start of the command, the CLI process could "phone home" to a server, or GitHub repo, and look for messages the user hasn't seen yet. If there are any, it could be displayed at the end of the CLI command, maybe color-coded to severity.

In the simplest case we could use it to announce new version releases. Right now we have to count on someone either coming to the site, the repo, or running a random yarn rw upgrade to find out there's a new version.

It would be extra cool if you could only notify some users based on some condition, like they're using a certain auth provider (see Motivation).

Motivation

We have a potential change to the codebase that, when released, would cause everyone to see a big scary warning message the next time they run their test suite, even if all the tests are passing. Unfortunately, this change is only needed for users that are using Clerk for auth, but the scary warning will be seen by everyone. We also have a second change scheduled that would eliminate the warning message, but it won't be ready for a couple of months.

But, if we could notify just Clerk users that we have a fix coming, but could include instructions for a simple fix in the meantime (until the second change goes live) then we could help the people that experiencing the problem but not inconvenience/confuse everyone else.

Detailed proposal

Where do the messages come from? We've got a database and service running for Telemetry, but I'm thinking this could come from something even simpler, like a GitHub repo. There would be a nice side effect of making sure all of the messages are open and available for anyone to see.

I'm thinking CSV format or something similar, that's something like:

  1. a timestamp of when the message was added (we could maybe not show messages if they're older than X days)
  2. the severity of the message
  3. the message itself
  4. a script name that could be downloaded and executed that contains logic to determine if you're using a certain feature (the scripts can live in the repo along with the warning messages)
2022-08-01 12:00:00,notice,RedwoodJS v3.0 is now available! See https://github.com/redwood/redwoojs for details,
2022-08-01 02:00:00,warning,It looks like you're using Clerk for authentication. We're working on a fix, but in in the meantime you can patch your codebase: https://community.redwoodjs.com/clerk-js-patch,clerkTest.js

I'm not sure what the security implications are of running these scripts in the context of the user's app though. Presumably they'd only be created by us so we can make sure they're not doing anything malicious. Or we just skip that entirely and everyone sees every message.

Alternate, security-friendly implementation: include path(s) and a regex that, if it matches against the app's file list, will trigger the message. For example, the following data structure:

{ paths: ['web/src/App.js'], match: /AuthProvider.*?clerk/ }

Would try to run the match against any files in paths and if there's a match, show the warning for apps that are using Clerk for auth. It wouldn't be able to do stuff like "it must match string A in file 1, but not match string B in file 2" but could be a good start.

Multiple Messages

Not sure what to do if there's multiple messages since the last time you received one. Maybe we only show ones marked as status critical, as well as the latest version update one?

Storing State

Similar to how telemetry works, we could create a text file in /.redwood with the timestamp of the last viewed message.

Disablement

Users should be able to disable these checks for messages with an ENV var, similar to disabling telemetry.

Are you interested in working on this?

Tobbe commented 2 years ago

Another thing we discussed a few days ago that could possibly have similar mechanics is a "pre-update check" script. A script that would run before you try to update to a given version, checking your environment for anything that might block you from upgrading.

The specific motivation for that feature was the cell prerender PR. It will use PageName.routeHooks.js files. So we wanted to run a check before upgrading to check if the user already had any files with that name and ask them to rename them before upgrading.

We could also use it to detect if any codemod needs to be executed, and possibly even offer to run said codemods automatically for the user.

Similar to the solution for cli messages we thought we could have a GH repo of pre-update check scripts, all tagged (probably put in folders) with different RW versions. If there are any scripts for the version the user is upgrading to we would download those from the GH repo and run them. And we had the same security worries as already expressed for the cli messages rfc.

BBurnworth commented 2 years ago

This idea might be nice for helping users around issues like this: https://github.com/redwoodjs/redwood/issues/6194 which will probably be temporary and patched in the next run... but it would inform the user how to work around bugs quicker.

cannikin commented 2 years ago

Made an update in the description for a more security-friendly way to determine if someone is using a feature in an app (regex to the rescue!).

cannikin commented 2 years ago

Related to: https://github.com/redwoodjs/redwood/issues/537

Josh-Walker-GM commented 2 years ago

EDITED: To reflect my updated thoughts now I've gained a little bit of experience with something similar and to sick to messaging and disregard upgrade related stuff which probably requires it's own RFC.

I've taken into consideration the great ideas in this RFC and gave it some thought myself, hope it's welcome. I'll try to document my ideas here.

I think we should have a separate repo which contains the generic messages. I think this gives the best balance instead of trying to amalgamate them into one message type.

The benefits of using a github repo in my mind are:

  1. Transparency - the content is visible to all, trustworthy
  2. Easy to fetch - simple web requests to get the data
  3. Potential automation - CI gives potential automation to some of the update or message data for example

CLI We can perform silent background tasks when a user runs a CLI command. We can use this to periodically fetch the latest messages from the repo. We could then, again periodically, let the user know of any new unread messages by appending a small unintrusive notice at the end of their CLI command.

It is probably wise to have a specific command like rw messages which would perform the task of showing the user messages.

Generic Messages I propose some messages.json file in the github repo which contains an array of message objects each with a structure:

interface GenericMessage {
    id: string // some uuid to identify the message by
    created: string // the datetime the message was created 
    level: string // e.g. "critical", "warning", "info" etc.
    message: string // the message content
    conditions: GenericMessageCondition[][] // specific conditional logic to determine whether to show the user the message
    maxDisplay: number // maximum number of times to display the message
    maxAge: number // maximum age after which the message should be ignored
    force: boolean // if true always display the message
}

We would have a local copy of the messages.json stored within the project. I would then also have another file like acknowledged.json which would store an array of {id: "...", shown: n}. This would be an effective way of tracking both if a message has been shown and how many times it has been. The messages command can prune the acknowledged.json file and remove any objects with id's not in messages.json as well as insert any new entries with the default {id: "...", shown: 0}.

Pruning the remote messages.json on the repo could be automated using the maxAge for example.

I propose handling the conditions using an array of arrays of conditional logic objects. I would then consider all the first level arrays to be or'd together and all the second level arrays to be and'd together. Some examples of this are:

[[{}]] - one single condition
[[{}, {}]] - simple and
[[{}], [{}]] - simple or
[[{}, {}], [{}], [{}, {}, {}]] - complex combination: (A and B) or (C) or (D and E and F)

This I think will allow us good control over the conditional logic by which we show users messages. I think using this structure it is also easily possible to shortcut so as not needing to evaluate every condition if we don't have to.

The conditional logic objects themselves I suggest a structure something like:

interface GenericMessageCondition{
    check: string // "file" or "content" so as to check if a file exists or the file content 
    paths: string[] // file paths to check
    match: string // regex condition
    negate: boolean // if true negate the final value
}

I think this approach is reasonably simple but also powerful enough to give high configurability. I like the idea of simply matching existing file paths or file content using regex rather than arbitrary scripts for generic messages.

Please do give feedback, I hope I haven't over engineered anything here but have the feeling I may have. I've also attempted to provide maximum configurability but simplifications could be made if we limit some of our ability to configure.

cannikin commented 1 year ago

Found prisma's upgrade notice:

image