Added new deploy task to fix deployments duplicating the index.js file

This commit is contained in:
Richard Osborne
2026-01-06 17:34:05 +01:00
parent 3bf411d081
commit beff9f0886

View File

@@ -0,0 +1,235 @@
# DEPLOY-000: Clean Up Old Hashed Files on Deploy
## Overview
Fix the deployment process to remove old hashed JavaScript files before creating new ones. Currently, each deployment generates a new `index-{hash}.js` file for cache-busting but never removes previous versions, causing the deployment folder to accumulate duplicate files and grow indefinitely.
## Context
### The Problem
When deploying to a local folder, the build process generates content-hashed filenames for cache-busting:
```typescript
// From deploy-index.ts
if (enableHash) {
const hash = createHash();
hash.update(content, 'utf8');
const hex = hash.digest('hex');
filename = addSuffix(url, '-' + hex); // Creates index-abc123def.js
}
```
This is good practice—browsers fetch fresh code when the hash changes. However, **there's no cleanup step** to remove the previous hashed files. After 10 deployments, you have 10 `index-*.js` files. After 100 deployments, you have 100.
### Impact
- **Bloated project size**: Each index.js is ~500KB+ depending on project complexity
- **Confusing output folder**: Multiple index files make it unclear which is current
- **Upload waste**: Deploying to hosting platforms uploads unnecessary files
- **Git noise**: If committing builds, each deploy adds a new file
### Existing Infrastructure
**`deploy-index.ts`** - Handles file hashing and writing:
```typescript
// packages/noodl-editor/src/editor/src/utils/compilation/build/deploy-index.ts
export async function copyDeployFilesToFolder({
project, direntry, files, exportJson, baseUrl, envVariables, runtimeType
}: CopyDeployFilesToFolderArgs)
```
**`cleanup.ts`** - Existing cleanup utility (only handles subfolders):
```typescript
// packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts
export async function clearFolders({ projectPath, outputPath, files }: ClearFoldersOptions)
```
**`deployer.ts`** - Main deployment orchestration:
```typescript
// packages/noodl-editor/src/editor/src/utils/compilation/build/deployer.ts
export async function deployToFolder({ project, direntry, environment, baseUrl, envVariables, runtimeType })
```
## Requirements
### Functional Requirements
1. **Remove old hashed files** before writing new ones during deployment
2. **Pattern matching** for `index-{hash}.js` files (hash is hex string from xxhash64)
3. **Preserve non-hashed files** like `index.html`, static assets, `noodl_bundles/`, etc.
4. **Work for all deploy types**: local folder, and as foundation for cloud deploys
### Non-Functional Requirements
- No user-facing changes (silent fix)
- Minimal performance impact (single directory scan)
- Backward compatible with existing projects
## Technical Approach
### Option A: Extend `cleanup.ts` (Recommended)
Add a new function to the existing cleanup module:
```typescript
// packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts
export interface CleanupHashedFilesOptions {
/** The output directory path */
outputPath: string;
/** File patterns to clean (regex) */
patterns?: RegExp[];
}
const DEFAULT_HASHED_PATTERNS = [
/^index-[a-f0-9]+\.js$/, // index-abc123.js
/^index-[a-f0-9]+\.js\.map$/, // source maps if we add them later
];
export async function cleanupHashedFiles({
outputPath,
patterns = DEFAULT_HASHED_PATTERNS
}: CleanupHashedFilesOptions): Promise<string[]> {
const removedFiles: string[] = [];
if (!filesystem.exists(outputPath)) {
return removedFiles;
}
const files = await filesystem.listDirectory(outputPath);
for (const file of files) {
// Skip directories
if (file.isDirectory) continue;
// Check against patterns
const shouldRemove = patterns.some(pattern => pattern.test(file.name));
if (shouldRemove) {
const filePath = filesystem.join(outputPath, file.name);
await filesystem.remove(filePath);
removedFiles.push(file.name);
}
}
return removedFiles;
}
```
### Option B: Inline in `copyDeployFilesToFolder`
Add cleanup directly in `deploy-index.ts` before writing files:
```typescript
export async function copyDeployFilesToFolder({
project, direntry, files, exportJson, baseUrl, envVariables, runtimeType
}: CopyDeployFilesToFolderArgs) {
// NEW: Clean up old hashed files first
await cleanupOldHashedFiles(direntry);
// ... existing logic
}
async function cleanupOldHashedFiles(direntry: string) {
if (!filesystem.exists(direntry)) return;
const files = await filesystem.listDirectory(direntry);
const hashedPattern = /^index-[a-f0-9]+\.js$/;
for (const file of files) {
if (!file.isDirectory && hashedPattern.test(file.name)) {
await filesystem.remove(filesystem.join(direntry, file.name));
}
}
}
```
### Recommendation
**Option A** is preferred because:
- Follows existing pattern of `cleanup.ts` module
- More extensible for future hashed assets (CSS, source maps)
- Can be reused by cloud deploy providers
- Easier to test in isolation
## Implementation Checklist
### Phase 1: Core Fix
- [ ] **Add `cleanupHashedFiles` function to `cleanup.ts`**
- Accept output path and optional patterns array
- Default patterns for `index-*.js` files
- Return list of removed files (for logging/debugging)
- Handle non-existent directories gracefully
- [ ] **Integrate into deployment flow**
- Import in `deploy-index.ts`
- Call before `writeIndexFiles()` in `copyDeployFilesToFolder()`
- Only run when `enableHash` is true (matches current behavior)
- [ ] **Add logging** (optional but helpful)
- Debug log removed files count
- Integrate with existing deployment progress indicators
### Phase 2: Testing
- [ ] **Manual testing**
- Deploy project to local folder
- Verify single `index-{hash}.js` exists
- Deploy again with code changes
- Verify old hash file removed, new one exists
- Deploy without changes, verify same hash retained
- [ ] **Edge cases**
- Empty output directory (first deploy)
- Output directory doesn't exist yet
- Read-only files (if possible on target OS)
- Very long hash patterns (shouldn't occur with xxhash64)
### Phase 3: Future Considerations (Out of Scope)
- [ ] Clean up other hashed assets when added (CSS, fonts)
- [ ] Manifest file tracking deployed assets
- [ ] Atomic deploys (write to temp, swap folders)
## Files to Modify
| File | Changes |
|------|---------|
| `packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts` | Add `cleanupHashedFiles()` function |
| `packages/noodl-editor/src/editor/src/utils/compilation/build/deploy-index.ts` | Import and call cleanup before writing |
## Estimated Effort
| Task | Hours |
|------|-------|
| Implement `cleanupHashedFiles` | 0.5 |
| Integrate into deploy flow | 0.5 |
| Testing & edge cases | 1.0 |
| **Total** | **2 hours** |
## Dependencies
- None (uses existing `@noodl/platform` filesystem APIs)
## Risks & Mitigations
| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| Accidentally delete non-hashed files | Low | High | Strict regex pattern, only match expected format |
| Performance on large folders | Very Low | Low | Single directory scan, typically <100 files |
| Break existing deploys | Very Low | Medium | Pattern only matches hash format, preserves all other files |
## Success Criteria
1. After multiple deployments to the same folder, only ONE `index-{hash}.js` file exists
2. The correct (latest) hash file is retained
3. All other deployment files remain intact
4. No user-visible changes to deployment flow
## Related Tasks
- **DEPLOY-001**: One-Click Deploy (will benefit from clean output)
- **DEPLOY-002**: Preview Deployments (needs clean folders per preview)
- **DEPLOY-003**: Deploy Settings (may add "clean build" toggle)