Assembly
Assembly is a reposityry that contains all third-party odoo addons needed for certain server, that is populated with addons from various sources in semi-automatic way.
The main purpose of assembly is to simplify deployment process to production servers.
Assembly contains odood-assembly.yml file (that is also referenced as assembly-spec),
that describes list of addons and list of git sources to populate this assembly with.
All third-party addons will be placed into dist directory inside assembly during sync operation.
After spec is created/updated there is need to run sync operation as next step.
The sync operation updates assembly with latest versions of addons according to the spec definition.
So, after sync is completed, and changes pushed to assembly git repo,
the servers that use this assembly could be updated in single step, by calling command
odood assembly upgrade that will do all the job.
Assembly Spec
Assembly spec is described in odood-assembly.yml file in root directory of assembly.
Assembly spec is YAML file that looks like:
spec:
addons-list:
- name: generic_m2o
- name: generic_mixin
- name: generic_tag
sources-list:
- url: https://github.com/crnd-inc/generic-addons
ref: '18.0'
Following shortcuts available for sources, to make spec more readable:
spec:
addons-list:
- name: generic_mixin
- generic_tag # Addons could be specified by name only.
sources-list:
- github: crnd-inc/generic-addons # converted to https://github.com/crnd-inc/generic-addons
- oca: web # converted to https://github.com/OCA/web
Additionally, it is allowed to download addons from Odoo Apps. For example, following spec, will download all addons from Odoo Apps. No git sources provided.
spec:
addons-list:
- name: generic_m2o
odoo_apps: true
- name: generic_mixin
odoo_apps: true
- name: generic_tag
odoo_apps: true
Assembly workflow
Typical assembly workflow could be splitted on two parts:
- Assembly maintenance
- Server operations
The first one includes such operations like:
- Initialization of new assembly
- Management of assembly spec, that describes what addons and from what sources have to be included in assembly.
- Assembly synchronization - just pull latest versions of addons defined in spec, and update the assembly repo.
The second one, includes operations to be performed on server side. These operations includes:
- Configure server to use assembly
- Upgrade server
Assembly maintenance
To create assembly, we have to have some Odood instance (may be local development instance), that will be used to maintain assembly. So, let’s assume that we have some Odood instance for Odoo 18, and we need to configure it to use assembly. We can initialize assembly as follows:
odood assembly init
This way, odood will create empty assembly for that project.
The generated assembly config (odood-assembly.yml) could look like:
spec:
addons-list: []
sources-list: []
If we want to add new module my_addon from github.com/my/repo to assembly, then we have to do add following changes in spec (odood-assembly.yml):
- Add name of addon in
addons-listsection ofspec - Add information about source to fetch this addons from in
sources-listsection ofspec
So, in result, our spec could look like:
spec:
addons-list:
- name: my_addon
sources-list:
- url: https://github.com/my/repo
ref: 18.0
As next step, we have to sync the assembly, to make Odood pull latest versions of selected modules from specified sources. We can do it using following command:
odood assembly sync
After this command, specified addons will be located (updated) in dist folder inside assembly, and ready to commit.
Also, we have to manually add odood-assembly.yml to git index before commit, to make sure spec is committed too.
So, next we have to push assembly to some git repo and then we could use it on servers.
Server operations
Initialize server with assembly
At first, on the server we have to configure it in the way to use already existing assembly (from git repo).
To do this, we have to call command odood assembly init specifying git repository to initialize assembly from.
For example:
odood assembly init --repo <url of git repo with assembly>
As next step, it is good to link assembly, to ensure all addons from assembly is available on the server. To do this, we can use following command
odood assembly link --ual
So, after this steps the server is configured to use assembly.
Update of server assembly
When server is configured to use assembly, then server management becomes pretty simple - all server updates could be done via single command:
odood assembly upgrade [--backup]
That will do all the job: 0. Optionally backup all databases
- pull assembly changes,
- relink modules,
- update addons on all databases.
Docker Compose deployments
When running Odoo in Docker Compose, the assembly is baked into the Docker image at build time rather than cloned on the server. Updates therefore follow a different workflow: build a new image, stop the running container, run addon updates, restart with the new image.
See Docker Compose — Upgrading assembly-based deployments for the full workflow including backup, recovery steps, and the recommended Compose service layout.
Assembly management
There is group of commands designed to deal with assemblies: odood assembly.
Run odood assembly --help to get more info about available commands.
This group contains following commands:
odood assembly init- allows to initalize assembly (new assembly or clone existing assembly from git)odood assembly use- allows to configure server to use assembly from specified path. Useful in CI flows.odood assembly status- show current status of assemblyodood assembly sync- this command synchronizes assembly to actual state. This operation includes following steps done automatically:- Clone or update (pull) all git sources listed in spec
- Remove all addons in
distfolder of assembly - Copy latest versions of addons to
distfolder - Add copied addons to git index of assembly repo
- Optionally commit chages to assembly git repo
- Optionally generate changelog for assembly
odood assembly link- completely relink this assembly (remove all links to assembly fromcustom_addons, and create new links). This is needed to ensure that only actual assembly addons linked.odood assembly pull- pull changes for assembly repo. Useful during server updateodood assembly upgrade- simple way to upgrade server that is configured to use assembly.odood addons update --assembly- this option could be used forodood addons install/update/uninstallcommands to install/update/uninstall addons contained in assembly.
Also, command odood addons find-installed could be used to generate spec for assembly based on third-party addons installed in specified database(s).
This is useful to migrate already existing Odood project to use assembly instead of multiple repositories.
Private git sources
Assemblies can clone private git repostitories via access tokens.
For each source in spec, it is possible to specify name or access-group,
that could be used to check environment variables for access credentials to clone specified sources.
For example, if following source defined:
spec:
addons-list:
- name: my_addon
sources-list:
- github: my/private-repo
ref: 18.0
access-group: my_repos
Odood will check environment variable ODOOD_ASSEMBLY_my_repos_CRED for access credentials for this repo.
The format for this variable is: user:token
Note, that in case of GitHub Actions, you have to provide access token for private repo in GitHub Actions Secrets. Thus, additionally in CI workflow definition, you have to assign secret to correct environment variable (see docs).
Changelogs and versions
Assemblies support automatic generation of changelogs and update of repository version.
The odood assembly sync command has option --changelog, that enables automatic generation of changelogs for assembly repo.
When this option passed, then Odood will generate and maintain automatically following files in root directory of repo:
VERSION- this file will contain assembly version in format<odoo major>.<odoo minor>.<major>.<minor>.<patch>.CHANGELOG.md- full changelog, that will be updated on each sync automatically.CHANGELOG.latest.md- changelog of lates update.
Note, that recommended flow for update process is to create separate branch for each update, and apply each update with merge request.
Version
Example of VERSION file content:
18.0.1.2.3
This file is updated automatically on each sync (if --changelog option used).
Following rules are applied to generate new repo version:
- Odoo serie (
<odoo major>.<odoo minor>) will be set to project’s Odoo version - If new addon added to assembly, major version part will be increased
- If some addons were deleted, then major version part will be increased
- If some of assembly addons changed major part, then major version part of assembly will be increased.
- If some of assembly addons changed minor part, then minor version part will be increased
- All othe cases will increase patch part of assembly version.
CHANGELOG.md
The changelog file contains information about each update of assembly, that includes:
- Update version
- Update date
- Addons added (name and version of each addon)
- Addons removed (name and version of each addon)
- Addons updated (name, old_version, new_version for each addon)
- Notable changes (if updated addon has list of notable changes for specific versions)
Sample changelog
Below is example of changelog generated by Odood during assembly sync operation:
# Changelog
## Release 18.0.2.0.0 (2025-Dec-23 19:18:33)
### Added addons
- `my_new_addon` (18.0.0.1.0)
### Removed addons
- `my_old_addon` (18.0.0.0.3)
### Updated addons
- `some_addon` (18.0.1.2.1 -> 18.0.1.3.0)
### Notable changes
#### Addon `some_addon`
##### Version 1.3.0
- Some new useful feature added. Now users should be happy.
## Release 18.0.1.0.1 (2025-Dec-20 12:11:38)
### Updated addons
- `some_addon` (18.0.1.2.0 -> 18.0.1.2.1)
Notable changes
The idea for this section is to provide list of changes that could be interesting for end users. For example, it could contain information about some feature implemented or some breaking changes.
This feature expectes that addon developers provide information about notable changes of addon in following way:
- addon must contain directory
changelogthat will store changelogs for this addon - for each version of addon that has notable changes, file
changelog/changelog.X.Y.Z.mdhave to be added (hereX- major version of module,Y- minor version of module,Z- patch version of module; Odoo serie is not taken into account). This file should contain description of notable changes in MarkDown format. - note, there is limitation for only
h6headers inchangelog/changelog.X.Y.Z.mdfiles, becouse all headers larger thanh6will be used in final assembly changelog.
For example (in context of example above (Sample changelog)), we have to add changelog/changelog.1.3.0.md file inside root directory of module some_addon with following content:
- Some new useful feature added. Now users should be happy.
CHANGELOG.latest.md
This file in same format as CHANGELOG.md, but contains only info from last update.
Sample CI configuration to build/update assemblies automatically
Usually assembiles require CI to update modules automatically or semi-automatically.
Build assembly on GitHub CI
Sample GitHub Actions workflow configuration, that will build assembly automatically:
name: Sync assembly
on:
push:
branches:
- '18.0-*'
workflow_dispatch:
jobs:
sync-assembly:
name: Sync assembly
if: "!contains(github.event.head_commit.message, '[SYNC] Assembly synced')"
runs-on: ubuntu-latest
strategy:
fail-fast: true
permissions:
contents: write
pull-requests: write
container:
image: ghcr.io/katyukha/odood/odoo/18.0:0.5.4
steps:
- uses: actions/checkout@v4
- name: Add current directory as safe directory for git
run: git config --global --add safe.directory "$(pwd)"
- name: Sync assembly
run: |
odood --config-from-env -v -d assembly -p . sync \
--changelog \
--dockerfile \
--commit \
--commit-user='Github Action' \
--commit-email='github-action@odood.dev' \
--push
The --dockerfile flag instructs Odood to generate (or regenerate) a Dockerfile in the assembly
repository root on every sync. This Dockerfile copies the synced dist/ directory into the image
and runs odood addons link — it is what enables building a Docker image from the assembly.
This workflow runs on all branches matching 18.0-*. Usual flow:
- Create a new branch
18.0-update - Wait for the job to complete
- Create a pull request
- Review and merge the pull request
- Delete the
18.0-updatebranch (or configure automatic stale-branch deletion)
Semi-automatic update cycle (recommended)
The workflow above commits directly to the current branch. For a more controlled flow that requires human review before merging, use a separate workflow that creates a PR automatically:
name: Init assembly sync
on: workflow_dispatch
jobs:
init-assembly-sync:
name: Init assembly sync
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
container:
image: ghcr.io/katyukha/odood/odoo/18.0:latest
env:
# Credentials for private repos (if needed):
# ODOOD_ASSEMBLY_myrepo_CRED: "x-access-token:${{ secrets.MY_REPO_PAT }}"
steps:
- uses: actions/checkout@v4
- name: Add current directory as safe directory for git
run: git config --global --add safe.directory "$(pwd)"
- name: Use current repo as assembly
run: odood -v -d --config-from-env assembly use .
- name: Sync assembly
run: |
odood -v -d --config-from-env assembly sync \
--changelog \
--commit \
--commit-user='Github Action' \
--commit-email='github-action@odood.dev' \
--push-to=18.0-assembly-update \
--fail-nothing-to-commit
create-pull-request:
name: Create assembly sync PR
runs-on: ubuntu-latest
needs: init-assembly-sync
permissions:
contents: read
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- id: check-pr-exists
run: |
prs=$(gh pr list \
--repo "$GITHUB_REPOSITORY" \
--head '18.0-assembly-update' \
--base '18.0' \
--json title \
--jq 'length')
if ((prs > 0)); then
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
fi
- if: '!steps.check-pr-exists.outputs.pr_exists'
run: |
gh label create auto-update \
--repo "$GITHUB_REPOSITORY" \
--description "Automatic update" \
--force
gh pr create \
--repo "$GITHUB_REPOSITORY" \
--draft \
--title="Automatic assembly update" \
--body="Automatic assembly update" \
-l auto-update \
-B 18.0 -H 18.0-assembly-update
Key flags used in the sync step:
--push-to=18.0-assembly-update— pushes to a dedicated update branch instead of the current one.--fail-nothing-to-commit— exits non-zero if no addons changed, preventing a no-op PR.assembly use .— registers the current directory as the assembly path, needed when the assembly repository and the Odood project share the same repo.
Triggering this workflow creates a draft PR from 18.0-assembly-update into 18.0 (if one does
not already exist). Pushing to 18.0-assembly-update also triggers the Sync assembly workflow
above (it matches 18.0-*), which re-runs with --dockerfile to regenerate the Dockerfile.
Releasing a Docker image
Once the update PR is merged to the stable branch, trigger this workflow manually to tag the release and build a multi-architecture Docker image published to GHCR:
name: Do Release
on: workflow_dispatch
jobs:
set-tag:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Read version
id: version
run: echo "version=v$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Create version tag
uses: actions/github-script@v7
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/${{ steps.version.outputs.version }}',
sha: context.sha
}).catch(err => {
if (err.status !== 422) throw err;
github.rest.git.updateRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/${{ steps.version.outputs.version }}',
sha: context.sha
});
})
build-and-push-docker-image:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
ODOO_SERIE: '18.0'
permissions:
contents: write
packages: write
attestations: write
runs-on: ubuntu-latest
needs: set-tag
steps:
- uses: actions/checkout@v4
- name: Read version
id: version
run: echo "version=v$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=match,pattern=v(.*),group=1,value=${{ steps.version.outputs.version }}
type=match,pattern=v(\d+\.\d+)\.(.*),group=1,value=${{ steps.version.outputs.version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
The metadata-action step derives two image tags from the assembly VERSION file
(e.g. 18.0.1.2.3):
- Full version (
18.0.1.2.3) — pinned; use this indocker-compose.ymlfor reproducibility and rollback. - Minor version (
18.0.1.2) — floating; always points to the latest patch of that minor version.
Multi-arch builds (linux/amd64,linux/arm64) require QEMU and Docker Buildx.
Remove the platforms line if you only need amd64.
Full release cycle
1. Trigger "Init assembly sync" (workflow_dispatch)
→ syncs addons, commits, pushes to 18.0-assembly-update, opens draft PR
→ "Sync assembly" fires automatically on the new branch, regenerates Dockerfile
2. Review and merge the PR to 18.0
3. Trigger "Do Release" (workflow_dispatch)
→ reads VERSION file, creates git tag, builds and pushes Docker image with version tags
4. Deploy using the upgrade workflow:
→ see Upgrading assembly-based deployments in Docker Compose docs
Private repo notes
In case when private repo have to be added to assembly, following additional steps have to be applied:
- Specify credentials for private repo (usually system user + access token) in GitHub Actions Secrets
- On Sync assembly step, assign variable
ODOOD_ASSEMBLY_accessgroup_CREDvalue in formatuser:passwordthat is fetched from action’s secrets, whereaccessgroupis value specified inaccess-groupornameproperty of corresponding git source
All the rest will be handled by Odood.
For example, in case when private git source is hosted on github, the Sync assembly step may look like:
- name: Sync assembly
env:
ODOOD_ASSEMBLY_myrepo_CRED: "x-access-token:${{ secrets.GH_MY_REPO_PAT }}
run: |
odood --config-from-env -v -d assembly -p . sync \
--changelog \
--commit \
--commit-user='Github Action' \
--commit-email='github-action@odood.dev' \
--push
It is expected, that assembly contains git-source named myrepo or that has access-group equal to myrepo.
Also, it is expected that access-token for this git repo added to GitHub Actions Secrets under name GH_MY_REPO_PAT
Build assembly on GitLab CI
Sample GitLab CI configuration, that will build assembly automatically:
build_assembly_on_commit:
image: ghcr.io/katyukha/odood/odoo/18.0:0.5.4
before_script:
# Add current directory as safe, thus allowing git operations in this dir.
- git config --global --add safe.directory "$(pwd)"
script:
# Create temporary branch to allow push work from Odood
- git checkout -b 18.0-tmp-assembly
# Do assembly sync
- odood --config-from-env assembly -p . sync --commit --commit-user="GitLab Bot" --commit-email="gitlab-bot@odood.dev" --push --push-to "$CI_COMMIT_BRANCH"
except:
variables:
# Do not run package on commits created by packager itself
- $CI_COMMIT_MESSAGE =~ /\[SYNC\] Assembly synced/
refs:
# Do not run job for stable branches
- "18.0"
only:
refs:
- branches
Note, that it is required to allow gitlab-ci-token to push changes back to project. This have to be configured in repository settings (CI/CD Settings -> Job token permissions)
Usually, it is required to create new branch to run build job.