Skip to content

chore(cli): improve logging #289

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
22 changes: 18 additions & 4 deletions src/uipath/_cli/_auth/_auth_server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import http.server
import json
import logging
import os
import socketserver
import ssl
Expand All @@ -9,6 +10,8 @@

from ._oidc_utils import get_auth_config

logger = logging.getLogger(__name__)

load_dotenv()

# Server port
Expand Down Expand Up @@ -38,7 +41,7 @@ def do_POST(self):
content_length = int(self.headers["Content-Length"])
post_data = self.rfile.read(content_length)
token_data = json.loads(post_data.decode("utf-8"))
print("Received authentication information")
logger.info("Received authentication information")

self.send_response(200)
self.end_headers()
Expand All @@ -56,6 +59,7 @@ def do_POST(self):
os.makedirs(uipath_dir, exist_ok=True)
error_log_path = os.path.join(uipath_dir, ".error_log")

logger.debug(f"Writing error log to: {error_log_path}")
with open(error_log_path, "a", encoding="utf-8") as f:
f.write(
f"\n--- Authentication Error Log {time.strftime('%Y-%m-%d %H:%M:%S')} ---\n"
Expand All @@ -66,13 +70,16 @@ def do_POST(self):
self.end_headers()
self.wfile.write(b"Log received")
else:
logger.warning(f"Received request to unknown path: {self.path}")
self.send_error(404, "Path not found")

def do_GET(self):
"""Handle GET requests by serving index.html."""
# Always serve index.html regardless of the path
try:
index_path = os.path.join(os.path.dirname(__file__), "index.html")
logger.debug(f"Serving index.html from: {index_path}")

with open(index_path, "r") as f:
content = f.read()

Expand All @@ -93,7 +100,9 @@ def do_GET(self):
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content.encode("utf-8"))
logger.debug("Successfully served index.html")
except FileNotFoundError:
logger.error(f"Index file not found at: {index_path}")
self.send_error(404, "File not found")

def end_headers(self):
Expand All @@ -106,8 +115,6 @@ def do_OPTIONS(self):
self.send_response(200)
self.end_headers()

return SimpleHTTPSRequestHandler


class HTTPSServer:
def __init__(self, port=6234, cert_file="localhost.crt", key_file="localhost.key"):
Expand All @@ -125,6 +132,7 @@ def __init__(self, port=6234, cert_file="localhost.crt", key_file="localhost.key
self.httpd = None
self.token_data = None
self.should_shutdown = False
logger.debug(f"Initialized HTTPS server on port {port}")

def token_received_callback(self, token_data):
"""Callback for when a token is received.
Expand All @@ -134,6 +142,7 @@ def token_received_callback(self, token_data):
"""
self.token_data = token_data
self.should_shutdown = True
logger.debug("Token received callback triggered")

def create_server(self, state, code_verifier, domain):
"""Create and configure the HTTPS server.
Expand All @@ -146,6 +155,7 @@ def create_server(self, state, code_verifier, domain):
Returns:
socketserver.TCPServer: The configured HTTPS server.
"""
logger.debug("Creating SSL context")
# Create SSL context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(self.cert_file, self.key_file)
Expand All @@ -157,6 +167,7 @@ def create_server(self, state, code_verifier, domain):
)
self.httpd = socketserver.TCPServer(("", self.port), handler)
self.httpd.socket = context.wrap_socket(self.httpd.socket, server_side=True)
logger.debug("Server created successfully")

return self.httpd

Expand All @@ -176,10 +187,11 @@ def start(self, state, code_verifier, domain):

try:
if self.httpd:
logger.info(f"Starting server on port {self.port}")
while not self.should_shutdown:
self.httpd.handle_request()
except KeyboardInterrupt:
print("Process interrupted by user")
logger.info("Process interrupted by user")
finally:
self.stop()

