Skip to content

Commit 1fdb2b4

Browse files
Merge pull request #9 from ahmed-n-abdeltwab/feature/make-modular
fix: permission issues and model training errors
2 parents c6cd594 + caa6625 commit 1fdb2b4

File tree

5 files changed

+40
-18
lines changed

5 files changed

+40
-18
lines changed

.github/workflows/train_and_release.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,19 @@ jobs:
3939
docker build -t $DOCKER_IMAGE .
4040
docker images
4141
42-
- name: Run training pipeline
42+
- name: Prepare release directory
4343
run: |
4444
mkdir -p ./$RELEASE_DIR/latest
45+
chmod -R 777 ./$RELEASE_DIR
46+
47+
- name: Run training pipeline
48+
run: |
4549
docker run --rm \
4650
-v $(pwd)/data:/app/data \
4751
-v $(pwd)/models:/app/models \
4852
-v $(pwd)/$RELEASE_DIR:/app/release \
53+
-e PYTHONPATH=/app \
54+
-u 1001 \
4955
$DOCKER_IMAGE
5056
5157
- name: Verify artifacts
@@ -57,10 +63,6 @@ jobs:
5763
docker logs $(docker ps -lq) || true
5864
exit 1
5965
fi
60-
if [ ! -f ./$RELEASE_DIR/latest/metadata.json ]; then
61-
echo "Error: metadata.json not found!"
62-
exit 1
63-
fi
6466
6567
- name: Create release package
6668
run: |

Dockerfile

+7-5
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ ENV PYTHONPATH=/app
2626
RUN mkdir -p /app && \
2727
mkdir -p /app/data && \
2828
mkdir -p /app/models && \
29-
mkdir -p /app/release && \
29+
mkdir -p /app/release/latest && \
3030
chmod -R 777 /app
3131

32-
# Create non-root user
33-
RUN useradd -m appuser && \
32+
# Create non-root user with specific UID
33+
RUN useradd -u 1001 -m appuser && \
3434
chown -R appuser:appuser /app
3535

3636
WORKDIR /app
@@ -41,8 +41,10 @@ COPY --from=builder --chown=appuser:appuser /opt/venv /opt/venv
4141
ENV PATH="/opt/venv/bin:$PATH"
4242

4343
# Copy application files
44-
COPY --chown=appuser:appuser src/ ./src/
45-
COPY --chown=appuser:appuser config/ ./config/
44+
COPY --chown=appuser:appuser . .
45+
46+
# Install package in development mode
47+
RUN pip install -e .
4648

4749
# Health check
4850
HEALTHCHECK --interval=30s --timeout=10s \

config/components/model_trainer.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ params:
99
hyperparams:
1010
random_forest:
1111
n_estimators: [100, 200]
12-
max_depth: [10, 20, 30] # Remove None from options
12+
max_depth: [10, 20] # Removed None
1313
min_samples_split: [2, 5]
1414
svm:
1515
C: [0.1, 1, 10]

src/components/artifact_exporter.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
class ArtifactExporter:
1111
def __init__(self, config: Dict[str, Any]):
1212
self.config = config
13-
# Ensure absolute path and proper directory structure
1413
self.config["output_dir"] = os.path.abspath(config.get("output_dir", "release"))
14+
15+
# Ensure directory exists with write permissions
1516
os.makedirs(os.path.join(self.config["output_dir"], "latest"), exist_ok=True)
17+
os.chmod(os.path.join(self.config["output_dir"], "latest"), 0o777)
1618

1719
def export_training_artifacts(
1820
self, training_results: Dict[str, Any]
@@ -24,6 +26,7 @@ def export_training_artifacts(
2426
# Clear existing contents
2527
shutil.rmtree(release_dir, ignore_errors=True)
2628
os.makedirs(release_dir, exist_ok=True)
29+
os.chmod(release_dir, 0o777)
2730

2831
exported_files = {}
2932
artifacts = training_results["artifacts"]
@@ -42,6 +45,7 @@ def export_training_artifacts(
4245
if src and os.path.exists(src):
4346
dest = os.path.join(release_dir, filename)
4447
shutil.copy2(src, dest)
48+
os.chmod(dest, 0o666) # Ensure writable
4549
exported_files[filename.split(".")[0]] = dest
4650
elif filename == "metrics.json":
4751
# Create default metrics if missing
@@ -71,8 +75,10 @@ def _create_default_metrics(
7175
"warning": "Metrics not properly saved during training",
7276
},
7377
)
74-
with open(os.path.join(release_dir, "metrics.json"), "w") as f:
78+
metrics_path = os.path.join(release_dir, "metrics.json")
79+
with open(metrics_path, "w") as f:
7580
json.dump(default_metrics, f, indent=2)
81+
os.chmod(metrics_path, 0o666)
7682

7783
def _create_feature_structure(
7884
self, release_dir: str, training_results: Dict[str, Any]
@@ -82,14 +88,18 @@ def _create_feature_structure(
8288
"required_features": len(training_results.get("selected_features", [])),
8389
"version": datetime.now().strftime("%Y%m%d_%H%M%S"),
8490
}
85-
with open(os.path.join(release_dir, "feature_structure.json"), "w") as f:
91+
features_path = os.path.join(release_dir, "feature_structure.json")
92+
with open(features_path, "w") as f:
8693
json.dump(feature_structure, f, indent=2)
94+
os.chmod(features_path, 0o666)
8795

8896
def _create_package_info(self, release_dir: str, files: Dict[str, str]):
8997
package_info = {
9098
"created_at": datetime.now().isoformat(),
9199
"contents": {k: os.path.basename(v) for k, v in files.items()},
92100
"notes": "Automatically generated by spyware-detector-training pipeline",
93101
}
94-
with open(os.path.join(release_dir, "package_info.json"), "w") as f:
102+
info_path = os.path.join(release_dir, "package_info.json")
103+
with open(info_path, "w") as f:
95104
json.dump(package_info, f, indent=2)
105+
os.chmod(info_path, 0o666)

src/main.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ def main():
2525
try:
2626
logger.info("🚀 Starting Spyware Detector Training Pipeline")
2727

28-
# Ensure release directory exists
28+
# Ensure release directory exists with proper permissions
2929
release_dir = os.path.abspath("release/latest")
3030
os.makedirs(release_dir, exist_ok=True)
31+
os.chmod(release_dir, 0o777)
3132
logger.info(f"Release directory: {release_dir}")
33+
logger.info(
34+
f"Directory permissions: {oct(os.stat(release_dir).st_mode & 0o777)}"
35+
)
3236

3337
# Initialize and run pipeline
3438
pipeline = TrainingPipeline("config/pipeline.yaml")
@@ -42,14 +46,18 @@ def main():
4246
shutil.make_archive("model_release", "zip", release_dir)
4347
logger.info("📦 Created release package: model_release.zip")
4448

45-
# Log exported files
46-
logger.info("💾 Artifacts generated:")
49+
# Verify all artifacts were created
50+
missing_files = []
4751
for name, path in results["exported_files"].items():
4852
if path and os.path.exists(path):
4953
logger.info(f" - {name}: {path}")
5054
else:
55+
missing_files.append(name)
5156
logger.warning(f" - {name}: FILE MISSING")
5257

58+
if missing_files:
59+
raise RuntimeError(f"Missing exported files: {', '.join(missing_files)}")
60+
5361
except Exception as e:
5462
logger.error(f"❌ Pipeline failed: {str(e)}", exc_info=True)
5563
sys.exit(1)

0 commit comments

Comments
 (0)