Skip to content
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: allow CLI to find a different port - WF-76 #577

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/framework/chat-assistant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Next, open your terminal and navigate to the directory where you want to create
This command sets up a new project called `chat-assistant` in the specified directory.
</Step>
<Step title="Edit your project">
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:3006`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:4005`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.

<CodeGroup>
```bash Standard port
Expand Down Expand Up @@ -151,4 +151,4 @@ Once the application is deployed, the CLI will return with the URL of your live

## Conclusion

That's all it takes to set up a basic application with the Writer Framework. This setup not only demonstrates the platform's capabilities, but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
That's all it takes to set up a basic application with the Writer Framework. This setup not only demonstrates the platform's capabilities, but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
6 changes: 3 additions & 3 deletions docs/framework/product-description-generator.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ Next, open your terminal and navigate to the directory where you want to create
This command sets up a new project called `product-description-app` in the specified directory using a template designed for this tutorial.
</Step>
<Step title="Edit your project">
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:3006`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:4005`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.

<CodeGroup>
```bash Standard port
writer edit product-description-app
```

```bash Custom port
writer edit product-description-app port=3007
writer edit product-description-app --port=3007
```
</CodeGroup>
</Step>
Expand Down Expand Up @@ -377,4 +377,4 @@ Once the application is deployed, the CLI will return with the URL of your live

## Conclusion

You’ve now built a full application with the Writer Framework and the Writer AI module. Congratulations! This application not only demonstrates the platform's capabilities but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
You’ve now built a full application with the Writer Framework and the Writer AI module. Congratulations! This application not only demonstrates the platform's capabilities but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
2 changes: 1 addition & 1 deletion docs/framework/quickstart-tutorial.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Once this is done, we can finally run the Framework editor using the command:
writer edit .
```

This will run our Framework instance. Runtime logs can be observed in the terminal, and the app is available at http://localhost:3006.
This will run our Framework instance. Runtime logs can be observed in the terminal, and the app is available at http://localhost:4005.

![newly created application](./images/quickstart/new_app.png)

Expand Down
8 changes: 8 additions & 0 deletions docs/framework/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ It's not recommended to expose Framework to the Internet. If you need to access
If you need to disable this protection, use the flag `--enable-remote-edit`.
</Warning>

<Info>
The editor starts by default on port 4005. If you launch multiple editors in parallel and do not specify a port, Writer Framework will automatically assign the next port until reaching the limit of 4099.
</Info>

## Run an app

When your app is ready, execute the `run` command, which will allow others to run, but not edit, your Framework app.
Expand All @@ -69,6 +73,10 @@ When your app is ready, execute the `run` command, which will allow others to ru
writer run my_app
```

<Info>
Your app starts by default on port 3005. If you launch multiple apps in parallel and do not specify a port, Writer Framework will automatically assign the next port until reaching the limit of 3099.
</Info>

You can specify a port and host. Specifying `--host 0.0.0.0` enables you to share your application in your local network.

```sh
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/release-notes-generator.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Next, open your terminal and navigate to the directory where you want to create
```
</Step>
<Step title="Edit your project">
To edit your project, run the following commands. This will bring up the console, which displays Framework-wide messages and errors, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:3006`. If this port is in use, you can specify a different port. Open this address in your browser to view your default application setup.
To edit your project, run the following commands. This will bring up the console, which displays Framework-wide messages and errors, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:4005`. If this port is in use, you can specify a different port. Open this address in your browser to view your default application setup.

<CodeGroup>
```bash Standard port
Expand Down Expand Up @@ -422,4 +422,4 @@ You'll be prompted for your API key.
Once the application is deployed, the CLI will return with the URL of your live application.

## Conclusion
By following these steps, you've created a complete Release notes generator application using Writer Framework. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
By following these steps, you've created a complete Release notes generator application using Writer Framework. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
6 changes: 3 additions & 3 deletions docs/framework/social-post-generator.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ Next, open your terminal and navigate to the directory where you want to create
This command will set up a new project called `social-generator` in the specified directory using an `ai-starter` template.
</Step>
<Step title="Edit project">
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:3006`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.
To edit your project, run the below commands. This will bring up the console, where Framework-wide messages and errors will appear, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:4005`. If that port is in use, you can specify a different port. Open this address in your browser to view your default application setup.

<CodeGroup>
```bash Standard port
writer edit social-generator
```

```bash Custom port
writer edit social-generator port=3007
writer edit social-generator --port=3007
```
</CodeGroup>
</Step>
Expand Down Expand Up @@ -216,4 +216,4 @@ Once the application is deployed, the CLI will return with the URL of your live

## Conclusion

That's all it takes to set up a basic application with the Writer Framework. This setup not only demonstrates the platform's capabilities but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
That's all it takes to set up a basic application with the Writer Framework. This setup not only demonstrates the platform's capabilities but also provides a foundation on which you can build more complex applications. To learn more, explore the rest of the Writer Framework documentation and the API documentation.
12 changes: 6 additions & 6 deletions src/writer/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ def main():