Expand All @@ -188,5 +200,7 @@ def start(self, state, code_verifier, domain):
def stop(self):
"""Stop the server gracefully and cleanup resources."""
if self.httpd:
logger.debug("Stopping server")
self.httpd.server_close()
self.httpd = None
logger.debug("Server stopped")
56 changes: 47 additions & 9 deletions src/uipath/_cli/cli_auth.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
# type: ignore
import json
import logging
import os
import socket
import webbrowser

import click
from dotenv import load_dotenv

from .._utils._logs import setup_logging
from ._auth._auth_server import HTTPSServer
from ._auth._oidc_utils import get_auth_config, get_auth_url
from ._auth._portal_service import PortalService, select_tenant
from ._auth._utils import update_auth_file, update_env_file
from ._utils._common import environment_options

logger = logging.getLogger(__name__)

load_dotenv()


Expand All @@ -32,66 +36,100 @@ def set_port():
port_option_one = auth_config.get("portOptionOne", 8104)
port_option_two = auth_config.get("portOptionTwo", 8055)
port_option_three = auth_config.get("portOptionThree", 42042)

logger.debug(f"Checking port availability. Initial port: {port}")

if is_port_in_use(port):
logger.debug(f"Port {port} is in use, trying alternatives")
if is_port_in_use(port_option_one):
if is_port_in_use(port_option_two):
if is_port_in_use(port_option_three):
logger.error("All configured ports are in use")
raise RuntimeError(
"All configured ports are in use. Please close applications using ports or configure different ports."
)
else:
port = port_option_three
logger.debug(f"Using port option three: {port}")
else:
port = port_option_two
logger.debug(f"Using port option two: {port}")
else:
port = port_option_one
logger.debug(f"Using port option one: {port}")
else:
logger.debug(f"Using initial port: {port}")

auth_config["port"] = port
with open(
os.path.join(os.path.dirname(__file__), "..", "auth_config.json"), "w"
) as f:
config_path = os.path.join(os.path.dirname(__file__), "..", "auth_config.json")
logger.debug(f"Updating auth config at: {config_path}")
with open(config_path, "w") as f:
json.dump(auth_config, f)


@click.command()
@environment_options
def auth(domain="alpha"):
@click.option(
"--verbose",
"-v",
is_flag=True,
help="Enable verbose logging",
)
def auth(domain="alpha", verbose=False):
"""Authenticate with UiPath Cloud Platform."""
# Setup logging based on verbose flag
setup_logging(should_debug=verbose)

logger.debug(f"Starting authentication process for domain: {domain}")
portal_service = PortalService(domain)

if (
os.getenv("UIPATH_URL")
and os.getenv("UIPATH_TENANT_ID")
and os.getenv("UIPATH_ORGANIZATION_ID")
):
logger.debug("Checking existing authentication")
try:
portal_service.ensure_valid_token()
click.echo("Authentication successful")
logger.info("Authentication successful")
return
except Exception:
click.echo(
except Exception as e:
logger.warning(f"Existing authentication invalid: {str(e)}")
logger.info(
"Authentication not found or expired. Please authenticate again."
)

logger.debug("Generating auth URL")
auth_url, code_verifier, state = get_auth_url(domain)

logger.debug("Opening browser for authentication")
webbrowser.open(auth_url, 1)
auth_config = get_auth_config()

print(
logger.info(
"If a browser window did not open, please open the following URL in your browser:"
)
print(auth_url)
logger.info(auth_url)

logger.debug("Starting auth server")
server = HTTPSServer(port=auth_config["port"])
token_data = server.start(state, code_verifier, domain)

try:
if token_data:
logger.debug("Token received, updating services")
portal_service.update_token_data(token_data)
update_auth_file(token_data)
access_token = token_data["access_token"]
update_env_file({"UIPATH_ACCESS_TOKEN": access_token})

logger.debug("Fetching tenants and organizations")
tenants_and_organizations = portal_service.get_tenants_and_organizations()
select_tenant(domain, tenants_and_organizations)
logger.info("Authentication completed successfully")
else:
logger.error("No token data received")
click.echo("Authentication failed")
except Exception as e:
logger.error(f"Authentication failed: {str(e)}")
click.echo(f"Authentication failed: {e}")
Loading
Loading