Skip to content

Integ-runner: Breaks the CWD and path when using Python language #638

@tomharvey

Description

@tomharvey

Describe the bug

After test file discovery integ-runner changes the working directory from the users's cwd (usually the project root) to the test file's parent directory and then runs the cdk command, which (when using Python) means that the cwd and sys.path are in an unexpected state.

This means that we cannot import Stacks or Constructs from directories in the project which are above or adjacent to the test files. Since this is the expected location of Stacks and Constructs - that makes integ-runner unusable in python.

Given a CDK project of file layout like the below:

my-cdk-app/
├─ my_cdk_app/
│  ├─ my_stack.py
│  ├─ lambda/
│  │ ├─ index.py
├─ test/
│  ├─ integ_.my_stack.py
├─ app.py
├─ cdk.json

While in app.py it's expected that we would from my_cdk_app.my_stack import MyStack, within inter_.my_stack.py we are unable to do so.

Since the working directory has changed before we spawn the cdk and python subprocesses the sys.path doesn't contain the root of the project and we get a ModuleNotFoundError

Secondly, if my_stack.py contains a relative path definition, like for a Lambda function Code.from_asset(f"{os.get_cwd()} /my_cdk_app/lambda"). Then the relative path would break as you're now working from a base path of /test instead of the root path / of the project. And, so the asset cannot be found at /test/my_cdk_app/lambda since it's at /my_cdk_app/lambda.

Expected Behavior

I expect the cdk command and the integ-runner commands to run in the same way - whereby the current working directory is the directory that I run the command from.

It's very common to expect that the cwd is the directory that the user initiates a script from. And the script is expected, by user, to be integ-runner, not integ_.my_stack.py.

Current Behavior

The integ-runner command runs the cdk command after changing directories. Therefore the cwd is inconsistent and unexpected and results in ModuleNotFoundErrors and missing assets for lambda, Dockerfiles, S3Deployments and other stacks which use a path to define the location of assets.

Reproduction Steps

Create a CDK project:

mkdir my_cdk_app
cd my_cdk_app
cdk init --language python

Add an integ-runner test:

mkdir test
touch test/integ_.my_cdk_app.py

And populate that test file with:

# test/integ_.my_cdk_app.py

import aws_cdk as cdk
from aws_cdk import (
    integ_tests_alpha,
)

from my_cdk_app.my_cdk_app_stack import MyCdkAppStack

app = cdk.App()
stack = MyCdkAppStack(app, "TestStack")

integration_test = integ_tests_alpha.IntegTest(app, "Integ", test_cases=[stack])

synth = app.synth()

Then, when you run integ-runner you will get the ModuleNotFoundError:

Verifying integration test snapshots...

Traceback (most recent call last):
  File "/workspaces/my_cdk_app/test/integ_.my_cdk_app.py", line 6, in <module>
    from my_cdk_app.my_cdk_app_stack import MyCdkAppStack
ModuleNotFoundError: No module named 'my_cdk_app'

While cdk synth will successfully import from my_cdk_app.my_cdk_app_stack import MyCdkAppStack and synthesis the stack.

Possible Solution

To fix the behaviour in a hacky way, we can edit the test file, adding this to the top:

import sys
sys.path.insert(0, project_root_path)

Where project_root_path is the absolute path within your developer machine to the project root - in my case "/workspaces/my_cdk_app".

After this, it will successfully run.

When I need to fix the cwd to make Code.from_asset work as expected I need to also add:

import os
os.chdir(project_root_path)

This is very hacky, and as the project might live in different locations between different developers, it's very brittle.

So, I made https://pypi.org/project/cdk-integ-runner-cwd-fix/ to provide a less brittle, but still hacky fix.

Using that I need to add to the top of each test file:

from cdk_integ_runner_cwd_fix import fix_cwd
fix_cwd()

And then run inter-runner while providing the project root path as an environment variable:

CDK_INTEG_RUNNER_CWD=$(pwd) integ-runner

Doing so, shows no (so far) problems with the operation of integ-runner, so it would be much preferred if integ-runner didn't introduce this unexpected change in working directory in the first place, and we didn't need to hack the path and CWD at all.

You could introduce a --cwd option to integ-runner which would incorporate a similar approach. Or, untangle all of the chdir that happens between test file discovery and test file being run.

Additional Information/Context

integ-runner --version
>>> 2.128.0-alpha.0

CDK CLI Version

2.122.0 (build 7e77e02)

Framework Version

No response

Node.js Version

v20.11.0

OS

debian bullseye

Language

Python

Language Version

Python 3.12.0

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions