Skip to content
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
99 changes: 39 additions & 60 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,78 +67,31 @@ jobs:
- name: Install dependencies
run: uv sync --dev

# Handle workflow_dispatch (manual trigger)
- name: Bump version and create release
if: github.event_name == 'workflow_dispatch'
id: create_release
# Compute and stage the new version locally — defer commit/tag/release
# until lint+tests+build+publish all pass, so any failure leaves main untouched.
- name: Compute new version
id: version
run: |
# Get current version from __init__.py using grep/sed (avoid importing)
CURRENT_VERSION=$(grep -E "^__version__ = " src/unstract/api_deployments/__init__.py | sed -E 's/__version__ = "(.*)"/\1/')
echo "Current version: $CURRENT_VERSION"

# Calculate new version based on input
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}

case "${{ github.event.inputs.version_bump }}" in
"major")
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
"minor")
MINOR=$((MINOR + 1))
PATCH=0
;;
"patch")
PATCH=$((PATCH + 1))
;;
"major") MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
"minor") MINOR=$((MINOR + 1)); PATCH=0 ;;
"patch") PATCH=$((PATCH + 1)) ;;
esac

NEW_VERSION="$MAJOR.$MINOR.$PATCH"
echo "New version: $NEW_VERSION"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT

# Update version in __init__.py
sed -i "s/__version__ = \"$CURRENT_VERSION\"/__version__ = \"$NEW_VERSION\"/" src/unstract/api_deployments/__init__.py

# Commit version changes
git add src/unstract/api_deployments/__init__.py
git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
git push origin main

# Create git tag
git tag "v$NEW_VERSION"
git push origin "v$NEW_VERSION"

# Create GitHub release
RELEASE_NOTES="${{ github.event.inputs.release_notes }}"
if [ -z "$RELEASE_NOTES" ]; then
gh release create "v$NEW_VERSION" \
--title "Release v$NEW_VERSION" \
--generate-notes \
${{ github.event.inputs.pre_release == 'true' && '--prerelease' || '' }}
else
gh release create "v$NEW_VERSION" \
--title "Release v$NEW_VERSION" \
--notes "$RELEASE_NOTES" \
--generate-notes \
${{ github.event.inputs.pre_release == 'true' && '--prerelease' || '' }}
fi

echo "Created release v$NEW_VERSION"
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}

# Set version for subsequent steps
- name: Set version output
id: version
run: |
echo "version=${{ steps.create_release.outputs.version }}" >> $GITHUB_OUTPUT

# Verify the version was updated correctly
- name: Verify version update
run: |
PACKAGE_VERSION=$(grep -E "^__version__ = " src/unstract/api_deployments/__init__.py | sed -E 's/__version__ = "(.*)"/\1/')
Expand All @@ -149,27 +102,53 @@ jobs:
exit 1
fi
Comment thread
greptile-apps[bot] marked this conversation as resolved.

# Create test environment
- name: Create test env
run: cp tests/sample.env tests/.env

# Run linting
- name: Run linting
run: uv run ruff check src/

# Run tests
- name: Run tests
run: uv run pytest tests/

# Build the package
- name: Build package
run: uv build

# Publish to PyPI using Trusted Publishers
# PyPI publish first because it is the only step that cannot be undone —
# if a later commit/tag/release step fails, the PyPI artifact is the
# source of truth and the git metadata can be retried manually.
- name: Publish to PyPI
run: uv publish

# Output success message
- name: Commit version bump and create release
run: |
NEW_VERSION="${{ steps.version.outputs.version }}"

git add src/unstract/api_deployments/__init__.py
git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
git push origin main

git tag "v$NEW_VERSION"
git push origin "v$NEW_VERSION"

RELEASE_NOTES="${{ github.event.inputs.release_notes }}"
if [ -z "$RELEASE_NOTES" ]; then
gh release create "v$NEW_VERSION" \
--title "Release v$NEW_VERSION" \
--generate-notes \
${{ github.event.inputs.pre_release == 'true' && '--prerelease' || '' }}
else
gh release create "v$NEW_VERSION" \
--title "Release v$NEW_VERSION" \
--notes "$RELEASE_NOTES" \
--generate-notes \
${{ github.event.inputs.pre_release == 'true' && '--prerelease' || '' }}
fi

