Compare commits
3 Commits
v2026.01.0
...
v2026.01.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0480f398ac | ||
|
|
7df625b65f | ||
|
|
c8ecda67bd |
54
CLAUDE.md
54
CLAUDE.md
@@ -270,3 +270,57 @@ Basil includes a complete CI/CD pipeline with Gitea Actions for automated testin
|
||||
- `DOCKER_USERNAME` - Docker Hub username
|
||||
- `DOCKER_PASSWORD` - Docker Hub access token
|
||||
- `DEPLOY_WEBHOOK_URL` - Webhook endpoint for deployments
|
||||
|
||||
## Version Management
|
||||
|
||||
**IMPORTANT**: Increment the version with **every production deployment**.
|
||||
|
||||
### Version Format
|
||||
Basil uses calendar versioning with the format: `YYYY.MM.PPP`
|
||||
- `YYYY` - Four-digit year (e.g., 2026)
|
||||
- `MM` - Two-digit month with zero-padding (e.g., 01 for January, 12 for December)
|
||||
- `PPP` - Three-digit patch number with zero-padding that increases with each deployment in a month
|
||||
|
||||
### Examples
|
||||
- `2026.01.001` - First deployment in January 2026
|
||||
- `2026.01.002` - Second deployment in January 2026
|
||||
- `2026.02.001` - First deployment in February 2026 (patch resets to 001)
|
||||
- `2026.02.003` - Third deployment in February 2026
|
||||
|
||||
### Version Update Process
|
||||
When deploying to production:
|
||||
|
||||
1. **Update version files:**
|
||||
```bash
|
||||
# Update both version files with new version
|
||||
# packages/api/src/version.ts
|
||||
# packages/web/src/version.ts
|
||||
export const APP_VERSION = '2026.01.002';
|
||||
```
|
||||
|
||||
2. **Commit the version bump:**
|
||||
```bash
|
||||
git add packages/api/src/version.ts packages/web/src/version.ts
|
||||
git commit -m "chore: bump version to 2026.01.002"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
3. **Create Git tag and release:**
|
||||
```bash
|
||||
# Tag should match version with 'v' prefix
|
||||
git tag v2026.01.002
|
||||
git push origin v2026.01.002
|
||||
|
||||
# Or use Gitea MCP to create tag and release
|
||||
```
|
||||
|
||||
4. **Document in release notes:**
|
||||
- Summarize changes since last version
|
||||
- List bug fixes, features, and breaking changes
|
||||
- Reference related pull requests or issues
|
||||
|
||||
### Version Display
|
||||
The current version is displayed in:
|
||||
- API: `GET /api/version` endpoint returns `{ version: '2026.01.002' }`
|
||||
- Web: Footer or about section shows current version
|
||||
- Both packages export `APP_VERSION` constant for internal use
|
||||
|
||||
@@ -390,7 +390,7 @@ describe('Recipes Routes - Real Integration Tests', () => {
|
||||
expect(prisma.recipe.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should delete old relations before update', async () => {
|
||||
it('should only delete relations that are being updated', async () => {
|
||||
(prisma.recipeSection.deleteMany as any).mockResolvedValue({});
|
||||
(prisma.ingredient.deleteMany as any).mockResolvedValue({});
|
||||
(prisma.instruction.deleteMany as any).mockResolvedValue({});
|
||||
@@ -406,22 +406,46 @@ describe('Recipes Routes - Real Integration Tests', () => {
|
||||
});
|
||||
(prisma.cookbook.findMany as any).mockResolvedValue([]);
|
||||
|
||||
// Test 1: Only updating title - should not delete any relations
|
||||
await request(app)
|
||||
.put('/api/recipes/1')
|
||||
.send({ title: 'Updated' });
|
||||
|
||||
expect(prisma.recipeSection.deleteMany).toHaveBeenCalledWith({
|
||||
where: { recipeId: '1' },
|
||||
expect(prisma.recipeSection.deleteMany).not.toHaveBeenCalled();
|
||||
expect(prisma.ingredient.deleteMany).not.toHaveBeenCalled();
|
||||
expect(prisma.instruction.deleteMany).not.toHaveBeenCalled();
|
||||
expect(prisma.recipeTag.deleteMany).not.toHaveBeenCalled();
|
||||
|
||||
// Reset mocks
|
||||
vi.clearAllMocks();
|
||||
(prisma.ingredient.deleteMany as any).mockResolvedValue({});
|
||||
(prisma.recipeTag.deleteMany as any).mockResolvedValue({});
|
||||
(prisma.recipe.update as any).mockResolvedValue({
|
||||
id: '1',
|
||||
title: 'Updated',
|
||||
ingredients: [],
|
||||
tags: [],
|
||||
});
|
||||
(prisma.cookbook.findMany as any).mockResolvedValue([]);
|
||||
|
||||
// Test 2: Updating tags and ingredients - should only delete those
|
||||
await request(app)
|
||||
.put('/api/recipes/1')
|
||||
.send({
|
||||
title: 'Updated',
|
||||
ingredients: [],
|
||||
tags: []
|
||||
});
|
||||
|
||||
expect(prisma.ingredient.deleteMany).toHaveBeenCalledWith({
|
||||
where: { recipeId: '1' },
|
||||
});
|
||||
expect(prisma.instruction.deleteMany).toHaveBeenCalledWith({
|
||||
where: { recipeId: '1' },
|
||||
});
|
||||
expect(prisma.recipeTag.deleteMany).toHaveBeenCalledWith({
|
||||
where: { recipeId: '1' },
|
||||
});
|
||||
// These should NOT be called since we didn't send them
|
||||
expect(prisma.recipeSection.deleteMany).not.toHaveBeenCalled();
|
||||
expect(prisma.instruction.deleteMany).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 500 on update error', async () => {
|
||||
|
||||
@@ -363,11 +363,19 @@ router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
const { sections, ingredients, instructions, tags, ...recipeData } = req.body;
|
||||
|
||||
// Delete existing relations
|
||||
await prisma.recipeSection.deleteMany({ where: { recipeId: req.params.id } });
|
||||
await prisma.ingredient.deleteMany({ where: { recipeId: req.params.id } });
|
||||
await prisma.instruction.deleteMany({ where: { recipeId: req.params.id } });
|
||||
await prisma.recipeTag.deleteMany({ where: { recipeId: req.params.id } });
|
||||
// Only delete relations that are being updated (not undefined)
|
||||
if (sections !== undefined) {
|
||||
await prisma.recipeSection.deleteMany({ where: { recipeId: req.params.id } });
|
||||
}
|
||||
if (ingredients !== undefined) {
|
||||
await prisma.ingredient.deleteMany({ where: { recipeId: req.params.id } });
|
||||
}
|
||||
if (instructions !== undefined) {
|
||||
await prisma.instruction.deleteMany({ where: { recipeId: req.params.id } });
|
||||
}
|
||||
if (tags !== undefined) {
|
||||
await prisma.recipeTag.deleteMany({ where: { recipeId: req.params.id } });
|
||||
}
|
||||
|
||||
// Helper to clean IDs from nested data
|
||||
const cleanIngredient = (ing: any, index: number) => ({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Application version following the pattern: YYYY.MM.PATCH
|
||||
* Example: 2026.01.1 (January 2026, patch 1)
|
||||
* Application version following the pattern: YYYY.MM.PPP
|
||||
* Example: 2026.01.002 (January 2026, patch 2), 2026.02.003 (February 2026, patch 3)
|
||||
* Month and patch are zero-padded. Patch increments with each deployment in a month.
|
||||
*/
|
||||
export const APP_VERSION = '2026.01.1';
|
||||
export const APP_VERSION = '2026.01.002';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Application version following the pattern: YYYY.MM.PATCH
|
||||
* Example: 2026.01.1 (January 2026, patch 1)
|
||||
* Application version following the pattern: YYYY.MM.PPP
|
||||
* Example: 2026.01.002 (January 2026, patch 2), 2026.02.003 (February 2026, patch 3)
|
||||
* Month and patch are zero-padded. Patch increments with each deployment in a month.
|
||||
*/
|
||||
export const APP_VERSION = '2026.01.1';
|
||||
export const APP_VERSION = '2026.01.002';
|
||||
|
||||
Reference in New Issue
Block a user