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
46 changes: 46 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: "Version, no leading v (e.g. 1.1.5)"
required: true

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Guard against non-main dispatch
run: |
if [ "${{ github.ref }}" != "refs/heads/main" ]; then
echo "Release must be dispatched from main, got ${{ github.ref }}"; exit 1
fi

- name: Validate version and guard against existing tag
run: |
v="${{ inputs.version }}"
echo "$v" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || { echo "Bad version format: $v"; exit 1; }
if git ls-remote --exit-code --tags origin "refs/tags/v$v" >/dev/null 2>&1; then
echo "Tag v$v already exists on origin"; exit 1
fi

- name: Bump utils/version.py
run: |
v="${{ inputs.version }}"
sed -i "s/^__version__ = .*/__version__ = \"$v\"/" utils/version.py
grep -q "^__version__ = \"$v\"$" utils/version.py || { echo "sed did not update version"; exit 1; }

- name: Commit, tag, and push to main
run: |
v="${{ inputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "Release v$v"
git tag "v$v"
git push origin HEAD --follow-tags
4 changes: 0 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
2. Click your user profile (top right corner).
3. Select 'Keys' from the menu.
4. Click 'New Key' and copy the provided key.

Please do not modify CLI_VERSION; it is used for debugging purposes.
"""

CLI_VERSION = "v1.0.0"

HOST = ""
KEY = ""
3 changes: 2 additions & 1 deletion core/utils_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import Any

from core.utils_db import load_data
from utils.version import __version__

# Configure logger
logger = logging.getLogger("core.engine.sync")
Expand Down Expand Up @@ -60,7 +61,7 @@ def _build_payload(
for c in cost_rows
]

engine_version = getattr(config, "CLI_VERSION", "v1.0.0").strip()
engine_version = f"v{__version__}"
now = int(time.time())

payload: dict[str, Any] = {
Expand Down
22 changes: 16 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
)
from utils.validate import validate_region, validate_config
from utils import codes
from utils.version import __version__

# Configure the root logger to ensure logs propagate from all modules
logging.basicConfig(
Expand Down Expand Up @@ -780,6 +781,13 @@ def parse_arguments():
formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"--version",
action="version",
version=f"cloudexit v{__version__}",
help="Show the CLI version and exit.",
)

subparsers = parser.add_subparsers(
dest="cloud_provider", help="Specify the cloud provider (aws or azure)."
)
Expand Down Expand Up @@ -859,19 +867,21 @@ def parse_arguments():


def main():
# Parse arguments first so --version/--help exit before any side effects
# (ASCII art, dataset download).
args = parse_arguments()

# Print ASCII art
console.print(ascii_art, style="bold cyan")

# Ensure latest dataset is available before proceeding
initialize_dataset()

args = parse_arguments()

# Check if the cloud provider is specified
# Nothing to do without a subcommand — show help before any dataset download.
if not args.cloud_provider:
print_help_message()
return

# Ensure latest dataset is available before proceeding
initialize_dataset()

# Dispatch based on provided arguments
try:
if args.cloud_provider == "aws":
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "cloudexit"
version = "1.0.0"
dynamic = ["version"]
description = "Open-source cloud exit assessment CLI for evaluating cloud lock-in risk."
readme = "README.md"
requires-python = ">=3.12"
Expand Down Expand Up @@ -33,6 +33,9 @@ dev = [
[project.scripts]
cloudexit = "main:main"

[tool.setuptools.dynamic]
version = {attr = "utils.version.__version__"}

[tool.setuptools]
py-modules = ["main", "config"]

Expand Down
4 changes: 2 additions & 2 deletions tests/test_utils_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_uses_host_from_environment_when_config_host_empty(self):
patch.dict(os.environ, {"HOST": "env.exitcloud.io"}, clear=False),
patch(
"core.utils_sync.config",
types.SimpleNamespace(HOST="", CLI_VERSION="v1"),
types.SimpleNamespace(HOST=""),
),
patch("core.utils_sync._build_payload", return_value={"sample": "payload"}),
patch(
Expand Down Expand Up @@ -47,7 +47,7 @@ def test_returns_clear_error_when_host_missing_everywhere(self):
patch.dict(os.environ, {}, clear=True),
patch(
"core.utils_sync.config",
types.SimpleNamespace(HOST="", CLI_VERSION="v1"),
types.SimpleNamespace(HOST=""),
),
):
result = post_assessment(
Expand Down
1 change: 1 addition & 0 deletions utils/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "1.0.0"