Skip to content

Commit 14f9cc2

Browse files
test cleanup to venvs
1 parent 65cca68 commit 14f9cc2

File tree

7 files changed

+129
-11
lines changed

7 files changed

+129
-11
lines changed

.github/workflows/nightly-seed-grouping.yml

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ jobs:
5151
# rust-sdk
5252
]
5353
include:
54-
- concurrency-count-override: 8
55-
sdk-name: python-sdk
56-
54+
- concurrency-count-override: 8
55+
sdk-name: python-sdk
56+
5757
steps:
5858
# Note: this step is meant for when running on ubuntu-latest
5959
- name: Remove Bloat
@@ -63,7 +63,7 @@ jobs:
6363
# this might remove tools that are actually needed,
6464
# if set to "true" but frees about 6 GB
6565
tool-cache: false
66-
66+
6767
# all of these default to true, but feel free to set to
6868
# "false" if necessary for your workflow
6969
android: true
@@ -99,14 +99,21 @@ jobs:
9999
- name: Save Seed Test Log to File
100100
id: save-seed-test-log-to-file
101101
run: |
102-
# Default to 16 concurrent fixtures if not overridden. In the case of python-sdk,
102+
# Default to 16 concurrent fixtures if not overridden. In the case of python-sdk,
103103
# the virtual environments take up too much space so this needs to be limited
104104
pnpm seed:local test --generator ${{ matrix.sdk-name }} --parallel ${{ matrix.concurrency-count-override || 16 }} --allow-unexpected-failures > ${{ matrix.sdk-name }}-seed-test-log.txt
105105
106-
# CHRISM - consideration
107-
# - name: Cleanup Virtualenvs
108-
# run: |
109-
# find . -name "fern-*" -type d | head -10 | xargs rm -rf
106+
- name: Cleanup Virtualenvs
107+
if: always() # Run even if tests fail
108+
run: |
109+
echo "Cleaning up Poetry virtualenvs to free disk space..."
110+
# Remove Poetry virtualenv cache
111+
rm -rf ~/.cache/pypoetry/virtualenvs/*
112+
# Also clean any local .venv directories
113+
find . -name ".venv" -type d -exec rm -rf {} + 2>/dev/null || true
114+
# Clean poetry cache
115+
poetry cache clear --all pypi 2>/dev/null || true
116+
echo "Virtualenv cleanup completed"
110117
111118
- name: Upload Seed Test Log as Artifact
112119
uses: actions/upload-artifact@v4
@@ -134,8 +141,7 @@ jobs:
134141
strategy:
135142
fail-fast: false
136143
matrix:
137-
sdk-name:
138-
[
144+
sdk-name: [
139145
# CHRISM - temp
140146
python-sdk
141147
# ruby-model,

generators/python/src/fern_python/cli/abstract_generator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ def generate_project(
140140
publisher.run_poetry_install()
141141
publisher.run_ruff_check_fix()
142142
publisher.run_ruff_format()
143+
publisher.cleanup_virtualenvs() # Clean up virtualenv after all operations
143144
elif output_mode_union.type == "publish":
144145
publisher.run_poetry_install()
145146
publisher.run_ruff_check_fix()
146147
publisher.run_ruff_format()
147148
publisher.publish_package(publish_config=output_mode_union)
149+
publisher.cleanup_virtualenvs() # Clean up virtualenv after all operations
148150

149151
self.postrun(
150152
generator_exec_wrapper=generator_exec_wrapper,
@@ -197,6 +199,7 @@ def _publish(
197199
generator_config=generator_config,
198200
)
199201
publisher.publish_package(publish_config=publish_config)
202+
publisher.cleanup_virtualenvs() # Clean up virtualenv after publishing
200203

201204
def _write_files_for_github_repo(
202205
self, project: Project, output_mode: GithubOutputMode, write_unit_tests: bool

generators/python/src/fern_python/cli/publisher.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,44 @@ def run_poetry_install(self) -> None:
5151
safe_command="poetry install",
5252
)
5353

54+
def cleanup_virtualenvs(self) -> None:
55+
"""Remove Poetry virtualenvs to free up disk space"""
56+
try:
57+
# Get virtualenv info to identify which one was created
58+
env_info_result = subprocess.run(
59+
["poetry", "env", "info", "--path"],
60+
stdout=subprocess.PIPE,
61+
stderr=subprocess.PIPE,
62+
cwd=self._generator_config.output.path,
63+
check=False, # Don't fail if no venv exists
64+
text=True
65+
)
66+
67+
if env_info_result.returncode == 0 and env_info_result.stdout.strip():
68+
venv_path = env_info_result.stdout.strip()
69+
print(f"Removing virtualenv at: {venv_path}")
70+
71+
# Remove the specific virtualenv
72+
self._run_command(
73+
command=["poetry", "env", "remove", "--all"],
74+
safe_command="poetry env remove --all",
75+
)
76+
print("Virtualenv cleanup completed successfully")
77+
else:
78+
print("No virtualenv found to clean up")
79+
80+
except Exception as e:
81+
# Don't fail the entire generation process if cleanup fails
82+
print(f"Warning: Virtualenv cleanup failed: {e}")
83+
self._generator_exec_wrapper.send_update(
84+
logging.GeneratorUpdate.factory.log(
85+
logging.LogUpdate(
86+
level=logging.LogLevel.DEBUG,
87+
message=f"Virtualenv cleanup warning: {e}"
88+
)
89+
)
90+
)
91+
5492
def publish_package(
5593
self,
5694
*,

packages/seed/src/commands/test/script-runner/DockerScriptRunner.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,47 @@ export class DockerScriptRunner extends ScriptRunner {
5151
}
5252
}
5353

54+
public async cleanup({ taskContext, id }: { taskContext: TaskContext; id: string }): Promise<void> {
55+
if (this.skipScripts) {
56+
return;
57+
}
58+
59+
const workDir = id.replace(":", "_");
60+
61+
for (const script of this.scripts) {
62+
taskContext.logger.debug(`Cleaning up fixture ${id} in container ${script.containerId}`);
63+
64+
// Clean up the fixture directory and any virtualenvs
65+
const cleanupCommands = [
66+
`rm -rf /${workDir}`,
67+
// Clean up Poetry virtualenvs created during this fixture's execution
68+
`find . -name '.venv*' -type d -exec rm -rf {} + 2>/dev/null || true`,
69+
// Clean up any virtualenvs in the Poetry cache for this specific project
70+
`rm -rf ./*-py* 2>/dev/null || true`,
71+
// Clean up Poetry cache
72+
`poetry cache clear --all pypi 2>/dev/null || true`
73+
];
74+
75+
const cleanupScript = cleanupCommands.join(" && ");
76+
77+
const cleanupCommand = await loggingExeca(
78+
taskContext.logger,
79+
"docker",
80+
["exec", script.containerId, "/bin/sh", "-c", cleanupScript],
81+
{
82+
doNotPipeOutput: false,
83+
reject: false // Don't fail if cleanup has issues
84+
}
85+
);
86+
87+
if (cleanupCommand.failed) {
88+
taskContext.logger.warn(`Cleanup warning for fixture ${id}: ${cleanupCommand.stderr}`);
89+
} else {
90+
taskContext.logger.debug(`Successfully cleaned up fixture ${id}`);
91+
}
92+
}
93+
}
94+
5495
protected async initialize(): Promise<void> {
5596
await this.startContainers(this.context);
5697
}

packages/seed/src/commands/test/script-runner/LocalScriptRunner.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,27 @@ export class LocalScriptRunner extends ScriptRunner {
4040
// No containers to stop for local execution
4141
}
4242

43+
public async cleanup({ taskContext, id }: { taskContext: TaskContext; id: string }): Promise<void> {
44+
if (this.skipScripts) {
45+
return;
46+
}
47+
48+
taskContext.logger.debug(`Cleaning up fixture ${id} (local execution)`);
49+
50+
// For local execution, clean up Poetry virtualenvs in the output directory
51+
// This is less critical since local runs use temporary directories that are cleaned up anyway
52+
// But we can still clean up Poetry caches to free space
53+
try {
54+
await loggingExeca(taskContext.logger, "poetry", ["cache", "clear", "--all", "pypi"], {
55+
doNotPipeOutput: false,
56+
reject: false
57+
});
58+
taskContext.logger.debug(`Successfully cleaned up Poetry cache for fixture ${id}`);
59+
} catch (error) {
60+
taskContext.logger.warn(`Cleanup warning for fixture ${id}: ${error}`);
61+
}
62+
}
63+
4364
protected async initialize(): Promise<void> {
4465
// No initialization needed for local execution
4566
}

packages/seed/src/commands/test/script-runner/ScriptRunner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export abstract class ScriptRunner {
3737

3838
public abstract run({ taskContext, id, outputDir }: ScriptRunner.RunArgs): Promise<ScriptRunner.RunResponse>;
3939
public abstract stop(): Promise<void>;
40+
public abstract cleanup({ taskContext, id }: { taskContext: TaskContext; id: string }): Promise<void>;
4041

4142
protected abstract initialize(): Promise<void>;
4243
}

packages/seed/src/commands/test/test-runner/TestRunner.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ export abstract class TestRunner {
263263
scriptStopwatch.stop();
264264
metrics.compileTime = scriptStopwatch.duration();
265265

266+
// Clean up virtualenvs after script execution (success or failure)
267+
try {
268+
await this.scriptRunner?.cleanup({ taskContext, id });
269+
} catch (cleanupError) {
270+
taskContext.logger.warn(`Cleanup failed for fixture ${id}: ${cleanupError}`);
271+
// Don't fail the test due to cleanup issues
272+
}
273+
266274
if (scriptResponse?.type === "failure") {
267275
return {
268276
type: "failure",

0 commit comments

Comments
 (0)