agentsclimarketplace

Frappe fixtures

Skill prilk-consulting/frappe-agent-kit/skills/frappe-fixtures

Frappe fixtures, workspaces, and sidebar setup. Use for exporting/syncing fixtures, Workspace + Workspace Sidebar + Desktop Icon configuration, nested-set fixture ordering, and per-environment ID pitfalls that break fresh-site migrations. Not for data-migration patches or patches.txt — use frappe-upgrade.From its SKILL.md

Install
npx -y skills add prilk-consulting/frappe-agent-kit --skill frappe-fixtures

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.4 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

Fixtures, Workspaces & Sidebars

Usage

Use this skill when:

  • Exporting or syncing fixtures (bench export-fixtures, hooks fixtures list)
  • Setting up a Workspace, Workspace Sidebar, or Desktop Icon for an app
  • Debugging fresh-site bench migrate crashes caused by fixture ordering or per-environment links
  • Deciding between fixtures and install.py for custom fields

Fixture basics

# hooks.py
fixtures = [
    {"dt": "Custom Field", "filters": [["module", "=", "My Module"]]},
    {"dt": "Property Setter", "filters": [["module", "=", "My Module"]]},
    {"dt": "Role", "filters": [["name", "in", ["My Role"]]]},
]
bench --site <site> export-fixtures --app myapp     # DB → apps/myapp/myapp/fixtures/*.json
bench --site <site> migrate                          # JSON → DB (runs sync_fixtures)
bench --site <site> execute frappe.utils.fixtures.sync_fixtures --args "['myapp']"  # sync only
  • Always filter — an unfiltered {"dt": "Custom Field"} exports every custom field on the site, including other apps'. Each app owns only its own records.
  • Use or_filters when records can't be matched by one field: {"dt": "Wiki Document", "or_filters": [...]}.
  • Fixture sync is upsert-only: deleting a record from the JSON does not delete it from sites that already have it. Removals need a patch.
  • Re-export after every UI-side change, and diff the export before committing — export-fixtures rewrites whole files and happily captures unrelated records that match your filter.
  • For content managed live in the UI by end users (website pages, wiki content), consider NOT shipping fixtures at all — a fixture sync on deploy will overwrite their edits. Fixtures fit configuration, not user-owned content.

Workspace + Sidebar + Desktop Icon

CRITICAL naming rule

  • Workspace Sidebar name MUST differ from the module name
  • Workspace Sidebar module field MUST equal the module name (this prevents auto-gen)
  • Example: module="Logistics" → sidebar name="Logistics Hub", sidebar module="Logistics"

If name matches module, Frappe's auto_generate_sidebar_from_module() creates a cached auto-generated sidebar (top 3 DocTypes only) that overrides yours.

# hooks.py
fixtures = [
    {"dt": "Workspace", "filters": [["name", "in", ["MyWorkspace"]]]},
    {"dt": "Workspace Sidebar", "filters": [["name", "in", ["My Sidebar"]]]},
    {"dt": "Desktop Icon", "filters": [["name", "in", ["My Sidebar"]]]},
]
// fixtures/workspace_sidebar.json (minimal)
[{
  "doctype": "Workspace Sidebar", "name": "My Sidebar", "module": "MyModule",
  "title": "My Sidebar", "header_icon": "bot-message-square", "standard": 1,
  "items": [
    {"label": "Home", "link_to": "MyWorkspace", "link_type": "Workspace", "type": "Link"},
    {"label": "My DocType", "link_to": "My DocType", "link_type": "DocType", "type": "Link", "icon": "list"}
  ]
}]
  • Desktop Icon link_to and sidebar reference the sidebar name.
  • Icon SVG: 118x118px, colored background + white icon, in public/images/.
  • Sidebar item icons: pick from frappe/public/icons/lucide/icons.svg.
  • After syncing: bench --site <site> clear-cache (sidebars are cached).

Nested-set fixture ordering gotcha

bench export-fixtures writes records sorted alphabetically by name. For nested-set doctypes (tree doctypes: Item Group, Customer Group, Account, Wiki Document, BOM trees, …) this can put children before their parents in the JSON — random hash names sort unpredictably.

On a fresh site, bench migrate hits the child row first, calls update_add_node, looks up the parent's lft/rgt — and the parent doesn't exist yet:

File ".../frappe/utils/nestedset.py", line 77, in update_add_node
    left, right = frappe.db.get_value(doctype, {"name": parent}, ["lft", "rgt"], for_update=True)
TypeError: cannot unpack non-iterable NoneType object

Fix: topologically re-sort the JSON after every export so each record appears only after its parent. Run the bundled script (in this skill's scripts/ directory):

python3 scripts/sort_nested_fixtures.py apps/<app>/<app>/fixtures/<doctype>.json <parent_fieldname>
# e.g. python3 scripts/sort_nested_fixtures.py apps/myapp/myapp/fixtures/item_group.json parent_item_group

It re-sorts by tree depth in place (idempotent; exit 2 on parent cycles). Run it as a post-export step after every bench export-fixtures of a tree doctype. Fixture order is per-file; fixture files import alphabetically by filename, so make sure parent-doctype fixtures sort before child-doctype fixtures if they're linked.

This gotcha never shows on the dev site where the records already exist — only on fresh installs / first bench migrate of a downstream bench. Test fixtures on a throwaway site before shipping.

Per-environment ID gotcha

Fixtures that contain links to records that are generated per-environment (revision IDs, version rows, file attachments, auto-named children that aren't themselves exported) will migrate fine on the source site and crash on every other site with LinkValidationError: Could not find <DocType>.

Before committing an export, null out or strip any field whose value is an autoname/hash that the fixture set itself does not include. Typical offenders: "current revision" pointers, "latest version" links, cached file URLs pointing at /private/files/.

# post-export scrub example
for d in docs:
    d["main_revision"] = None   # per-environment pointer; rebuilt on first save

Custom fields: fixtures vs install.py

Two valid approaches — pick ONE per app, don't mix:

ApproachProCon
install.py + create_custom_fields() in after_install/after_migrateCode-reviewed dicts, no export step, idempotentFields not visible in fixture diffs
fixtures = [{"dt": "Custom Field", "filters": [["module", "=", "X"]]}]UI-first workflowExport captures noise; needs module set on every field

The install.py approach is generally preferred for apps that extend other apps' DocTypes — see the frappe-dev skill.

What ships with it: 1 file

1.9 KB alongside SKILL.md, 1 of them executable

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.