Skip to content

Commit 7525882

Browse files
committed
feat(workflow): add GitHub Actions workflow for automatic PyPI publishing on version tags
docs(README): update development and deployment instructions, including publishing to PyPI feat(cli): add logout command to clear state and API key information feat(client): extend ClientState to include user information and update user info retrieval chore(pyproject): update project metadata with homepage, documentation, and classifiers
1 parent cc78118 commit 7525882

File tree

6 files changed

+743
-3
lines changed

6 files changed

+743
-3
lines changed

.github/workflows/pypi.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Trigger on version tags
7+
8+
jobs:
9+
build-and-publish:
10+
name: Build and publish to PyPI
11+
runs-on: ubuntu-latest
12+
environment: pypi
13+
permissions:
14+
# Required for trusted publishing to PyPI
15+
id-token: write
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: '3.x'
24+
25+
- name: Install build dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install build twine
29+
30+
- name: Build package
31+
run: python -m build
32+
33+
- name: Publish to PyPI
34+
uses: pypa/gh-action-pypi-publish@release/v1

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,49 @@ The Infactory SDK provides simple and powerful interfaces to work with your data
88
pip install infactory
99
```
1010

11+
## Development and Deployment
12+
13+
### Local Development
14+
15+
1. Clone the repository:
16+
```bash
17+
git clone https://github.com/infactory-io/infactory-py.git
18+
cd infactory-py
19+
```
20+
21+
2. Install Poetry (if not already installed):
22+
```bash
23+
curl -sSL https://install.python-poetry.org | python3 -
24+
```
25+
26+
3. Install dependencies:
27+
```bash
28+
poetry install
29+
```
30+
31+
4. Run tests:
32+
```bash
33+
poetry run pytest
34+
```
35+
36+
### Publishing to PyPI
37+
38+
The package is automatically published to PyPI when a new version tag is pushed to the repository. To release a new version:
39+
40+
1. Update the version in `pyproject.toml`
41+
2. Create and push a new version tag:
42+
```bash
43+
git add pyproject.toml
44+
git commit -m "Bump version to X.Y.Z"
45+
git tag vX.Y.Z
46+
git push origin main --tags
47+
```
48+
49+
The GitHub Actions workflow will automatically:
50+
- Build the package
51+
- Run tests
52+
- Publish to PyPI using trusted publisher configuration
53+
1154
## Getting Started
1255

1356
### Setting your API Key

infactory_client/cli.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
# Configure logging
4343
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
4444
logger = logging.getLogger("infactory-cli")
45+
logger.setLevel(logging.DEBUG)
4546
console = Console()
4647

4748
load_dotenv()
@@ -148,6 +149,37 @@ def login():
148149
typer.echo(f"Failed to login: {e}", err=True)
149150
raise typer.Exit(1)
150151

152+
@app.command()
153+
def logout():
154+
"""Logout and clear all state information."""
155+
try:
156+
# Get config directory
157+
config_dir = get_config_dir()
158+
state_file = config_dir / "state.json"
159+
api_key_file = config_dir / "api_key"
160+
161+
# Remove state file if it exists
162+
if state_file.exists():
163+
state_file.unlink()
164+
typer.echo("State information cleared.")
165+
166+
# Remove API key file if it exists
167+
if api_key_file.exists():
168+
api_key_file.unlink()
169+
typer.echo("API key removed.")
170+
171+
# Check if NF_API_KEY is set in environment
172+
if os.getenv("NF_API_KEY"):
173+
typer.echo("\nNOTE: The NF_API_KEY environment variable is still set.")
174+
typer.echo("To completely logout, you should unset it:")
175+
typer.echo(" export NF_API_KEY=")
176+
177+
typer.echo("\nLogout successful!")
178+
179+
except Exception as e:
180+
typer.echo(f"Error during logout: {e}", err=True)
181+
raise typer.Exit(1)
182+
151183
@app.command()
152184
def show():
153185
"""Show current state including API key (masked), organization, team, and project."""
@@ -173,6 +205,15 @@ def show():
173205
# Add API key
174206
table.add_row("API Key", masked_api_key)
175207

208+
# Show user info if set
209+
if client.state.user_id:
210+
table.add_row("User ID", client.state.user_id)
211+
table.add_row("User Email", client.state.user_email or "Not set")
212+
table.add_row("User Name", client.state.user_name or "Not set")
213+
table.add_row("User Created At", client.state.user_created_at or "Not set")
214+
else:
215+
table.add_row("User", "Not set")
216+
176217
# Show organization info if set
177218
if client.state.organization_id:
178219
try:

infactory_client/client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
class ClientState(BaseModel):
1818
"""Represents the state of the client."""
1919
user_id: Optional[str] = None
20+
user_email: Optional[str] = None
21+
user_name: Optional[str] = None
22+
user_created_at: Optional[str] = None
2023
organization_id: Optional[str] = None
2124
team_id: Optional[str] = None
2225
project_id: Optional[str] = None
@@ -179,7 +182,12 @@ def connect(self):
179182
# Test connection by getting current user
180183
try:
181184
user_info = self._get("v1/authentication/me")
185+
self._logger.debug(f"User info: {user_info}")
186+
is_clerk_user = user_info.get("clerk_user_id") or False
182187
self.state.user_id = user_info.get("id")
188+
self.state.user_email = user_info.get("email") or ("in CLERK" if is_clerk_user else "---")
189+
self.state.user_name = user_info.get("name") or ("in CLERK" if is_clerk_user else "---")
190+
self.state.user_created_at = user_info.get("created_at")
183191
self._save_state()
184192
except Exception as e:
185193
raise AuthenticationError(f"Failed to connect with the provided API key: {e}")

0 commit comments

Comments
 (0)