release: v0.4.8
Introduce the project versioning system (MAJOR.MINOR.PATCH, each 0-999) and tag the first release. - VERSION file as single source of truth (0.4.8) - __manifest__.py version -> 17.0.0.4.8 (odoo series + product version) - CHANGELOG.md with plain-language v0.4.8 release notes - scripts/bump-version.sh: bump (major/minor/patch/set) + tag from CHANGELOG - README.md: version header, 69-test status, changelog link - CLAUDE.md §15 Versioning & Releases; corrected Odoo 17 mail.template / blog.post.content compatibility table Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f5169c1a81
commit
b4d1e577df
6 changed files with 276 additions and 13 deletions
46
CHANGELOG.md
Normal file
46
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to **ITSulu Blog Publisher** are recorded here.
|
||||
|
||||
This project uses a three-part version number — `MAJOR.MINOR.PATCH` (each part 0–999):
|
||||
|
||||
- **MAJOR** — a major release for sale; significant feature upgrades or a significant change to the software.
|
||||
- **MINOR** — one or more features added, or a meaningful performance improvement.
|
||||
- **PATCH** — a single group of commits, or one large commit.
|
||||
|
||||
Release notes are written in plain language so anyone on the team can follow what changed.
|
||||
|
||||
---
|
||||
|
||||
## v0.4.8 — 2026-05-30
|
||||
|
||||
The first tagged release. This version gets the whole test suite running green on the
|
||||
ITSulu Kubernetes cluster and fixes two bugs that affected real blog posts and emails.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Notification emails now render correctly.** The email template was written in an old
|
||||
syntax that Odoo 17 no longer understands, so notification emails were going out with raw
|
||||
code (like `${object.blog_post_id.name}`) instead of the actual post title. The template
|
||||
was rebuilt using Odoo 17's current format, so subjects and bodies now fill in properly.
|
||||
- **Generated blog posts are no longer empty.** When a post was created automatically, the
|
||||
AI-written body was being thrown away before it reached the post. Every auto-generated post
|
||||
was published blank. The body is now saved to the post's content field.
|
||||
- Removed a duplicate field definition that produced a warning on every Odoo startup.
|
||||
|
||||
### Testing & Infrastructure
|
||||
|
||||
- The full automated test suite — **69 tests** (48 unit, 15 behaviour, 6 performance) — now
|
||||
passes end to end on the ITSulu Kubernetes test cluster.
|
||||
- The CI/CD pipeline was corrected: stages now run in the right order, database credentials
|
||||
are handled properly, and test reports are generated as build artifacts.
|
||||
- End-to-end tests now run as ephemeral jobs on the ITSulu cluster (`itsulu-testing`
|
||||
namespace) instead of an external preview service.
|
||||
- Brought the test code up to date with Odoo 17 (email rendering, blog post fields, and the
|
||||
behaviour-test environment setup).
|
||||
|
||||
### Documentation
|
||||
|
||||
- Recorded the Odoo 16 → 17 migration lessons (email templates, post body fields, template
|
||||
database refresh) in `CLAUDE.md` so they are not rediscovered the hard way.
|
||||
- Introduced this changelog and the project versioning scheme.
|
||||
96
CLAUDE.md
96
CLAUDE.md
|
|
@ -797,10 +797,13 @@ When extending core Odoo models (e.g., `blog.post`), be aware of field differenc
|
|||
|
||||
| Model | Field | Odoo 16 | Odoo 17 | Note |
|
||||
|-------|-------|---------|---------|------|
|
||||
| `blog.post` | `body_arch` | Yes | **No** | XML layout field; removed in Odoo 17 |
|
||||
| `blog.post` | `body` | Yes | **No** | HTML body field; use editor interface instead |
|
||||
| `blog.post` | `body_arch` | Yes | **No** | Removed in Odoo 17 |
|
||||
| `blog.post` | `body` | Yes | **No** | Removed in Odoo 17 |
|
||||
| `blog.post` | `content` | — | **Yes** | The HTML body field in Odoo 17 — **writable**; auto-generation MUST write `'content': llm_response.body_html` or posts publish empty |
|
||||
| `blog.post` | `itsulu_social_id` | N/A | Custom | Add via `_inherit` for reverse relationship to custom models |
|
||||
| `mail.template` | Subject/Body | Mako syntax | Mako syntax | Renders async in send_mail(); use Mako `% for` loops, not method calls in templates |
|
||||
| `mail.template` | subject / email_from | Mako `${}` | **inline_template `{{ }}`** | Mako `${}` is dead in Odoo 17 — renders literally |
|
||||
| `mail.template` | body_html | Mako `${}`/`% for` | **qweb `type="html"`** | Use `<t t-out="">`, `<t t-foreach t-as>`, `<t t-if>` as real XML children. NOT `type="qweb"` (invalid RNG type) |
|
||||
| `mail.template` | render method | `generate_email()` | **`_render_field(field, [ids])`** | Returns `{id: rendered_str}`; `_generate_template` returns UNrendered text |
|
||||
|
||||
**Pattern for extending blog.post safely:**
|
||||
```python
|
||||
|
|
@ -817,15 +820,24 @@ class BlogPost(models.Model):
|
|||
|
||||
**Pattern for creating blog.post test records:**
|
||||
```python
|
||||
# Use sudo() to bypass Odoo validation when creating minimal test posts
|
||||
# Use sudo() to bypass Odoo validation when creating minimal test posts.
|
||||
# The body field in Odoo 17 is `content` (NOT body / body_arch / body_html).
|
||||
post = self.env['blog.post'].sudo().create({
|
||||
'name': 'Test Post',
|
||||
'blog_id': blog.id,
|
||||
'is_published': True,
|
||||
# Do NOT include body, body_arch, or body_html — use website editing UI
|
||||
'content': '<p>HTML body goes here</p>', # Odoo 17 body field — writable
|
||||
})
|
||||
```
|
||||
|
||||
**Pattern for asserting on a rendered mail.template (synchronous):**
|
||||
```python
|
||||
template = self.env.ref('module.email_template_xmlid')
|
||||
rendered = template._render_field('subject', [record.id]) # {id: 'rendered text'}
|
||||
assert 'Expected' in rendered[record.id]
|
||||
# body_html: template._render_field('body_html', [record.id])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Failure Recovery Quick Reference
|
||||
|
|
@ -915,7 +927,79 @@ assert 800 <= log.tokens_used <= 1200
|
|||
|
||||
---
|
||||
|
||||
## 14. Phased Adoption Triggers
|
||||
## 15. Versioning & Releases
|
||||
|
||||
The project uses a three-part version number, **`MAJOR.MINOR.PATCH`**, each part `0–999`.
|
||||
|
||||
| Part | Bump when… | Examples |
|
||||
|---|---|---|
|
||||
| **MAJOR** | A major release for sale: significant feature upgrade, or a significant change to the software. | new product tier, rewrite, breaking redesign |
|
||||
| **MINOR** | One or more features added, or a meaningful performance improvement. | new LLM provider, new wizard, 2× faster generation |
|
||||
| **PATCH** | A single group of commits, or one large commit. | bug-fix batch, doc pass, refactor, dependency bump |
|
||||
|
||||
### Sources of truth & where the version lives
|
||||
|
||||
The repo-root **`VERSION`** file is the single source of truth. These are kept in sync:
|
||||
|
||||
| File | Form | Notes |
|
||||
|---|---|---|
|
||||
| `VERSION` | `0.4.8` | Source of truth — plain text, one line |
|
||||
| `addons/itsulu_blog_publisher/__manifest__.py` | `'version': '17.0.0.4.8'` | Odoo manifest = `<odoo_series>.<product_version>` = `17.0` + `0.4.8` |
|
||||
| `README.md` | `**Version:** 0.4.8` | Header line + status footer |
|
||||
| `CHANGELOG.md` | `## v0.4.8 — <date>` | Release notes section per version |
|
||||
| git tag | `v0.4.8` | Annotated tag; message = the CHANGELOG section |
|
||||
|
||||
**Never hand-edit the version in multiple files.** Use the helper script — it updates all of
|
||||
them from `VERSION` and derives the Odoo manifest version (`17.0.<MAJOR>.<MINOR>.<PATCH>`).
|
||||
|
||||
### Release process (every version change)
|
||||
|
||||
```bash
|
||||
# 1. Bump the version (updates VERSION, manifest, README)
|
||||
scripts/bump-version.sh patch # or: minor | major | set X.Y.Z
|
||||
|
||||
# 2. Write the release notes — add a "## vX.Y.Z — <date>" section to CHANGELOG.md
|
||||
# in plain, common language (what changed, why it matters), NOT git-speak.
|
||||
# Group under Fixed / Added / Changed / Testing & Infrastructure / Documentation.
|
||||
|
||||
# 3. Commit the bump + notes together
|
||||
git add -A && git commit -m "release: vX.Y.Z"
|
||||
|
||||
# 4. Tag the release — the tag message is taken from the CHANGELOG section
|
||||
scripts/bump-version.sh tag
|
||||
|
||||
# 5. Push branch and tag
|
||||
git push && git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
### Release-notes style (common language)
|
||||
|
||||
Write for a teammate, not a compiler. Each entry says **what changed and why it matters** in
|
||||
one or two sentences of plain English. Prefer:
|
||||
|
||||
> - **Notification emails now render correctly.** They were going out with raw code in the
|
||||
> subject instead of the post title; the template was rebuilt for Odoo 17.
|
||||
|
||||
over:
|
||||
|
||||
> - fix: migrate mail.template body_html ${} → qweb t-out
|
||||
|
||||
Keep the technical detail for commit messages and the CLAUDE.md failure table; the CHANGELOG is
|
||||
the human-readable record. Group entries under **Fixed / Added / Changed / Testing &
|
||||
Infrastructure / Documentation** as applicable.
|
||||
|
||||
### Rules
|
||||
|
||||
- One tag per version; tags are immutable. If a release is wrong, ship the next PATCH — never
|
||||
move a tag.
|
||||
- Tag on the release branch **after** the work is merged/finalised, so the tag points at the
|
||||
commit that actually ships.
|
||||
- The Odoo manifest version must always be `17.0.` + the `VERSION` value. The `-u` upgrade path
|
||||
relies on the manifest version increasing, so bump before deploying schema/data changes.
|
||||
|
||||
---
|
||||
|
||||
## 16. Phased Adoption Triggers
|
||||
|
||||
**Add Phase 2 (BDD, factories, coverage gates) when ≥ 2:**
|
||||
- Stakeholders ask "how do we know X works?"
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -1,7 +1,11 @@
|
|||
# ITSulu Blog Publisher — Odoo 17 Addon
|
||||
|
||||
**Version:** 0.4.8
|
||||
|
||||
Automated blog post generation and publishing for Odoo 17 Community Edition, powered by generative AI (Anthropic Claude, OpenAI, Google Gemini, or Ollama).
|
||||
|
||||
> **Versioning** — `MAJOR.MINOR.PATCH` (each 0–999). MAJOR = major release for sale / significant change; MINOR = features or performance improvements; PATCH = a single group of commits. See [CHANGELOG.md](CHANGELOG.md) for release notes and [CLAUDE.md](CLAUDE.md) §15 for the full scheme.
|
||||
|
||||
## Features
|
||||
|
||||
### ✨ Core Functionality
|
||||
|
|
@ -28,13 +32,14 @@ Automated blog post generation and publishing for Odoo 17 Community Edition, pow
|
|||
|
||||
### 📊 Test Coverage
|
||||
|
||||
**Phase 2 Complete**: 63 automated tests across 6 test files
|
||||
**69 automated tests**, all passing on the ITSulu Kubernetes test cluster.
|
||||
|
||||
| Test Suite | Count | Status |
|
||||
|---|---|---|
|
||||
| Unit Tests (TDD) | 48 | ✅ All Passing |
|
||||
| Behavior Tests (BDD) | 15 | ✅ All Passing |
|
||||
| **Total** | **63** | **100% ✅** |
|
||||
| Behaviour Tests (BDD) | 15 | ✅ All Passing |
|
||||
| Performance Benchmarks | 6 | ✅ All Passing |
|
||||
| **Total** | **69** | **100% ✅** |
|
||||
|
||||
**Coverage Areas**:
|
||||
- Topic queue management (7 tests)
|
||||
|
|
@ -42,7 +47,11 @@ Automated blog post generation and publishing for Odoo 17 Community Edition, pow
|
|||
- Social media copy (16 tests)
|
||||
- Schedule slots (10 tests)
|
||||
- LLM router dispatch (7 tests)
|
||||
- E2E workflows (15 BDD scenarios)
|
||||
- E2E / behaviour workflows (15 BDD scenarios)
|
||||
- Performance SLOs — latency, query count, token usage, concurrency (6 benchmarks)
|
||||
|
||||
Tests run as ephemeral Kubernetes jobs in the `itsulu-testing` namespace against a
|
||||
primed PostgreSQL template database. See [CLAUDE.md](CLAUDE.md) §8 for the K8s test setup.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -232,7 +241,8 @@ pre-commit run --all-files
|
|||
|
||||
## Documentation
|
||||
|
||||
- [CLAUDE.md](CLAUDE.md) — Complete TDD framework, testing patterns, troubleshooting
|
||||
- [CHANGELOG.md](CHANGELOG.md) — Release notes and version history
|
||||
- [CLAUDE.md](CLAUDE.md) — Complete TDD framework, testing patterns, versioning scheme, troubleshooting
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) — System design, data flow, API contracts
|
||||
- [PHASE2_ROADMAP.md](PHASE2_ROADMAP.md) — Phase 2 implementation status
|
||||
|
||||
|
|
@ -260,6 +270,6 @@ https://gitlab.com/itsulu-odoo/itsulu-blog-publisher/issues
|
|||
|
||||
---
|
||||
|
||||
**Current Status**: Phase 2 Complete ✅ (63/63 tests passing)
|
||||
**Current Status**: v0.4.8 — test suite green (69/69 passing on ITSulu K8s) ✅
|
||||
**Last Updated**: 2026-05-30
|
||||
**Maintainer**: Nicholas Riegel (nicholasr@itsulu.com)
|
||||
|
|
|
|||
1
VERSION
Normal file
1
VERSION
Normal file
|
|
@ -0,0 +1 @@
|
|||
0.4.8
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'ITSulu Blog Publisher',
|
||||
'version': '17.0.1.0.0',
|
||||
# Odoo manifest version = <odoo_series>.<product_version>. Product version
|
||||
# is tracked in the repo-root VERSION file (currently 0.4.8). See CLAUDE.md §15.
|
||||
'version': '17.0.0.4.8',
|
||||
'summary': 'AI-powered blog post generation with multi-LLM support, scheduling, and social media copy',
|
||||
'description': """
|
||||
ITSulu Blog Publisher
|
||||
|
|
|
|||
120
scripts/bump-version.sh
Executable file
120
scripts/bump-version.sh
Executable file
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# bump-version.sh — ITSulu Blog Publisher version manager
|
||||
#
|
||||
# Versioning scheme: MAJOR.MINOR.PATCH (each part 0–999)
|
||||
# MAJOR major release for sale; significant feature upgrade / significant change
|
||||
# MINOR feature(s) added, or a performance improvement
|
||||
# PATCH a single group of commits, or one large commit
|
||||
#
|
||||
# The repo-root VERSION file is the single source of truth. The Odoo manifest
|
||||
# version is derived as 17.0.<MAJOR>.<MINOR>.<PATCH> (Odoo-series prefix +
|
||||
# the product version), e.g. product 0.4.8 -> manifest 17.0.0.4.8.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/bump-version.sh patch # 0.4.8 -> 0.4.9
|
||||
# scripts/bump-version.sh minor # 0.4.8 -> 0.5.0
|
||||
# scripts/bump-version.sh major # 0.4.8 -> 1.0.0
|
||||
# scripts/bump-version.sh set 1.2.3 # set an explicit version
|
||||
# scripts/bump-version.sh tag # create annotated git tag v<VERSION>
|
||||
# # from the CHANGELOG section
|
||||
#
|
||||
# Typical flow for a release:
|
||||
# 1) scripts/bump-version.sh minor
|
||||
# 2) edit CHANGELOG.md — fill in the new "## vX.Y.Z" section (plain language)
|
||||
# 3) git add -A && git commit -m "release: vX.Y.Z"
|
||||
# 4) scripts/bump-version.sh tag # tags + uses the CHANGELOG notes
|
||||
# 5) git push && git push --tags
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
VERSION_FILE="$ROOT/VERSION"
|
||||
MANIFEST="$ROOT/addons/itsulu_blog_publisher/__manifest__.py"
|
||||
README="$ROOT/README.md"
|
||||
CHANGELOG="$ROOT/CHANGELOG.md"
|
||||
ODOO_SERIES="17.0"
|
||||
|
||||
die() { echo "error: $*" >&2; exit 1; }
|
||||
|
||||
read_version() {
|
||||
[ -f "$VERSION_FILE" ] || die "VERSION file not found at $VERSION_FILE"
|
||||
tr -d '[:space:]' < "$VERSION_FILE"
|
||||
}
|
||||
|
||||
validate() {
|
||||
local v="$1"
|
||||
[[ "$v" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] \
|
||||
|| die "invalid version '$v' — expected MAJOR.MINOR.PATCH, each part 0–999"
|
||||
for part in "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"; do
|
||||
[ "$part" -le 999 ] || die "version part '$part' exceeds 999"
|
||||
done
|
||||
}
|
||||
|
||||
write_everywhere() {
|
||||
local v="$1"
|
||||
echo "$v" > "$VERSION_FILE"
|
||||
# Odoo manifest: 17.0.<MAJOR>.<MINOR>.<PATCH> (series prefix + product version)
|
||||
sed -i -E "s/('version': ')[0-9.]+(')/\1${ODOO_SERIES}.${v}\2/" "$MANIFEST"
|
||||
# README version line: **Version:** X.Y.Z
|
||||
if grep -qE '^\*\*Version:\*\*' "$README"; then
|
||||
sed -i -E "s/^(\*\*Version:\*\*) .*/\1 ${v}/" "$README"
|
||||
fi
|
||||
echo " VERSION -> $v"
|
||||
echo " manifest -> ${ODOO_SERIES}.${v}"
|
||||
echo " README -> **Version:** ${v}"
|
||||
}
|
||||
|
||||
cmd="${1:-}"; arg="${2:-}"
|
||||
current="$(read_version)"
|
||||
|
||||
case "$cmd" in
|
||||
major|minor|patch)
|
||||
IFS='.' read -r MA MI PA <<< "$current"
|
||||
case "$cmd" in
|
||||
major) MA=$((MA+1)); MI=0; PA=0 ;;
|
||||
minor) MI=$((MI+1)); PA=0 ;;
|
||||
patch) PA=$((PA+1)) ;;
|
||||
esac
|
||||
new="${MA}.${MI}.${PA}"
|
||||
validate "$new"
|
||||
echo "Bumping ($cmd): $current -> $new"
|
||||
write_everywhere "$new"
|
||||
echo
|
||||
echo "Next: add a '## v${new}' section to CHANGELOG.md, commit, then: scripts/bump-version.sh tag"
|
||||
;;
|
||||
set)
|
||||
[ -n "$arg" ] || die "usage: bump-version.sh set <X.Y.Z>"
|
||||
validate "$arg"
|
||||
echo "Setting version: $current -> $arg"
|
||||
write_everywhere "$arg"
|
||||
echo
|
||||
echo "Next: add a '## v${arg}' section to CHANGELOG.md, commit, then: scripts/bump-version.sh tag"
|
||||
;;
|
||||
tag)
|
||||
v="$current"
|
||||
tagname="v${v}"
|
||||
git -C "$ROOT" rev-parse "$tagname" >/dev/null 2>&1 && die "tag $tagname already exists"
|
||||
# Extract this version's section from CHANGELOG (from "## vX.Y.Z" to the next "## v" or "---").
|
||||
notes="$(awk -v ver="## v${v}" '
|
||||
$0 ~ "^"ver" " || $0 == ver {flag=1}
|
||||
flag && /^---[[:space:]]*$/ {exit}
|
||||
flag && /^## v/ && $0 !~ ver {exit}
|
||||
flag {print}
|
||||
' "$CHANGELOG")"
|
||||
[ -n "$notes" ] || die "no CHANGELOG.md section found for ## v${v}"
|
||||
echo "Creating annotated tag $tagname with release notes:"
|
||||
echo "----------------------------------------"
|
||||
echo "$notes"
|
||||
echo "----------------------------------------"
|
||||
git -C "$ROOT" tag -a "$tagname" -m "$notes"
|
||||
echo "Tagged $tagname. Push with: git push origin $tagname"
|
||||
;;
|
||||
""|-h|--help|help)
|
||||
grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'
|
||||
echo "current version: $current"
|
||||
;;
|
||||
*)
|
||||
die "unknown command '$cmd' (try: major | minor | patch | set <v> | tag | help)"
|
||||
;;
|
||||
esac
|
||||
Loading…
Reference in a new issue