-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cli-2.0): initial draft #28
base: main
Are you sure you want to change the base?
Changes from 10 commits
9d496ab
6e5838e
dc4eb9d
03d0637
e669b63
ade052f
8d465e3
313a848
45a920a
9120514
373ac57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,393 @@ | ||
# CLI 2.0 <!-- What do you want to call your `awesome_feature`? --> | ||
|
||
- Implementation Owner: @christyjacob4 | ||
- Start Date: 12-07-2021 | ||
- Target Date: (expected date of completion, dd-mm-yyyy) | ||
- Appwrite Issue: NA | ||
|
||
## Summary | ||
|
||
[summary]: #summary | ||
|
||
Improve the Appwrite CLI, adding support for console SDK features, and an improved experience with cloud functions. | ||
|
||
## Problem Statement (Step 1) | ||
|
||
[problem-statement]: #problem-statement | ||
|
||
**What problem are you trying to solve?** | ||
|
||
1. We cannot create and manage projects from the CLI | ||
2. Deploying functions is not very easy using the CLI | ||
3. Developers find it hard to create and test cloud functions locally. | ||
|
||
|
||
**What is the context or background in which this problem exists?** | ||
|
||
The CLI functionality is limited to a Server SDK which prevents us from creating and managing projects. There is also difficulty associated with the creation, packaging and deployment of functions. | ||
|
||
|
||
**Once the proposal is implemented, how will the system change?** | ||
|
||
The CLI will behave more like the console SDK and provide options to create delete and manage all your projects without leaving the CLI. The CLI will also make it easier to create, test and deploy cloud functions. | ||
|
||
## Design proposal (Step 2) | ||
|
||
[design-proposal]: #design-proposal | ||
|
||
<!-- | ||
This is the technical portion of the RFC. Explain the design in sufficient detail keeping in mind the following: | ||
|
||
- Its interaction with other parts of the system is clear | ||
- It is reasonably clear how the contribution would be implemented | ||
- Dependencies on libraries, tools, projects or work that isn't yet complete | ||
- New API routes that need to be created or modifications to the existing routes (if needed) | ||
- Any breaking changes and ways in which we can ensure backward compatibility. | ||
- Use Cases | ||
- Goals | ||
- Deliverables | ||
- Changes to documentation | ||
- Ways to scale the solution | ||
|
||
Ensure that you include examples, code-snippets etc. to allow the community to understand the proposed solution. **It would be best if the examples use naming conventions that you intend to use during the actual implementation so that changes can be suggested early on during the development.** | ||
|
||
Write your answer below. | ||
|
||
--> | ||
|
||
This refactor of the CLI will revolve around the following areas | ||
1. Migration of the CLI from a Server Side SDK to a Console SDK | ||
2. Easier deployment of Cloud Functions | ||
3. Easier creation of Cloud Functions | ||
|
||
|
||
## Migration of the CLI from a Server SDK to a Console SDK | ||
--- | ||
|
||
Until now, the CLI was based off of our Server Side Swagger Spec. While this was a good start, it limits our functionality to a Server SDK. Ideally we want the CLI to be an alternative to the Console SDK ( aka the Appwrite Dashboard ). Here are some changes that we will need to make to adhere to the server spec. | ||
|
||
After the implementation of this RFC, the CLI will behave more like the Appwrite Console rather than a Server SDK. This accounts for a new form of Authentication that is similar to the way we handle logins in the Appwrite console. We use a cookie. | ||
|
||
We will introduce a new command `appwrite login` that will work in one of either ways. | ||
|
||
* `appwrite login` makes a request to the existing `accounts.createSession` endpoint, captures the returned cookie and uses this cookie in all the subsequent requests. | ||
|
||
Whenever we wish to access console specific features, we can authenticate those requests using the cookie and continue to use the API Key based authentication when using the CI. | ||
|
||
```sh | ||
$ appwrite login | ||
Email: | ||
Password: | ||
``` | ||
|
||
In CI environments, the authentication will continue to take place using the API key on a per project basis until we develop a new token / key based mechanism for console authentication. | ||
|
||
```sh | ||
appwrite client --setKey="" --setEndpoint="" ... | ||
``` | ||
|
||
* Second method relies on a browser based auth. | ||
|
||
```sh | ||
$ appwrite login | ||
|
||
# Browser based authentication continues. After a successful login, the returned token and user ID are stored in a hidden preferences file. | ||
``` | ||
|
||
1. The command first finds an available local port. | ||
2. Construct a redirect URL using the available port `https://localhost:1234` | ||
3. Spin up a local http server listening to on the available port ( 1234 in this case ) | ||
4. Opens the browser with a request to either the local appwrite server or appwrite cloud `/authorize/cli?callback=https://localhost:1234` with the redirect URL as one of the query parameters. | ||
|
||
![CLI Auth](cli_auth_flow.png) | ||
|
||
This approach may seem unnecessarily complex now since we only allow email password based logins in the console. But as we move towards supporting OAuth logins in the console, this is the approach that needs to be followed. [Firebase CLI](https://github.com/firebase/firebase-tools/blob/0b0459bfde3fabf41223bb6d5d39a5cf325f1010/src/auth.ts#L476) for reference. | ||
|
||
This method of authentication will require some changes in the Console. | ||
|
||
* **Custom redirect after login** - if the user is not logged into the console, they should be redirect to the sign in page, and after the sign in, should be brought back to `/authorize/cli?callback=https://localhost:1234`. | ||
|
||
* We should also add protection against [open-redirect attack](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html). | ||
|
||
|
||
## Easier Deployment of cloud functions | ||
--- | ||
|
||
### 🟢 Constrain deployments from current directory. | ||
|
||
We start by removing the `--code` parameter in the `createTag` command and constrain function deployments only from the current directory. | ||
|
||
The refactored command will look like this | ||
|
||
```sh | ||
appwrite functions createTag --functionId=[ID] --command=[COMMAND] | ||
``` | ||
|
||
This corresponding appwrite issue will also need to be tackled as we implement this. | ||
https://github.com/appwrite/appwrite/issues/1316 | ||
|
||
### 🟢 The `appwrite.json` File | ||
|
||
The next improvement is going to be along the lines of a `appwrite.json` file. Running `appwrite init` in the current directory will create a `appwrite.json` file and initialize the current directory with attributes necessary to communicate with a particular project. | ||
|
||
Consider this example | ||
```sh | ||
mkdir temp && cd temp | ||
|
||
# Initialize the current directory with an Appwrite project | ||
appwrite init | ||
|
||
temp | ||
└── appwrite.json | ||
``` | ||
|
||
A `appwrite.json` file will be associated with one project only whose ID will be visible in the `project` attribute. | ||
|
||
```json | ||
{ | ||
"project" : "", | ||
"functions": [ | ||
{ | ||
"name" : "My awesome function 💪", | ||
"path" : "./FunctionOne", | ||
"command" : "python main.py", | ||
"runtime": "python3.9", | ||
// This section is useful when we allow cloud functions to be tested locally | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSON can't have comments 😉 |
||
"vars" :{ | ||
"KEY 1" : "VALUE 1", | ||
} | ||
}, | ||
], | ||
"hosting": [] | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to suggest YAML or even TOML, support for them everywhere, easier to read and less prone to type-o frustration with quotes and such. Also... comments. |
||
``` | ||
|
||
There are two scenarios here | ||
* A developer would like to link the directory to an existing project | ||
|
||
```sh | ||
appwrite init | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its this to add the Appwrite stack, to setup for functions or just create the config file with the project id? If project, will it also create the project for us on our endpoints? |
||
Choose a project to link this directory to | ||
⭕️ Project 1 | ||
✅️ Project 2 | ||
⭕️ Project 3 | ||
``` | ||
|
||
* A developer would like to create a new project while initializing this directory | ||
|
||
```sh | ||
appwrite init --new | ||
|
||
Give your project a name: | ||
Project X | ||
|
||
Creating Project X... | ||
|
||
Project X Created 👍 | ||
``` | ||
|
||
> We need to first add support to utopia-php/CLI to allow for single select and multi select commands to enable this feature. | ||
|
||
|
||
### 🟢 The `appwrite deploy` command | ||
|
||
The `appwrite deploy` command is a convenient wrapper around three main commands | ||
* Create Function | ||
* Create Tag | ||
* Update Tag | ||
|
||
```sh | ||
# Usage 1 | ||
appwrite deploy functions | ||
Which functions would you like to deploy? | ||
✅️ FunctionOne | ||
✅️ FunctionTwo | ||
⭕️ FunctionThree | ||
|
||
# Usage 2 | ||
appwrite deploy functions --all | ||
# Deploys all the functions specified in appwrite.json | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about adding a 4th usage for CI environments to deploy some of the projects functions: appwrite deploy functions --names "My awesome function 💪, FunctionTwo" or
appwrite deploy functions --names "My awesome function 💪" "FunctionTwo" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this and others, I believe we should name space the commands |
||
# Future support for static hosting | ||
appwrite deploy site | ||
christyjacob4 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
appwrite deploy site --all | ||
``` | ||
|
||
## Easier creation of cloud functions | ||
--- | ||
|
||
We will introduce an `appwrite generate` command that does the following | ||
* Generate a function / static site template. | ||
* Make an entry in the `appwrite.json` file. Throw an error if the file doesn't exist. | ||
|
||
Here are some example usages | ||
```sh | ||
# Usage 1 | ||
appwrite generate function --name="FunctionOne" --runtime=python-3.9 | ||
# Creates a folder called FunctionOne with a template for a sample python function. | ||
temp | ||
├── appwrite.json | ||
└── FunctionOne | ||
├── .appwrite | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be some information regarding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have everything in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe most major configs are done using appwrite.json then .appwrite can be used for API keys and project keys as it'll be hidden by default right? Aslong as we remind users not to commit .appwrite they can share their project with ease There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreeing with @kodumbeats - If we are going to have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lohanidamodar There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kodumbeats |
||
├── main.py | ||
└── requirements.txt | ||
|
||
|
||
# Usage 2 | ||
appwrite generate function --name="FunctionTwo" --runtime=node-16.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be we can introduce one more parameter here |
||
# Creates a folder called FunctionTwo with a template for a sample node function. | ||
temp | ||
├── appwrite.json | ||
├── FunctionOne | ||
│ ├── .appwrite | ||
│ ├── main.py | ||
│ └── requirements.txt | ||
└── FunctionTwo | ||
├── index.js | ||
├── node_modules | ||
└── package.json | ||
``` | ||
|
||
Under the hood, this command does the following | ||
|
||
1. Clones a publicly hosted git repository and copy's a particular folder into your working directory. This will allow us to fix / modify templates without creating a new CLI release 👍 | ||
|
||
2. Makes an entry in `appwrite.json` | ||
|
||
```json | ||
{ | ||
"project" : "", | ||
"functions": [ | ||
{ | ||
"name" : "My awesome function 💪", | ||
"path" : "./FunctionOne", | ||
"command" : "python main.py", | ||
"runtime": "python3.9" | ||
}, | ||
], | ||
"hosting": [] | ||
} | ||
``` | ||
|
||
## Project Structure | ||
--- | ||
|
||
|
||
### Reader.php | ||
```php | ||
abstract class Reader { | ||
|
||
protected string $path; | ||
|
||
protected array $data; | ||
|
||
function read(): bool {} | ||
|
||
function write(): bool {} | ||
|
||
function getPath():string {} | ||
|
||
function setPath(string $path): void {} | ||
|
||
function getProperty(string $key, mixed $default = null): mixed {} | ||
|
||
function setProperty(string $key, mixed $value): void {} | ||
} | ||
``` | ||
|
||
### Preference.php | ||
|
||
```php | ||
// This class models the hidden preferences.json file | ||
class Preference extends Reader { | ||
|
||
function getKey(): string {} | ||
|
||
function setKey(string $key): void {} | ||
|
||
function getEndpoint(): string {} | ||
|
||
function setEndpoint(string $endpoint): void {} | ||
|
||
function getCookie(): string {} | ||
|
||
function setCookie(string $cookie): void {} | ||
|
||
function getLocale(): string {} | ||
|
||
function setLocale(string $locale): void {} | ||
|
||
function isLoaded(): bool {} | ||
} | ||
|
||
``` | ||
|
||
### Config.php | ||
|
||
|
||
```php | ||
// This class models the appwrite.json file. | ||
class Config extends Reader { | ||
|
||
function getFunction(string $name): array {} | ||
|
||
function getFunctions(): array {} | ||
|
||
function setFunction(array $function): void {} | ||
|
||
function getProject(): string {} | ||
|
||
function setProject(string $id): void {} | ||
|
||
} | ||
``` | ||
|
||
|
||
|
||
### Prior art | ||
|
||
[prior-art]: #prior-art | ||
|
||
<!-- | ||
|
||
Discuss prior art, both the good and the bad, in relation to this proposal. A | ||
few examples of what this can include are: | ||
|
||
- Does this functionality exist in other software and what experience has their | ||
community had? | ||
- For other teams: What lessons can we learn from what other communities have | ||
done here? | ||
- Papers: Are there any published papers or great posts that discuss this? If | ||
you have some relevant papers to refer to, this can serve as a more detailed | ||
theoretical background. | ||
|
||
This section is intended to encourage you as an author to think about the | ||
lessons from other software, provide readers of your RFC with a fuller picture. | ||
If there is no prior art, that is fine - your ideas are interesting to us | ||
whether they are brand new or if it is an adaptation from other software. | ||
|
||
Write your answer below. | ||
--> | ||
|
||
This RFC is inspired by the Firebase CLI. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the Fission CLI have any insights to offer? |
||
### Unresolved questions | ||
|
||
[unresolved-questions]: #unresolved-questions | ||
|
||
<!-- What parts of the design do you expect to resolve through the RFC process before this gets merged? --> | ||
|
||
<!-- Write your answer below. --> | ||
|
||
### Future possibilities | ||
|
||
[future-possibilities]: #future-possibilities | ||
|
||
<!-- This is also a good place to "dump ideas", if they are out of scope for the RFC you are writing but otherwise related. --> | ||
|
||
Along with this refactor, there are a couple of additional improvements that can be made if time permits. | ||
|
||
* Rewrite the current `Parser.php` class | ||
* A command to test functions locally. `appwrite test functions`. This command will leverage the environment variables declared in `appwrite.json` to run the function locally and allow easy testing. | ||
* Allow appwrite installation, upgrade and migration from the CLI. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to pick one or the other? I understand that we should think to support OAuth login, but it's not a great CLI experience if a browser is required to login.