Skip to content

Commit e273aee

Browse files
committed
chore: add ci github actions
1 parent 1389398 commit e273aee

File tree

10 files changed

+169
-26
lines changed

10 files changed

+169
-26
lines changed

.github/workflows/publish.yml renamed to .github/workflows/cd.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ on:
99
- pyproject.toml
1010

1111
jobs:
12+
lint:
13+
uses: ./.github/workflows/lint.yml
14+
1215
build:
1316
name: Build
1417
runs-on: ubuntu-latest

.github/workflows/cli.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths-ignore:
8+
- pyproject.toml
9+
pull_request:
10+
branches:
11+
- main
12+
13+
jobs:
14+
commit-lint:
15+
if: ${{ github.event_name == 'pull_request' }}
16+
uses: ./.github/workflows/commitlint.yml
17+
18+
lint:
19+
uses: ./.github/workflows/lint.yml

.github/workflows/commitlint.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Commit Lint
2+
3+
on:
4+
workflow_call
5+
6+
jobs:
7+
commitlint:
8+
name: Commit Lint
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v3
21+
with:
22+
node-version: 22
23+
24+
- name: Install Git
25+
run: |
26+
if ! command -v git &> /dev/null; then
27+
echo "Git is not installed. Installing..."
28+
sudo apt-get update
29+
sudo apt-get install -y git
30+
else
31+
echo "Git is already installed."
32+
fi
33+
34+
- name: Install commitlint
35+
run: |
36+
npm install conventional-changelog-conventionalcommits
37+
npm install commitlint@latest
38+
npm install @commitlint/config-conventional
39+
40+
- name: Configure
41+
run: |
42+
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
43+
44+
- name: Validate PR commits with commitlint
45+
run: |
46+
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr_branch
47+
npx commitlint --from ${{ github.event.pull_request.base.sha }} --to pr_branch --verbose

.github/workflows/lint.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Lint
2+
3+
on:
4+
workflow_call
5+
6+
jobs:
7+
# Job that runs when custom version testing is enabled - just completes successfully
8+
skip-lint:
9+
name: Skip Lint (Custom Version Testing)
10+
runs-on: ubuntu-latest
11+
if: contains(github.event.pull_request.labels.*.name, 'test-core-dev-version')
12+
permissions:
13+
contents: read
14+
steps:
15+
- name: Skip lint for custom version testing
16+
run: |
17+
echo "Custom version testing enabled - skipping normal lint process"
18+
echo "This job completes successfully to allow PR merging"
19+
20+
# Job that runs normal lint process when custom version testing is NOT enabled
21+
lint:
22+
name: Lint
23+
runs-on: ubuntu-latest
24+
if: "!contains(github.event.pull_request.labels.*.name, 'test-core-dev-version')"
25+
permissions:
26+
contents: read
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v4
31+
32+
- name: Setup uv
33+
uses: astral-sh/setup-uv@v5
34+
with:
35+
enable-cache: true
36+
37+
- name: Setup Python
38+
uses: actions/setup-python@v5
39+
with:
40+
python-version-file: ".python-version"
41+
42+
- name: Install dependencies
43+
run: uv sync --all-extras
44+
45+
- name: Check static types
46+
run: uv run mypy --config-file pyproject.toml .
47+
48+
- name: Check linting
49+
run: uv run ruff check .
50+
51+
- name: Check formatting
52+
run: uv run ruff format --check .
53+

src/uipath_mcp/_cli/_runtime/_runtime.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
148148

149149
# Set up SignalR client
150150
uipath_url = os.environ.get("UIPATH_URL")
151-
signalr_url = f"{uipath_url}/agenthub_/wsstunnel?slug={self._server.name}&runtimeId={self._runtime_id}"
151+
signalr_url = f"{uipath_url}/agenthub_/wsstunnel?slug={self.slug}&runtimeId={self._runtime_id}"
152152