echo "Created release v$NEW_VERSION"
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}

- name: Success message
run: |
echo "Successfully published version ${{ steps.version.outputs.version }} to PyPI using uv publish with Trusted Publishers"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ jobs:
- name: Create test env
run: cp tests/sample.env tests/.env

- name: Lint (ruff)
run: uv run ruff check src/

- name: Tests (pytest)
run: uv run pytest tests/ -v
2 changes: 1 addition & 1 deletion src/unstract/api_deployments/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.4.0"
__version__ = "1.2.1"

from .client import APIDeploymentsClient as APIDeploymentsClient

Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(


class NameConflictError(CloneError):
"""Raised when ``on_name_conflict='abort'`` and the target has a like-named entity."""
"""Raised on name collision when ``on_name_conflict='abort'``."""


class DependencyMissingError(CloneError):
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def _clone_one(
tgt = existing[0]
if self.ctx.options.on_name_conflict == "abort":
raise NameConflictError(
f"adapter '{name}' [{atype}] already exists in target as {tgt['id']}"
f"adapter '{name}' [{atype}] already on target as {tgt['id']}"
)
with lock:
result.adopted += 1
Expand Down
4 changes: 2 additions & 2 deletions src/unstract/clone/phases/api_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _clone_one(
tgt_wf_id = self.ctx.remap.resolve("workflow", src_wf_id)
if not tgt_wf_id:
logger.warning(
"no workflow remap for api_deployment '%s' (src workflow %s) — skipping",
"no workflow remap for api_deployment '%s' (src wf %s) — skipping",
api_name,
src_wf_id,
)
Expand All @@ -99,7 +99,7 @@ def _clone_one(
tgt = existing[0]
if self.ctx.options.on_name_conflict == "abort":
raise NameConflictError(
f"api_deployment '{api_name}' already exists in target as {tgt['id']}"
f"api_deployment '{api_name}' already on target as {tgt['id']}"
)
with lock:
result.adopted += 1
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def _clone_one(
metadata = src.get("connector_metadata") or {}
if not metadata:
logger.info(
"skipping connector '%s' (src=%s, catalog=%s) — source returned no metadata",
"skipping connector '%s' (src=%s, catalog=%s) — no source metadata",
name,
src_id,
src.get("connector_id"),
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/custom_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def _create_fresh(
with lock:
result.failed += 1
result.errors.append(
f"import {tool_name}: missing target adapter remap for default profile"
f"import {tool_name}: missing target adapter remap for default"
)
return None

Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def _ensure_default_doc(
tgt_docs = self.ctx.target.list_prompt_documents(tgt_tool_id)
except Exception as e:
logger.warning(
"files: skipping default-doc set for tool=%s — list tgt docs failed: %s",
"files: skipping default-doc set for tool=%s — tgt list failed: %s",
tool_name,
e,
)
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def run(self, report: CloneReport) -> PhaseResult:
skipped_types = len(src_pipelines) - len(migratable)
if skipped_types:
logger.info(
"Found %d source pipeline(s); skipping %d of unsupported type (DEFAULT/APP)",
"Found %d source pipeline(s); skipping %d unsupported (DEFAULT/APP)",
len(src_pipelines),
skipped_types,
)
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/phases/tool_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def _clone_workflow_tools(
return
if len(src_instances) > 1:
logger.warning(
"source workflow %s has %d tool_instances (expected ≤1) — migrating first only",
"source workflow %s has %d tool_instances (>1) — migrating first only",
src_wf_id,
len(src_instances),
)
Expand Down
2 changes: 1 addition & 1 deletion src/unstract/clone/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class PhaseResult:

@dataclass
class Endpoint:
"""Just enough about an endpoint for the report header — never carries the API key."""
"""Endpoint identity for the report header — never carries the API key."""

base_url: str
organization_id: str
Expand Down
Loading