diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..ad4ccd3 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,29 @@ +name: Build and test +on: + push: + branches: [ 'main' ] + + pull_request: + branches: [ 'main' ] + +# This allows a subsequently queued workflow run to interrupt and cancel previous runs +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + Run-tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: [ '3.8', '3.9', '3.10', '3.11' ] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '${{ matrix.python }}' + architecture: 'x64' + - run: pip install -U pip wheel poetry + - run: poetry install --with dev + - run: poetry run pytest tests diff --git a/airflowctl/cli.py b/airflowctl/cli.py index cba5395..6947586 100644 --- a/airflowctl/cli.py +++ b/airflowctl/cli.py @@ -74,8 +74,7 @@ def init( def build( project_path: Path = typer.Argument(Path.cwd(), help="Absolute path to the Airflow project directory."), settings_file: Path = typer.Option( - Path.cwd() / SETTINGS_FILENAME, - help="Path to the settings file.", + None, help="Path to the settings file. Defaults to PROJECT_DIR/settings.yaml.", show_default=False ), venv_path: Path = typer.Option( Path.cwd() / ".venv", @@ -93,9 +92,12 @@ def build( airflowctl_project_check(project_path) project_path = Path(project_path).absolute() + + if not settings_file: + settings_file = project_path / SETTINGS_FILENAME settings_file = Path(settings_file).absolute() - if not Path(project_path / SETTINGS_FILENAME).exists(): + if not settings_file.exists(): typer.echo(f"Settings file '{settings_file}' not found.") raise typer.Exit(1) @@ -412,7 +414,7 @@ def print_next_steps(venv_path: str | Path, version: str): next_steps = "Next Steps:" - activate_command = f"$ source {venv_path}/bin/activate" + activate_command = activate_virtualenv_cmd(venv_path) assert Path(venv_path).exists() diff --git a/airflowctl/utils/virtualenv.py b/airflowctl/utils/virtualenv.py index d99148d..49725b3 100644 --- a/airflowctl/utils/virtualenv.py +++ b/airflowctl/utils/virtualenv.py @@ -69,7 +69,7 @@ def verify_or_create_venv(venv_path: str | Path, recreate: bool, python_version: def activate_virtualenv_cmd(venv_path: str | Path) -> str: if os.name == "posix": bin_path = os.path.join(venv_path, "bin", "activate") - activate_cmd = f"source {bin_path}" + activate_cmd = f". {bin_path}" elif os.name == "nt": bin_path = os.path.join(venv_path, "Scripts", "activate") activate_cmd = f"call {bin_path}" diff --git a/poetry.lock b/poetry.lock index 3706fc1..5bde19c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -162,6 +162,17 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "markdown-it-py" version = "2.2.0" @@ -198,6 +209,35 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "psutil" version = "5.9.5" @@ -238,6 +278,29 @@ files = [ [package.extras] plugins = ["importlib-metadata"] +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "python-dotenv" version = "0.21.1" @@ -356,6 +419,17 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "typer" version = "0.9.0" @@ -409,4 +483,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "5489348a9dda8a0b909056a414dcc74be7d8049410a82b2e725e0f3ea2cef205" +content-hash = "92e8f317c8c31b7839e3572f107bd6d7aca39351250be6443512d01854fc221a" diff --git a/pyproject.toml b/pyproject.toml index 1cd59e2..6c975e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,9 @@ psutil = "^5.9.5" [tool.poetry.scripts] airflowctl = "airflowctl.cli:app" +[tool.poetry.group.dev.dependencies] +pytest = "^7.4.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..593e964 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,58 @@ +import tempfile + +import pytest +from typer.testing import CliRunner + +from airflowctl.cli import app + +runner = CliRunner() + + +@pytest.fixture(scope="function") +def temp_project_dir(): + with tempfile.TemporaryDirectory() as temp_dir: + yield temp_dir + + +def test_init_command(temp_project_dir): + result = runner.invoke( + app, + [ + "init", + temp_project_dir, + "--project-name", + "my_project", + "--airflow-version", + "2.6.3", + "--build-start", + "--background", + ], + ) + + assert result.exit_code == 0, result.output + assert "Airflow project built successfully." in result.output + + +def test_build_command(temp_project_dir): + result = runner.invoke( + app, + [ + "init", + temp_project_dir, + "--project-name", + "my_project", + ], + ) + + assert result.exit_code == 0, result.output + result_1 = runner.invoke( + app, + [ + "build", + temp_project_dir, + # "--recreate-venv" + ], + ) + + assert result_1.exit_code == 0, result_1.output + assert "Airflow project built successfully." in result_1.output