Skip to content

Latest commit

Β 

History

History
431 lines (288 loc) Β· 12.8 KB

018-cli-2.0.md

File metadata and controls

431 lines (288 loc) Β· 12.8 KB

CLI 2.0

  • Implementation Owner: @christyjacob4
  • Start Date: 12-07-2021
  • Target Date: (expected date of completion, dd-mm-yyyy)
  • Appwrite Issue: NA

Summary

Improve the Appwrite CLI, adding support for console SDK features, and an improved experience with cloud functions.

Problem Statement (Step 1)

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)

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.

    $ 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.

    appwrite client --setKey="" --setEndpoint="" ...
  • Second method relies on a browser based auth.

    $ 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

    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 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.

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

appwrite functions createTag --functionId=[ID] --command=[COMMAND]

This corresponding appwrite issue will also need to be tackled as we implement this. appwrite/appwrite#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

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.

{
    "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
          "vars" :{
            "KEY 1" : "VALUE 1",
          }
        },
    ],
    "hosting": []
}

There are two scenarios here

  • A developer would like to link the directory to an existing project
appwrite init

How would you like to start? 
⭕️ Create a new project
βœ…οΈ Link to an existing project

Choose a project to link this directory to 
⭕️ Project 1 (nowfoms)
βœ…οΈ Project 2 (dfsdda)
⭕️ Project 3 (wedfdd)
  • A developer would like to create a new project while initializing this directory
appwrite init

How would you like to start? 
βœ…οΈ Create a new project
⭕️ Link to an existing project

Give your project a name:
Project X

Creating Project X...

Project X Created πŸ‘

Once you choose the project, appwrite.json is initialized with the newly created project's ID?

{
  "projectId" : "xyz"
}

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 to activate it
# Usage 1
appwrite deploy functions --confirm 

Which functions would you like to deploy? 
βœ…οΈ FunctionOne
βœ…οΈ FunctionTwo
⭕️ FunctionThree

Deploying FunctionOne.. 
FunctionOne already exists 
Updating FunctionOne with new tag
Do you want to activate FunctionOne? [Yes/No]


# Usage 2
appwrite deploy functions --all
# Deploys all the functions specified in appwrite.json

# Future support for static hosting
appwrite deploy site
appwrite deploy site --all 

Easier creation of cloud functions


We will introduce an appwrite init function 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

# Usage 1
appwrite init function --functionId="abc" --name="FunctionOne" --runtime=python-3.9
# Creates a folder called FunctionOne with a template for a sample python function.
temp
β”œβ”€β”€ appwrite.json
└── FunctionOne
    β”œβ”€β”€ .appwrite
    β”œβ”€β”€ main.py
    └── requirements.txt


# Usage 2
appwrite init function --functionId="xyz" --name="FunctionTwo" --runtime=node-16.0
# 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


# Usage 3
appwrite init function

Give your function an ID
function_three

Give your function a name:
FunctionThree

Select a runtime:
python-3.9

Initializing function X ..

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

{
    "project" : "xyz",
    "functions": [
        {
          "name" : "My awesome function πŸ’ͺ",
          "path" : "./FunctionOne",
          "command" : "python main.py",
          "runtime": "python3.9"
        },
    ],
    "hosting": []
}

Project Structure


Reader.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

// 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

// 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

This RFC is inspired by the Firebase CLI.

Unresolved questions

Future possibilities

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.