All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning
- the
copier-updateskill warns that a merge can be wrong without raising a conflict, and says how to check- a template change can fail to arrive, and a project's own configuration can be replaced by the template's - both silently, so "no conflicts" does not mean "nothing to review"
- plugin: the
plugin-implementationskill says to setplugin_idexplicitly, and both example plugins now do - the generated default embeds the module path and class name, so moving or renaming a module changes the identity of a deployed task and orphans the workflow tasks referencing it- a project adopting this for a task that is already deployed has to write down the identifier it generates today, verbatim; a tidier one breaks exactly what setting the field protects (#91)
- plugin: the
plugin-implementationskill says whatexecute()is handed, not only what a task declares - a workflow can never deliver more inputs than the declared ports, but a port left unconnected is absent from the sequence rather than empty- a task guarding against surplus inputs is guarding against a state the workflow editor cannot produce, and must not warn its users about one (#89)
- the
Stophook only reports a rule that really joined[tool.ruff.lint] ignore, and names it- it matched any quoted rule code anywhere in
pyproject.toml, soextend-select, a[tool.deptry]entry and theper-file-ignoresrelaxation the shipped rules prescribe all blocked a session
- it matched any quoted rule code anywhere in
- plugin: the
plugin-implementationskill separates port arity (FixedNumberOfInputs) from port schema (FixedSchemaPort,FlexibleSchemaPort,UnknownSchemaPort) - the warning about flexible input schemas named only the first, so it read as not applying toFixedNumberOfInputs([FlexibleSchemaPort()]) - plugin: the example workflow plugin declares its ports, which the skill beside it says not to leave implicit
- the
preparationYAML anchor of the generatedTaskfile.yamlis replaced by two internal tasks,prepareandpackage:exists- a merge key is silently dropped by any task that declares
depsof its own, which is howbuildcame to run withoutpoetry:install
- a merge key is silently dropped by any task that declares
- neither format task depends on
poetry:installany more (#81)poetry installrefuses to run against a stale lock file, which stopped thePostToolUsehook formatting anything for the rest of a session- on a clone with no virtualenv yet, run
task checkortask poetry:installbefore formatting
- the
Stophook runs.claude/hooks/template-feedback.pydirectly instead of throughtask, whose output would corrupt the hook's JSON channel on stdout (#81) - the shipped
.claude/rules/tells an agent not to cite an issue or Jira ticket in a project's changelog, since the people reading it cannot open them - plugin: the
plugin-implementationskill documents ports that depend on a parameter's value - the standard way to offer a parameter and a connected input as mutually exclusive alternatives, rather than accepting both and choosing at runtime (#78) - plugin: the
plugin-implementationskill prefers fixed schema ports, and names thearray assignment index out of range: 0abort that a flexible input schema can cause when the operator is fed by a file dataset (#79)
- the
template:feedback-checkandcheck:preparetasks of the generatedTaskfile.yaml- nothing calls either: the
Stophook runs its script directly, andpreparedoes whatcheck:preparedid - a
TaskfileCustom.yamlnamingcheck:prepareas a dependency has to be updated; the hand run of the hook isecho '{}' | python3 .claude/hooks/template-feedback.py
- nothing calls either: the
- the
Stophook of a generated project looks at the right evidence (#80)- it no longer blocks on the conflict-marker example inside its own
copier-updateskill, on a project that has not committed yet, or on a changelog entry that merely names# noqa - it now sees a suppression added in an untracked file, and ignores one that was already there
- git output is parsed in a pinned format, so
diff.noprefix,core.quotePathor an untracked directory can no longer hide evidence
- it no longer blocks on the conflict-marker example inside its own
task checkcan no longer pass without having looked at the package (#83)PACKAGEis a shell expansion, so an exportedpackage_diroutranks the.copier-answers.enventry - and an empty one dropped the package from every command line while the checks still reported successpackage:existsnow fails when the name does not resolve to a directory
builddepends onpoetry:installagain (#82)- plugin:
task installbuilds and then runspoetry run cmemc, which failed on a fresh clone because no virtualenv had been created
- plugin:
- an empty
[tool.pytest.ini_options]table is back in the generatedpyproject.toml, so apytest.ini,tox.iniorsetup.cfgabove the project can no longer take over itsrootdiror injectaddoptsinto its suite (#85) - github:
check.ymldeclares a least privilegepermissions:block, and its two reporting steps arecontinue-on-error, so a pull request from a fork no longer reds the whole check on a token it cannot be given (#84) - github:
xportation/junit-coverage-reportis pinned tov1.0.3rather than the mutable@main- the same commit today, so the step does not change (#84)
- plugin: the
plugin-testingskill now says how to name an environment variable that a test introduces for an external system:TESTING_<SERVICE>_<THING>, gated with the sameskipifpattern asneeds_cmem- the prefix is what separates, in a
.envfile or a group level list of CI variables, the entries that drive the test suite from those that configure the product - the eccenca Corporate Memory connection variables are named as the
exception:
CMEM_BASE_URIand theOAUTH_*trio keep their establishedcmemcnames, because the plugin andcmemcread them too
- the prefix is what separates, in a
- the
LICENSEof a generated project now names that project's own copyright holder, rendered from theauthor_nameanswer, instead of the hardcodedCopyright 2021 CMEMevery project inherited unchanged- the year is dropped rather than templated: copier cannot compute one
without the deprecated
jinja2-timeextension, and a fixed2021is wrong for every project generated since - existing projects see the line change on their next
copier update
- the year is dropped rather than templated: copier cannot compute one
without the deprecated
- plugin: the
plugin-documentationskill now states how the product is named in user-visible text: eccenca Corporate Memory on the first mention of each block, plain "Corporate Memory" afterwards, never "CMEM", identifiers exempt- the shipped prose follows it: the
README-public.mdsentence is now a single [eccenca Corporate Memory] link rather than two links splitting the name, and the sample skipif reason readsNeeds eccenca Corporate Memory configuration - projects that answer
pypicarry theREADME-public.mdwording onto their public package page, so this is visible on the next release
- the shipped prose follows it: the
- plugin: the logging example in the agent guidance now uses an f-string
- it showed the
loggingidiomself.log.info("...%s...", count), which raisesTypeError:self.logis aPluginLogger, whose methods take a single already formatted string, not a format string plus arguments
- it showed the
- plugin: agent guidance now names
get_client(context)as the way to reach a deployment, instead ofClient.from_context(context=context)- the two are the same call -
get_client()adds aUserContextguard and is reached throughcmem-plugin-base, which every plugin already depends on - the guidance says explicitly that existing
Client.from_context()calls are correct and must not be rewritten, since that direction is the one that drops the guard
- the two are the same call -
-
.gitignorenow says what a project does with.idea/, following github/gitignore- only the machine specific parts are ignored -
workspace.xml,tasks.xml,usage.statistics.xml,dictionaries,shelf, the data source files - plusmisc.xml, which names the local Python interpreter, and.idea/sonarlint/, whose binary index files neither that list nor PyCharm covers vcs.xml,modules.xml, the project.iml,inspectionProfiles/andrunConfigurations/stay shared, which is what PyCharm's own generated.idea/.gitignorealready assumes: they describe the project, not the machine- until 8.8.0 an unanchored
*.xmlpattern hid all of these; removing it left them untracked, so your first update may show a handful of new untracked.ideafiles - committing them once is the intended resolution, and it makes the project's inspection profile and run configurations available to everyone who opens it - nothing is untracked by this: ignoring never removes a file git already follows
- only the machine specific parts are ignored -
-
plugin: the
plugin-implementationskill says how a task constructor answersPLR0913andPLR0917- a constructor takes one argument per
PluginParameter, so its arity follows the task's configuration surface and neither of ruff's escapes is available: keyword-only arguments change the signature DataIntegration instantiates, and folding parameters into an object breaks the one-argument-per-parameter mapping PLR0917became stable in ruff 0.16, so a task with six or more parameters can start failingtask checkwithout anything in the task having changed - the projects that already carry# noqa: PLR0913there need the code widened to# noqa: PLR0913, PLR0917- it stays out of the
ignorelist inpyproject.tomlon purpose, since it is a real finding on an ordinary function
- a constructor takes one argument per
- the session end check no longer reports a
copier updatethrough its content signals either- 9.2.0 stopped the update tripping the changed-files signal, but the
# noqasignal read the same diff and matched the template's own prose about# noqa- so updating to 9.2.0 was reported by the very text that fixed the previous round of this - diff lines are now attributed to their file, and a suppression inside a template owned file is ignored: it was written by the template, so it can never be this project working around anything. One written by hand while resolving an update conflict still counts, since it lands in this project's own source
- the ruff ignore list signal is likewise off during an update, where a new entry arrived with the template rather than being chosen here
- conflict markers keep being reported everywhere, including in template owned files, since that is exactly the "the update could not be merged" case worth hearing about
- 9.2.0 stopped the update tripping the changed-files signal, but the
- the lint rules in
.claude/rules/now say what to do when a rule cannot be satisfied- the ban on
# noqa, on growing theignorelist and on loosening mypy left no permitted resolution for the rare rule that is wrong in context -S701demands HTML autoescaping, which corrupts a Jinja template rendering JSON, so obeying it writes the bug it exists to prevent - the permitted resolution, once a human has agreed, is a
# noqacarrying its reason - never a newignoreentry, which would silence the rule for unexamined code as well
- the ban on
- plugin: the
plugin-implementationskill says what a plugin icon has to look like, not only where to put it- no colour of its own (
currentColoron the rootsvg) so the mark follows the workspace theme, and a transparent background so it does not sit as a coloured tile among icons that are - nothing in
task checkor plugin discovery complains about either, so both were previously found only by looking at the running workspace
- no colour of its own (
- plugin: the
plugin-implementationskill warns that a union type annotation on a parameter breaks discovery- without an explicit
param_type, the type is derived from the annotation, and a union raisesTypeError: issubclass() arg 1 must be a classat import time - which removes every plugin in the package from the workspace whiletask checkstays green
- without an explicit
- plugin: the
plugin-implementationskill records that DataIntegration clears a dependent parameter when one of its dependencies changes- the clearing propagates along a chain, so a chained parameter never holds a value from an earlier selection - there is no stale combination to defend against, and such a parameter can safely be mandatory
- the session end check no longer reports the
copier updatethat installs it.claude/hooks/template-feedback.pycounted any changed template owned file as a finding, and an update changes all of them - so taking a new template version made the check accuse you of the act of taking it- when the working tree carries a changed
_commitin.copier-answers.yml, those paths are the update and no longer count; an added# noqa, a grown ruff ignore list and leftover conflict markers still do, since those are real findings even mid-update
- generated projects can report findings back to the template
- a
template-feedbackskill checks what the template has already decided, drafts a GitHub issue and files it on eccenca/cmem-plugin-template - always after showing you the exact text, never on its own - it never names your repository and never pastes code from it, since the template tracker is public while most generated projects are not: an issue carries the template version,
project_typeand whethergithub_page/pypiare answered gh issue createis deliberately not in the pre-approved commands, so filing always asks for permission; the read-onlygh issue listandgh search issuesare pre-approved- a new
Stophook,.claude/hooks/template-feedback.py, run through the newtask template:feedback-check, reminds the agent at the end of a session - but only when this working tree shows that something in the template got in the way: a template owned file was edited, a# noqaor# type: ignorewas added, a rule joined the ruff ignore list, or acopier updateleft conflicts behind- this hook can hold a session open for one extra turn when it finds such evidence, so that the agent can decide whether the finding is worth reporting. Answering "this is specific to this project" ends the session, and it asks at most once per session
- create an empty
.claude/no-template-feedbackfile to switch it off - that file belongs to your project, socopier updatewill not take it away
- a
- the template's own dependabot configuration, which 9.0.0 broke
- the jinja directory name it pointed at reads as a glob to dependabot, which rejected the whole file - so no dependency updates were opened at all, not even for the template's own CI
- the condition moved to
_excludeincopier.yml, so the generated workflows keep a literal path - nothing changes for generated projects: updating to 9.0.1 renders exactly the same files as 9.0.0
- generated projects ship a
.claude/directory with agent support for Claude Code.claude/rules/is read in every session: which files belong to the template, that lint findings are fixed instead of silenced, and that user-visible changes need a changelog entry.claude/settings.jsonallows the everyday commands (task check,task format:fix,task build,pytest,ruff,mypy) without a prompt, whiletask installandtask uninstallstill have to be confirmed- it also formats edited files by running
task format:fixafter each agent edit - remove thehooksblock from.claude/settings.jsonif you do not want that - a
copier-updateskill describes how to take a new template version, and areleaseskill how to cut a release - the latter only in github hosted projects, see below - plugin projects additionally receive the
plugin-documentation,plugin-testingandplugin-implementationskills, plus a rules file aboutcmem-plugin-base, theneeds_cmemmarker and the tasks that change a running deployment - the
plugin-implementationskill records the conventions the plugin fleet converged on: reaching a deployment withcmem-client(and thatcmem.cmempy.*is deprecated), logging throughself.log,Icon(package=__package__), explicit port declarations, honouring workflow cancellation, reporting progress from inside the entity loop, typing secrets asPassword, and writing custom parameter types - including thatautocompletion_depends_on_parametershands its values toautocomplete()positionally
- no
CLAUDE.mdis written into generated projects- that file belongs to your project, is never touched by
copier update, and is read alongside the shipped rules
- that file belongs to your project, is never touched by
- a project is now generated with the pipeline it actually uses, not with both
github_pagedecides the host: with a URL you get.github/workflows/and no.gitlab-ci.yml, left blank you get.gitlab-ci.ymland no.githubdirectorypypidecides the publish path: the githubpublish.yml, or the manualpypijob of the gitlab pipeline, are only generated when it is answered yes- check both answers before updating, because
copier updateremoves the files they no longer select - both questions used to be documented as badge and link decoration, so a stale answer is likely: their help texts now say what they generate
- if you are built on gitlab but keep a github page, clear
github_page- a mirror is not a build host, and the badges are not worth the pipeline - the
releaseskill follows the same answers: it is delivered for github hosted projects only, and describes the tag triggered publish only whenpypiis set
- the Corporate Memory badge is now served from the production documentation instead of
dev.documentation.eccenca.com- the badge itself is unchanged - the development host served the same JSON, but is not a deployment anybody promises to keep up
- github: refresh the Trivy DB cache in the generated check workflow
- the key was constant, so the database was saved once and never replaced
- it now rotates per run with a prefix fallback, as the template's own CI does
- copier >= v9 is now enforced by the template, not only documented
- an older copier fails with a clear message instead of an obscure error
- v9 is a support policy: it is the only major this template is tested against
- the empty
[tool.pytest.ini_options] addoptssetting, which configured nothing
- .gitignore no longer ignores every
*.xmland*.htmlfile in the project- they were meant for the reports in
dist/, which is already ignored anyway - a plugin shipping an XML resource or an HTML page had it silently untracked
- after
copier update, such files may newly appear as untracked
- they were meant for the reports in
- .gitignore no longer ignores
version.py, unused since poetry-dynamic-versioning
- github: update actions/cache to v6 in the generated check workflow
- v5 moved to the node24 runtime and needs an Actions Runner >= 2.327.1
- only relevant for projects building on self-hosted runners
- use cmem-plugin-base v4.20.0 (Corporate Memory 26.2)
- plugin: example test code now uses cmem-client instead of cmem-cmempy
- github: update actions in generated workflows
- checkout v7, setup-task v3, setup-python v7, action-junit-report v6
- ruff: asserts (S101) are now allowed in
tests/only- previously allowed everywhere;
assertin plugin code is now reported
- previously allowed everywhere;
- ruff: unused args (ARG) are now allowed in
tests/ - ruff: ignore CPY001 (missing copyright notice)
- update dependencies esp. trivy (0.73.0.1) and ruff (0.16.2)
- generated Taskfile: custom tasks file is
TaskfileCustom.yaml- the file header wrongly named it
TaskfileCustom.yml, which is silently ignored
- the file header wrongly named it
- generated Taskfile: typo in the file header
- plugin: typo in the
uninstalltask description - README: poetry requirement is
>= v2.1([tool.poetry.requires-plugins]needs poetry 2.x) - README: task list was outdated
- README: drop manual
poetry self addstep (required plugins are installed automatically) - gitlab: remove misleading comment on the
pypijob (it is manual, not tag-restricted) - trivyignore: drop stale CVE-2022-39280 suppression from the safety era, explain the file instead
- gitlab: use python image v3.13.13
- trivy: use 0.71.2.1
- poetry: use explicit plugin requirements instead of Taskfile checks
- ruff UP043 issue in example plugin code
- export requirements.txt on build
- gitlab: use python image v3.13.12
- use cmem-plugin-base v4.16.1 (Corporate Memory 26.1)
- ruff target-version now
py313 - gitignore: .claude/settings.local.json
- update trivy-py-ecc to v0.69
- update dependencies, no vulnerabilities
- all: update dev-dependencies
- git repository init check (is now able to work inside submodule)
- trivy scan now includes dependencies
- github, gitlab: disable vex notification
- github, gitlab: disable progress
- github, gitlab: trivydb cached between runs
- replace trivy-py with trivy-py-ecc ^0.67.2
- all: switch to python 3.13.8
- all: update dev-dependencies
- use trivy as vulnerability scanner in exchange for safety
- pre-commit: use python 3.13
- upgrade base dependency to 4.12.1
- all: update dev-dependencies
- lower cmemc dependency restriction in order to avoid dependency deadlock
- gitlab pipeline - pytest job: export all paths in
distas artefacts
- deptry check step
- all: update dev-dependencies
- cmem-plugin-base -> 4.10.2 (Corporate Memory 25.1.x)
- update cmemc dev-dependency to v25
- plugin: example test code now uses integrated Context classes
- all: make poetry:install task non-internal (to allow calls from custom tasks)
- all: update dev-dependencies
- plugin: tailored for Corporate Memory v24.3.x
- Generalization of the template
- You can create now projects of the following types:
- eccenca Corporate Memory plugins (same as before)
- Generic Python Projects (this is new)
- The first template question will ask you for the project type.
- Most features depend on this project type and will adapt to the decision.
- You can create now projects of the following types:
- more shields
- limitations of the 6.x template version regarding project name
- dependency updates (ruff 0.5, pytest 8, pytest-cov 5)
- cmem-plugin-base -> 4.7.0 (Corporate Memory 24.2.x)
- github: update to actions/setup-python@v5
- github: update to arduino/setup-task@v2
- ignore dev dependency security issue 70612 for jinja2
- update ruff
- exampe test
- pytest-html report generation
- coverage report does some advanced exclusion:
- update dependencies, esp. ruff
- ignore pips 67599 safety issue
- check:ruff now creates always a junit XML file
- ignore FIX002 - allow to add TODO notes in the code
- ignore FBT (boolean trap)
- pre-commit hook switched to ruff as well
- integrate ruff (removing bandit, flake8, black and pylint)
- use plugin base 4.3.0 (cmem-cmempy >= 23.3)
- use poetry-dynamic-versioning option bump=true
- 0.0.1.devX.. instead of 0.0.0.postX
- race condition in deploy task (#19)
- missing check for poetry versioning plugin on build task
- avoid safety issue 62044 for pip less than 23.3
- pylint/pillow dependencies to avoid errors
- pylint/pillow dependencies to avoid errors
- gitlab CI: artifact path for pytet
- forward mikepenz/action-junit-report to v4
- github pipeline: use
concurrencyto avoid integration test issues
- update checkout action to v4
- extend documentation
- clean up local build plan / task documentation
- use cmem-plugin-base 4.1.0 which is used by Corporate Memory 23.2
- .python-version to the project root in order to control pyenv
- see https://realpython.com/intro-to-pyenv/ for a tutorial
- Windows / MinGW compatibility
- used github actions
- build plan
- Unneeded safety ignores
- github pipeline: remove cache config (poetry not found)
- README: pypi links
- switch to (and enforce) python 3.11
- switch to cmem-plugin-base 4.x (which is the base for Corporate Memory 23.2)
- check for correct poetry-dynamic-versioning plugin
- check for valid pyproject.toml (poetry check)
- forward to cmem-plugin-base 3.1.0 (23.1 release)
- forward dev dependencies
- upgrade dependencies incl. cmem-plugin-base to 3.0.0
- This includes backwards incompatible changes.
- Migration Notes: https://github.com/eccenca/cmem-plugin-base/blob/main/CHANGELOG.md
- update github actions checkout, cache and and setup-python (deprecated)
- update github actions checkout, cache and and setup-python (deprecated)
- change development dependencies to group notation.
- updates of black, mypy and coverage
- gitlab ci yml migrated to gitlab >15 compatibility
- github_page question (to add icons and homepage links)
- pypi question (to add icons and links)
- made for badge in README
- add github token for task checkout step in the github workflow to avoid quota errors
- dependabot github action on daily basis
- mypy and flake8 execution for tests code
- github actions to latest versions
- pytest memray memory profiler plugin
- enable
.envfile usage - Taskfile with
cleanandchecktasks to test the template - github build plan to test the template
- upgrade pytest-cov to 4.x
- upgrade mypy to 0.982
- unneeded
poetry:inittask
- github: remove unneeded secrets
- github: use secrets envs only in pylint step
- github: publish workflow now only executed on tags
- package versions are now generated with the poetry dynamic versioning plugin
- github: publication of tagged versions to pypi.org (if
PYPI_TOKENis set) - gitlab: publication of tagged versions to pypi.org (if
PYPI_TOKENis set)
- gitlab: split check phase into separate jobs
- github: split check phase into separate jobs
- gitlab: manual
publish:pypijob which usesPYPI_TOKEN
- gitlab build plan: forward base image to v3.9.12-1
- public README
- .gitattributes to reclassify *.py.jinja files as python
- action trigger on main branch
- set CMEM_BASE_URI and OAUTH_CLIENT_SECRET from github secrets
- needs_cmem annotation to run test only of cmem environment is available
- Dummy contexts in utils that can be used in tests
- lifetime transform plugin
- option to extend tasks with
TaskfileCustom.yml
- use plugin base ^2.1.0
- execution test now gives empty context
- use plugin base ^2
- change interface of execute
- all copier after task (they often break the execution)
- github action to run
task check
- remove useless option values not needed by latest pylint
- ignore safety for librdf dependency
- migration to copier 6
- initial version for copier 5