@main.command()
@click.option('--host', default="127.0.0.1", help="Host to run the app on")
@click.option('--port', default=3005, help="Port to run the app on")
@click.option('--port', default=None, help="Port to run the app on")
@click.argument('path')
def run(path, host, port):
def run(path: str, host: str, port: Optional[int]):
"""Run the app from PATH folder in run mode."""

abs_path = os.path.abspath(path)
Expand All @@ -34,11 +34,11 @@ def run(path, host, port):

@main.command()
@click.option('--host', default="127.0.0.1", help="Host to run the app on")
@click.option('--port', default=3006, help="Port to run the app on")
@click.option('--port', default=None, help="Port to run the app on")
@click.option('--enable-remote-edit', help="Set this flag to allow non-local requests in edit mode.", is_flag=True)
@click.option('--enable-server-setup', help="Set this flag to enable server setup hook in edit mode.", is_flag=True)
@click.argument('path')
def edit(path, port, host, enable_remote_edit, enable_server_setup):
def edit(path: str, port: Optional[int], host: str, enable_remote_edit: bool, enable_server_setup: bool):
"""Run the app from PATH folder in edit mode."""

abs_path = os.path.abspath(path)
Expand All @@ -63,9 +63,9 @@ def create(path, template):

@main.command()
@click.option('--host', default="127.0.0.1", help="Host to run the app on")
@click.option('--port', default=3006, help="Port to run the app on")
@click.option('--port', default=None, help="Port to run the app on")
@click.option('--enable-remote-edit', help="Set this flag to allow non-local requests in edit mode.", is_flag=True)
def hello(port, host, enable_remote_edit):
def hello(port: Optional[int], host: str, enable_remote_edit):
"""Create and run an onboarding 'Hello' app."""
create_app("hello", template_name="hello", overwrite=True)
writer.serve.serve("hello", mode="edit",
Expand Down
53 changes: 41 additions & 12 deletions src/writer/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import os.path
import pathlib
import socket
import textwrap
import typing
from contextlib import asynccontextmanager
Expand Down Expand Up @@ -500,7 +501,7 @@ def register_auth(
):
auth.register(app, callback=callback, unauthorized_action=unauthorized_action)

def serve(app_path: str, mode: ServeMode, port, host, enable_remote_edit=False, enable_server_setup=False):
def serve(app_path: str, mode: ServeMode, port: Optional[int], host, enable_remote_edit=False, enable_server_setup=False):
""" Initialises the web server. """

print_init_message()
Expand All @@ -513,6 +514,14 @@ def on_load():
Loading of the server_setup.py is active by default
when Writer Framework is launched with the run command.
"""
if port is None:
mode_allowed_ports = {
'run': (3005, 3099),
'edit': (4005, 4099)
}

port = _next_localhost_available_port(mode_allowed_ports[mode])

enable_server_setup = mode == "run" or enable_server_setup
app = get_asgi_app(app_path, mode, enable_remote_edit, on_load=on_load, enable_server_setup=enable_server_setup)
log_level = "warning"
Expand Down Expand Up @@ -599,17 +608,6 @@ def _mount_server_static_path(app: FastAPI, server_static_path: pathlib.Path) ->
if f.is_dir():
app.mount(f"/{f.name}", StaticFiles(directory=f), name=f"server_static_{f}")

def _execute_server_setup_hook(user_app_path: str) -> None:
"""
Runs the server_setup.py module if present in the application directory.

"""
server_setup_path = os.path.join(user_app_path, "server_setup.py")
if os.path.isfile(server_setup_path):
spec = cast(ModuleSpec, importlib.util.spec_from_file_location("server_setup", server_setup_path))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore


def app_runner(asgi_app: WriterFastAPI) -> AppRunner:
return asgi_app.state.app_runner
Expand All @@ -632,3 +630,34 @@ def wf_root_static_assets() -> List[pathlib.Path]:
all_static_assets.append(f)

return all_static_assets


def _execute_server_setup_hook(user_app_path: str) -> None:
"""
Runs the server_setup.py module if present in the application directory.

"""
server_setup_path = os.path.join(user_app_path, "server_setup.py")
if os.path.isfile(server_setup_path):
spec = cast(ModuleSpec, importlib.util.spec_from_file_location("server_setup", server_setup_path))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore


def _next_localhost_available_port(port_range: Tuple[int, int]) -> int:
"""
Searches for a free port in a given range on localhost to start the server

>>> port = _next_localhost_available_port((3005, 3099))

3005 is the first port to be tested. If it is not available, the port 3006 is tested, and so on.
"""
for port in range(port_range[0], port_range[1]):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex(('127.0.0.1', port))
sock.close()
if result != 0:
return port

raise OSError(f"No free port found to start the server between {port_range[0]} and {port_range[1]} .")
Loading