153153
if not self.context.folder_key:
154154
folder_path = os.environ.get("UIPATH_FOLDER_PATH")
@@ -159,7 +159,9 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
159159
"Please set the UIPATH_FOLDER_PATH or UIPATH_FOLDER_KEY environment variable.",
160160
UiPathErrorCategory.USER,
161161
)
162-
self.context.folder_key = self._uipath.folders.retrieve_key(folder_path=folder_path)
162+
self.context.folder_key = self._uipath.folders.retrieve_key(
163+
folder_path=folder_path
164+
)
163165
if not self.context.folder_key:
164166
raise UiPathMcpRuntimeError(
165167
"REGISTRATION_ERROR",
@@ -495,14 +497,14 @@ async def _on_session_start_error(self, session_id: str) -> None:
495497
try:
496498
response = await self._uipath.api_client.request_async(
497499
"POST",
498-
f"agenthub_/mcp/{self.slug}/out/message?sessionId={session_id}", # type: ignore
500+
f"agenthub_/mcp/{self.slug}/out/message?sessionId={session_id}",
499501
json=JSONRPCResponse(
500502
jsonrpc="2.0",
501503
id=0,
502504
result={
503505
"protocolVersion": "initialize-failure",
504506
"capabilities": {},
505-
"serverInfo": {"name": self.slug, "version": "1.0"}, # type: ignore
507+
"serverInfo": {"name": self.slug, "version": "1.0"},
506508
},
507509
).model_dump(),
508510
)
@@ -574,7 +576,7 @@ async def _on_runtime_abort(self) -> None:
574576
try:
575577
response = await self._uipath.api_client.request_async(
576578
"POST",
577-
f"agenthub_/mcp/{self.slug}/runtime/abort?runtimeId={self._runtime_id}", # type: ignore
579+
f"agenthub_/mcp/{self.slug}/runtime/abort?runtimeId={self._runtime_id}",
578580
)
579581
if response.status_code == 202:
580582
logger.info(
@@ -618,7 +620,7 @@ def packaged(self) -> bool:
618620

619621
@property
620622
def slug(self) -> str:
621-
return self.context.server_slug or self._server.name
623+
return self.context.server_slug or self._server.name # type: ignore
622624

623625
@property
624626
def server_type(self) -> UiPathServerType:

src/uipath_mcp/_cli/_runtime/_session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ async def _run_server(self, server_params: StdioServerParameters) -> None:
133133
try:
134134
if self._read_stream is None:
135135
logger.error("Read stream is not initialized")
136-
continue
136+
break
137137

138138
session_message = await self._read_stream.receive()
139139
if isinstance(session_message, Exception):

src/uipath_mcp/_cli/cli_init.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import json
33
import uuid
44
from typing import Any, Callable, overload
5+
56
from uipath._cli._utils._console import ConsoleLogger
67
from uipath._cli.middlewares import MiddlewareResult
78

89
from ._utils._config import McpConfig
910

1011
console = ConsoleLogger()
1112

13+
1214
async def mcp_init_middleware_async(
1315
entrypoint: str,
1416
options: dict[str, Any] | None = None,
@@ -52,7 +54,7 @@ async def mcp_init_middleware_async(
5254
with open(config_path, "w") as f:
5355
json.dump(uipath_data, f, indent=2)
5456

55-
console.success(f"Created '{config_path}' file." )
57+
console.success(f"Created '{config_path}' file.")
5658
return MiddlewareResult(should_continue=False)
5759

5860
except Exception as e:

src/uipath_mcp/_cli/cli_new.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import os
22
import shutil
3-
from typing import Tuple, Optional, List
3+
from typing import List, Optional, Tuple
44

55
import click
66
from uipath._cli._utils._console import ConsoleLogger
77
from uipath._cli.middlewares import MiddlewareResult
88

99
console = ConsoleLogger()
1010

11+
1112
def clean_directory(directory: str) -> None:
1213
"""Clean up Python files in the specified directory.
1314
@@ -20,11 +21,17 @@ def clean_directory(directory: str) -> None:
2021
for file_name in os.listdir(directory):
2122
file_path = os.path.join(directory, file_name)
2223

23-
if os.path.isfile(file_path) and file_name.endswith('.py'):
24+
if os.path.isfile(file_path) and file_name.endswith(".py"):
2425
# Delete the file
2526
os.remove(file_path)
2627

27-
def write_template_file(target_directory:str, file_path: str, file_name: str, replace_tuple: Optional[List[Tuple[str, str]]] = None) -> None:
28+
29+
def write_template_file(
30+
target_directory: str,
31+
file_path: str,
32+
file_name: str,
33+
replace_tuple: Optional[List[Tuple[str, str]]] = None,
34+
) -> None:
2835
"""Write a template file to the target directory with optional placeholder replacements.
2936
3037
Args:
@@ -37,9 +44,7 @@ def write_template_file(target_directory:str, file_path: str, file_name: str, re
3744
This function copies a template file to the target directory and optionally replaces placeholders
3845
with specified values. It logs a success message after creating the file.
3946
"""
40-
template_path = os.path.join(
41-
os.path.dirname(__file__), file_path
42-
)
47+
template_path = os.path.join(os.path.dirname(__file__), file_path)
4348
target_path = os.path.join(target_directory, file_name)
4449
if replace_tuple is not None:
4550
# replace the template placeholders
@@ -67,24 +72,22 @@ def generate_files(target_directory: str, server_name: str):
6772
- pyproject.toml: Project metadata and dependencies
6873
"""
6974
write_template_file(
70-
target_directory,
71-
"_templates/server.py.template",
72-
"server.py",
73-
None
75+
target_directory, "_templates/server.py.template", "server.py", None
7476
)
7577
write_template_file(
7678
target_directory,
7779
"_templates/mcp.json.template",
7880
"mcp.json",
79-
[("$server_name", server_name)]
81+
[("$server_name", server_name)],
8082
)
8183
write_template_file(
8284
target_directory,
8385
"_templates/pyproject.toml.template",
8486
"pyproject.toml",
85-
[("$project_name", server_name)]
87+
[("$project_name", server_name)],
8688
)
8789

90+
8891
def mcp_new_middleware(name: str) -> MiddlewareResult:
8992
"""Create a new MCP server project with template files.
9093
@@ -105,7 +108,9 @@ def mcp_new_middleware(name: str) -> MiddlewareResult:
105108
directory = os.getcwd()
106109

107110
try:
108-
with console.spinner(f"Creating new mcp server '{name}' in current directory ..."):
111+
with console.spinner(
112+
f"Creating new mcp server '{name}' in current directory ..."
113+
):
109114
clean_directory(directory)
110115
generate_files(directory, name)
111116
init_command = """uipath init"""
@@ -117,11 +122,21 @@ def mcp_new_middleware(name: str) -> MiddlewareResult:
117122
line = click.style("═" * 60, bold=True)
118123

119124
console.info(line)
120-
console.info(click.style(f"""Start '{name}' as a self-hosted MCP server""", fg="magenta", bold=True))
125+
console.info(
126+
click.style(
127+
f"""Start '{name}' as a self-hosted MCP server""",
128+
fg="magenta",
129+
bold=True,
130+
)
131+
)
121132
console.info(line)
122133

123-
console.hint(f""" 1. Set {click.style("UIPATH_FOLDER_PATH", fg="cyan")} environment variable""")
124-
console.hint(f""" 2. Start the server locally: {click.style(run_command, fg="cyan")}""")
134+
console.hint(
135+
f""" 1. Set {click.style("UIPATH_FOLDER_PATH", fg="cyan")} environment variable"""
136+
)
137+
console.hint(
138+
f""" 2. Start the server locally: {click.style(run_command, fg="cyan")}"""
139+
)
125140
return MiddlewareResult(should_continue=False)
126141
except Exception as e:
127142
console.error(f"Error creating demo agent {str(e)}")

src/uipath_mcp/middlewares.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from uipath._cli.middlewares import Middlewares
22

33
from ._cli.cli_init import mcp_init_middleware
4-
from ._cli.cli_run import mcp_run_middleware
54
from ._cli.cli_new import mcp_new_middleware
5+
from ._cli.cli_run import mcp_run_middleware
6+
67

78
def register_middleware():
89
"""This function will be called by the entry point system when uipath-mcp is installed"""

uv.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)