[{"data":1,"prerenderedAt":949},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fversion-and-changelog-qgis-plugin":3},{"id":4,"title":5,"body":6,"description":938,"extension":939,"meta":940,"navigation":366,"path":945,"seo":946,"stem":947,"__hash__":948},"docs\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fversion-and-changelog-qgis-plugin\u002Findex.md","Version and Changelog a QGIS Plugin",{"type":7,"value":8,"toc":925},"minimark",[9,13,17,26,160,165,192,196,249,287,291,306,322,328,332,385,393,396,484,488,491,549,561,643,647,650,660,674,680,688,692,757,764,768,816,820,829,833,839,845,858,867,886,892,896,921],[10,11,5],"h1",{"id":12},"version-and-changelog-a-qgis-plugin",[14,15,16],"p",{},"Once a plugin has users, its version number stops being a label and becomes a promise. QGIS compares it to decide whether to offer an update; users read it to decide whether to take one; and your support inbox depends on whether the changelog tells them what changed. None of that requires ceremony — a version, a compatibility range and five lines of changelog per release cover it.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002F","Publishing to the QGIS Plugin Repository",". It covers how QGIS compares versions, choosing minimum and maximum QGIS versions honestly, the experimental flag, writing a changelog that answers the only question users have, and keeping the tag, the metadata and the archive in agreement.",[14,27,28],{},[29,30,35,39,43,50,59,69,75,81,85,89,98,102,107,112,115,118,121,124,127,130,133,138,142,145,148,151,154,157],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 268","img","Diagram of what a version number communicates, showing the major, minor and patch parts against the kind of change each signals and what a user should expect when updating","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"What each part of the number promises",[40,41,42],"desc",{},"The major number changes when something users depend on is removed or behaves differently, so an update may require action. The minor number changes when features are added compatibly, so an update is safe and brings something new. The patch number changes for fixes only, so an update is safe and brings nothing to learn. Users read the number to decide how carefully to read the changelog.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","268","#f6f3ea",[51,52,58],"text",{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","The number tells the user how carefully to read the rest",[44,60],{"x":61,"y":62,"width":63,"height":64,"rx":65,"fill":66,"stroke":67,"style":68},"16","52","232","180","10","#fdf2e2","#b91c1c","stroke-width:2.5",[51,70,74],{"x":71,"y":72,"style":73,"fill":67,"textAnchor":57},"132","78","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","2.0.0 — major",[51,76,80],{"x":71,"y":77,"style":78,"fill":79,"textAnchor":57},"106","text-anchor:middle;font-size:11px;font-family:sans-serif","#2f3b35","an algorithm id changed",[51,82,84],{"x":71,"y":83,"style":78,"fill":79,"textAnchor":57},"128","a setting was removed",[51,86,88],{"x":71,"y":87,"style":78,"fill":79,"textAnchor":57},"150","output format differs",[44,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":67,"style":97},"36","172","192","44","6","#fffdf7","stroke-width:1.5",[51,99,101],{"x":71,"y":93,"style":100,"fill":67,"textAnchor":57},"text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","read before updating",[51,103,106],{"x":71,"y":104,"style":105,"fill":79,"textAnchor":57},"208","text-anchor:middle;font-size:10px;font-family:sans-serif","saved models may break",[44,108],{"x":109,"y":62,"width":63,"height":64,"rx":65,"fill":110,"stroke":111,"style":68},"264","#eff3ff","#2563eb",[51,113,114],{"x":53,"y":72,"style":73,"fill":111,"textAnchor":57},"1.4.0 — minor",[51,116,117],{"x":53,"y":77,"style":78,"fill":79,"textAnchor":57},"a new algorithm",[51,119,120],{"x":53,"y":83,"style":78,"fill":79,"textAnchor":57},"an optional parameter",[51,122,123],{"x":53,"y":87,"style":78,"fill":79,"textAnchor":57},"a new translation",[44,125],{"x":126,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":111,"style":97},"284",[51,128,129],{"x":53,"y":93,"style":100,"fill":111,"textAnchor":57},"safe to update",[51,131,132],{"x":53,"y":104,"style":105,"fill":79,"textAnchor":57},"something new to try",[44,134],{"x":135,"y":62,"width":63,"height":64,"rx":65,"fill":136,"stroke":137,"style":68},"512","#edf8e9","#15803d",[51,139,141],{"x":140,"y":72,"style":73,"fill":137,"textAnchor":57},"628","1.3.2 — patch",[51,143,144],{"x":140,"y":77,"style":78,"fill":79,"textAnchor":57},"a crash fixed",[51,146,147],{"x":140,"y":83,"style":78,"fill":79,"textAnchor":57},"a wrong total corrected",[51,149,150],{"x":140,"y":87,"style":78,"fill":79,"textAnchor":57},"a typo in a label",[44,152],{"x":153,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":137,"style":97},"532",[51,155,156],{"x":140,"y":93,"style":100,"fill":137,"textAnchor":57},"just update",[51,158,159],{"x":140,"y":104,"style":105,"fill":79,"textAnchor":57},"nothing to learn",[161,162,164],"h2",{"id":163},"prerequisites","Prerequisites",[166,167,168,176,189],"ul",{},[169,170,171,175],"li",{},[172,173,174],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer for testing.",[169,177,178,179,183,184,188],{},"A plugin with a ",[180,181,182],"code",{},"metadata.txt"," — see ",[21,185,187],{"href":186},"\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fwrite-metadata-txt-qgis-plugin\u002F","Write metadata.txt for a QGIS Plugin",".",[169,190,191],{},"A repository with tags, so releases can be identified after the fact.",[161,193,195],{"id":194},"set-the-version-and-compatibility-range","Set the version and compatibility range",[197,198,203],"pre",{"className":199,"code":200,"language":201,"meta":202,"style":202},"language-ini shiki shiki-themes github-dark","[general]\nname=Parcel Tools\nversion=1.4.0\nqgisMinimumVersion=3.22\nqgisMaximumVersion=3.99\nexperimental=False\ndeprecated=False\n","ini","",[180,204,205,213,219,225,231,237,243],{"__ignoreMap":202},[206,207,210],"span",{"class":208,"line":209},"line",1,[206,211,212],{},"[general]\n",[206,214,216],{"class":208,"line":215},2,[206,217,218],{},"name=Parcel Tools\n",[206,220,222],{"class":208,"line":221},3,[206,223,224],{},"version=1.4.0\n",[206,226,228],{"class":208,"line":227},4,[206,229,230],{},"qgisMinimumVersion=3.22\n",[206,232,234],{"class":208,"line":233},5,[206,235,236],{},"qgisMaximumVersion=3.99\n",[206,238,240],{"class":208,"line":239},6,[206,241,242],{},"experimental=False\n",[206,244,246],{"class":208,"line":245},7,[206,247,248],{},"deprecated=False\n",[14,250,251,254,255,258,259,262,263,266,267,270,271,274,275,278,279,282,283,286],{},[172,252,253],{},"Breakdown:"," ",[180,256,257],{},"version"," is compared by the plugin manager to decide whether an update exists, and it compares numerically part by part — so ",[180,260,261],{},"1.10.0"," is correctly newer than ",[180,264,265],{},"1.9.0",", but ",[180,268,269],{},"1.4"," and ",[180,272,273],{},"1.4.0"," are treated as different strings in some tooling, which is a good reason to always use three parts. ",[180,276,277],{},"qgisMinimumVersion"," should be the oldest release you have actually tested on, not the oldest you hope works; users on that release will install it and report what happens. ",[180,280,281],{},"qgisMaximumVersion"," at ",[180,284,285],{},"3.99"," means \"any 3.x\", which is the right default — setting it to the current release means every QGIS upgrade hides your plugin from its own users, and that generates more support mail than any bug.",[161,288,290],{"id":289},"use-the-experimental-channel-deliberately","Use the experimental channel deliberately",[197,292,294],{"className":199,"code":293,"language":201,"meta":202,"style":202},"version=2.0.0-rc1\nexperimental=True\n",[180,295,296,301],{"__ignoreMap":202},[206,297,298],{"class":208,"line":209},[206,299,300],{},"version=2.0.0-rc1\n",[206,302,303],{"class":208,"line":215},[206,304,305],{},"experimental=True\n",[14,307,308,310,311,314,315,317,318,321],{},[172,309,253],{}," An experimental release is only offered to users who have ticked \"Show also experimental plugins\", which makes it the right vehicle for a rewrite, a risky change or a version you want tested before it reaches everybody. The repository keeps the latest stable and the latest experimental separately, so shipping ",[180,312,313],{},"2.0.0-rc1"," as experimental does not disturb anybody running ",[180,316,273],{},". Two rules make it work: never leave ",[180,319,320],{},"experimental=True"," on a release you intend as stable, and never use the experimental channel as a substitute for testing — a broken experimental release still reaches real users, just fewer of them.",[14,323,324,327],{},[180,325,326],{},"deprecated=True"," is the other end of the same mechanism: it marks the plugin as no longer maintained, warns users, and keeps it installable for anybody who still depends on it. Setting it with a final release whose changelog names a replacement is the courteous way to retire something.",[161,329,331],{"id":330},"write-a-changelog-users-read","Write a changelog users read",[197,333,335],{"className":199,"code":334,"language":201,"meta":202,"style":202},"changelog=\n    1.4.0 - 2026-08-15\n    - Added the Split by Ward algorithm to the Processing provider\n    - Field picker now remembers the last field per layer\n    - Fixed area totals being reported in the wrong units for geographic layers\n\n    1.3.2 - 2026-06-02\n    - Fixed a crash when the input layer had no features\n    - German and French translations updated\n",[180,336,337,342,347,352,357,362,368,373,379],{"__ignoreMap":202},[206,338,339],{"class":208,"line":209},[206,340,341],{},"changelog=\n",[206,343,344],{"class":208,"line":215},[206,345,346],{},"    1.4.0 - 2026-08-15\n",[206,348,349],{"class":208,"line":221},[206,350,351],{},"    - Added the Split by Ward algorithm to the Processing provider\n",[206,353,354],{"class":208,"line":227},[206,355,356],{},"    - Field picker now remembers the last field per layer\n",[206,358,359],{"class":208,"line":233},[206,360,361],{},"    - Fixed area totals being reported in the wrong units for geographic layers\n",[206,363,364],{"class":208,"line":239},[206,365,367],{"emptyLinePlaceholder":366},true,"\n",[206,369,370],{"class":208,"line":245},[206,371,372],{},"    1.3.2 - 2026-06-02\n",[206,374,376],{"class":208,"line":375},8,[206,377,378],{},"    - Fixed a crash when the input layer had no features\n",[206,380,382],{"class":208,"line":381},9,[206,383,384],{},"    - German and French translations updated\n",[14,386,387,389,390,392],{},[172,388,253],{}," The changelog lives in ",[180,391,182],{}," and is shown directly in the plugin manager, which is the only place most users will ever read it — so it has to answer their single question, which is whether this update affects them. Newest first, one line per change, phrased as what changed rather than which function was edited. Dates matter more than they look: a user deciding whether their problem is already fixed compares the date against when they last updated. Indentation continues the value in the INI format, so every line after the first must be indented; an unindented line silently ends the changelog.",[14,394,395],{},"Three things to avoid: commit messages pasted wholesale, entries like \"various fixes and improvements\" which tell nobody anything, and silence about behaviour changes. If an output column was renamed, say so — the user whose spreadsheet broke will find out anyway, and would rather find out here.",[14,397,398],{},[29,399,402,405,408,411,414,420,425,432,438,441,445,448,452,456,459,463,467,471,473,476,478,481],{"viewBox":400,"role":32,"ariaLabel":401,"xmlns":34},"0 0 760 250","Comparison of a changelog entry written as commit messages against one written as user visible changes, with the user question each answers",[36,403,404],{},"Two changelogs for the same release",[40,406,407],{},"One changelog lists internal commit messages about refactoring and dependency bumps, which does not help a user decide whether to update. The other lists what changed from the user's point of view: a new algorithm, a remembered setting, and a corrected unit conversion, which answers the question directly.",[44,409],{"x":46,"y":46,"width":47,"height":410,"fill":49},"250",[51,412,413],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"Write for the person deciding whether to click Upgrade",[44,415],{"x":416,"y":417,"width":418,"height":64,"rx":65,"fill":66,"stroke":419,"style":68},"20","48","348","#b45309",[51,421,424],{"x":422,"y":423,"style":73,"fill":419,"textAnchor":57},"194","74","commit messages",[44,426],{"x":94,"y":427,"width":428,"height":54,"rx":429,"fill":96,"stroke":430,"style":431},"90","300","5","#59645f","stroke-width:1.2",[51,433,437],{"x":434,"y":435,"style":436,"fill":430},"60","109","font-size:11px;font-family:sans-serif","refactor summarise helper",[44,439],{"x":94,"y":440,"width":428,"height":54,"rx":429,"fill":96,"stroke":430,"style":431},"124",[51,442,444],{"x":434,"y":443,"style":436,"fill":430},"143","bump minimum version",[44,446],{"x":94,"y":447,"width":428,"height":54,"rx":429,"fill":96,"stroke":430,"style":431},"158",[51,449,451],{"x":434,"y":450,"style":436,"fill":430},"177","various fixes and improvements",[51,453,455],{"x":422,"y":454,"style":100,"fill":419,"textAnchor":57},"212","answers nothing",[44,457],{"x":458,"y":417,"width":418,"height":64,"rx":65,"fill":136,"stroke":137,"style":68},"392",[51,460,462],{"x":461,"y":423,"style":73,"fill":137,"textAnchor":57},"566","user-visible changes",[44,464],{"x":465,"y":427,"width":428,"height":54,"rx":429,"fill":96,"stroke":466,"style":97},"416","#0f766e",[51,468,470],{"x":469,"y":435,"style":436,"fill":79},"432","new Split by Ward algorithm",[44,472],{"x":465,"y":440,"width":428,"height":54,"rx":429,"fill":96,"stroke":466,"style":97},[51,474,475],{"x":469,"y":443,"style":436,"fill":79},"field picker remembers your choice",[44,477],{"x":465,"y":447,"width":428,"height":54,"rx":429,"fill":96,"stroke":67,"style":97},[51,479,480],{"x":469,"y":450,"style":436,"fill":79},"area totals corrected for lat-long",[51,482,483],{"x":461,"y":454,"style":100,"fill":137,"textAnchor":57},"the third line is why they update",[161,485,487],{"id":486},"keep-the-tag-the-metadata-and-the-archive-in-agreement","Keep the tag, the metadata and the archive in agreement",[14,489,490],{},"Three places record the version, and they drift the moment any of them is edited by hand.",[197,492,496],{"className":493,"code":494,"language":495,"meta":202,"style":202},"language-bash shiki shiki-themes github-dark","# bump once, in metadata.txt, then:\ngit commit -am \"Release 1.4.0\"\ngit tag -a v1.4.0 -m \"Release 1.4.0\"\ngit push --follow-tags\n","bash",[180,497,498,504,521,539],{"__ignoreMap":202},[206,499,500],{"class":208,"line":209},[206,501,503],{"class":502},"sjoCn","# bump once, in metadata.txt, then:\n",[206,505,506,510,514,518],{"class":208,"line":215},[206,507,509],{"class":508},"svObZ","git",[206,511,513],{"class":512},"sU2Wk"," commit",[206,515,517],{"class":516},"sDLfK"," -am",[206,519,520],{"class":512}," \"Release 1.4.0\"\n",[206,522,523,525,528,531,534,537],{"class":208,"line":221},[206,524,509],{"class":508},[206,526,527],{"class":512}," tag",[206,529,530],{"class":516}," -a",[206,532,533],{"class":512}," v1.4.0",[206,535,536],{"class":516}," -m",[206,538,520],{"class":512},[206,540,541,543,546],{"class":208,"line":227},[206,542,509],{"class":508},[206,544,545],{"class":512}," push",[206,547,548],{"class":516}," --follow-tags\n",[14,550,551,254,553,555,556,560],{},[172,552,253],{},[180,554,182],{}," is the single source of truth, because it is what QGIS reads. The tag exists so the exact source of a release can be recovered when a user reports a bug against it — without one, \"which version is this?\" has no answer three releases later. Building the archive from the metadata version, as the script in ",[21,557,559],{"href":558},"\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fpackage-qgis-plugin-as-zip\u002F","Package a QGIS Plugin as a Zip"," does, closes the loop: the file name, the metadata and the tag cannot disagree unless somebody edits the zip. A continuous-integration job that builds on tag and refuses when the tag and the metadata differ makes the guarantee mechanical.",[14,562,563],{},[29,564,567,570,573,576,591,594,601,604,608,615,619,623,626,630,633,639],{"viewBox":565,"role":32,"ariaLabel":566,"xmlns":34},"0 0 760 244","Diagram of the three places a version is recorded, the metadata file, the git tag and the built archive name, all derived from the metadata",[36,568,569],{},"One version, three records",[40,571,572],{},"The version in metadata.txt is the source of truth because it is what QGIS reads. The git tag exists so the source of a release can be recovered later, and the archive name is derived from the metadata so it cannot disagree. Editing any of the three by hand is what lets them drift apart.",[44,574],{"x":46,"y":46,"width":47,"height":575,"fill":49},"244",[577,578,579],"defs",{},[580,581,587],"marker",{"id":582,"viewBox":583,"refX":584,"refY":429,"markerWidth":585,"markerHeight":585,"orient":586},"verArrow","0 0 10 10","8","7","auto-start-reverse",[588,589],"path",{"d":590,"fill":466},"M0 0 L10 5 L0 10 z",[51,592,593],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"Derive the other two and they cannot drift",[44,595],{"x":596,"y":597,"width":598,"height":599,"rx":65,"fill":600,"stroke":466,"style":68},"272","50","216","62","#eef7f4",[51,602,182],{"x":53,"y":603,"style":73,"fill":466,"textAnchor":57},"76",[51,605,607],{"x":53,"y":606,"style":78,"fill":79,"textAnchor":57},"98","version=1.4.0 — what QGIS reads",[44,609],{"x":610,"y":611,"width":612,"height":613,"rx":584,"fill":96,"stroke":111,"style":614},"40","156","280","66","stroke-width:2",[51,616,618],{"x":64,"y":617,"style":100,"fill":111,"textAnchor":57},"182","git tag v1.4.0",[51,620,622],{"x":64,"y":621,"style":78,"fill":79,"textAnchor":57},"204","recovers the exact source later",[44,624],{"x":625,"y":611,"width":612,"height":613,"rx":584,"fill":96,"stroke":137,"style":614},"440",[51,627,629],{"x":628,"y":617,"style":100,"fill":137,"textAnchor":57},"580","parcel tools-1.4.0.zip",[51,631,632],{"x":628,"y":621,"style":78,"fill":79,"textAnchor":57},"name read from the metadata",[208,634],{"x1":635,"y1":636,"x2":637,"y2":87,"stroke":466,"style":638},"330","112","220","stroke-width:2;marker-end:url(#verArrow)",[208,640],{"x1":641,"y1":636,"x2":642,"y2":87,"stroke":466,"style":638},"430","540",[161,644,646],{"id":645},"decide-what-counts-as-breaking","Decide what counts as breaking",[14,648,649],{},"For a QGIS plugin, the interface users depend on is wider than the code, and three things in particular are breaking changes even though nothing in Python signals it.",[14,651,652,655,656,659],{},[172,653,654],{},"Algorithm identifiers."," A saved model or a script referencing ",[180,657,658],{},"parceltools:summariseparcels"," breaks if you rename either half. If you must, keep the old name registered as an alias for a release or two.",[14,661,662,665,666,669,670,673],{},[172,663,664],{},"Parameter names."," Batch configurations and scripts pass parameters by name. Renaming ",[180,667,668],{},"MIN_AREA"," to ",[180,671,672],{},"MINIMUM_AREA"," silently breaks every stored run, since an unknown key is ignored rather than rejected.",[14,675,676,679],{},[172,677,678],{},"Output shapes."," A renamed field, a changed unit, an extra row per input — anything downstream that reads your output is affected, and none of it produces an error.",[14,681,682,683,687],{},"Settings keys are the near-miss in this list: they break only your own plugin, so a migration on startup, as described in ",[21,684,686],{"href":685},"\u002Fqgis-plugin-development\u002Fplugin-settings-and-localization\u002F","Plugin Settings and Localization",", is enough there. Everything else in the list needs a major version and a changelog line that names the change plainly.",[161,689,691],{"id":690},"qgis-version-compatibility","QGIS version compatibility",[693,694,695,711],"table",{},[696,697,698],"thead",{},[699,700,701,705,708],"tr",{},[702,703,704],"th",{},"QGIS version",[702,706,707],{},"Python",[702,709,710],{},"Notes",[712,713,714,726,736,747],"tbody",{},[699,715,716,720,723],{},[717,718,719],"td",{},"3.22 LTR",[717,721,722],{},"3.9",[717,724,725],{},"Version comparison, experimental and deprecated flags as described.",[699,727,728,731,733],{},[717,729,730],{},"3.28 LTR",[717,732,722],{},[717,734,735],{},"Identical.",[699,737,738,741,744],{},[717,739,740],{},"3.34 LTR",[717,742,743],{},"3.12",[717,745,746],{},"Baseline for this page.",[699,748,749,752,754],{},[717,750,751],{},"3.40 \u002F 3.44",[717,753,743],{},[717,755,756],{},"Identical metadata handling; the plugin manager presents changelogs slightly more prominently.",[14,758,759,760,763],{},"Supporting a wide range of QGIS versions costs testing, not metadata. Where an API you need only exists on newer releases, guard it with ",[180,761,762],{},"hasattr()"," and raise the minimum only when the workaround becomes worse than dropping the old release.",[161,765,767],{"id":766},"troubleshooting","Troubleshooting",[166,769,770,779,790,796,804,810],{},[169,771,772,775,776,778],{},[172,773,774],{},"Users are not offered the update."," The version did not increase by the manager's comparison, or ",[180,777,281],{}," excludes their release.",[169,780,781,254,784,786,787,789],{},[172,782,783],{},"The plugin disappeared after a QGIS upgrade.",[180,785,281],{}," is too low. Set it to ",[180,788,285],{}," unless you have a specific reason.",[169,791,792,795],{},[172,793,794],{},"The changelog shows only the first line."," Continuation lines are not indented in the INI file.",[169,797,798,254,801,803],{},[172,799,800],{},"An experimental release reached everyone.",[180,802,320],{}," was left off, or was removed for a release that was not ready.",[169,805,806,809],{},[172,807,808],{},"A saved model stopped working after an update."," An algorithm or parameter identifier changed. That is a major version, and needs an alias or a clear note.",[169,811,812,815],{},[172,813,814],{},"The tag and the shipped version disagree."," The archive was built before the metadata was bumped. Derive the file name from the metadata.",[161,817,819],{"id":818},"conclusion","Conclusion",[14,821,822,823,825,826,828],{},"Use three-part versions and increase the part that matches the kind of change: major for anything that breaks a saved model, script or output; minor for additions; patch for fixes. Keep ",[180,824,277],{}," honest and ",[180,827,281],{}," open. Write the changelog for the person deciding whether to upgrade, newest first, one line per user-visible change. Tag every release, and build the archive from the metadata so nothing can drift.",[161,830,832],{"id":831},"frequently-asked-questions","Frequently Asked Questions",[14,834,835,838],{},[172,836,837],{},"Does QGIS require semantic versioning?","\nNo — it compares numbers part by part. Semantic versioning is a convention that makes the comparison meaningful to humans, which is why it is worth following.",[14,840,841,844],{},[172,842,843],{},"Can I publish a release for old QGIS versions alongside a new one?","\nYes. The repository serves the newest release compatible with each user's QGIS, so an older release with a lower maximum version keeps serving those users.",[14,846,847,850,851,853,854,857],{},[172,848,849],{},"Should the changelog live in metadata.txt or a separate file?","\nIn ",[180,852,182],{},", because that is what the plugin manager shows. Keeping a fuller ",[180,855,856],{},"CHANGELOG.md"," in the repository as well is common and costs little.",[14,859,860,863,864,866],{},[172,861,862],{},"How do I retire a plugin?","\nSet ",[180,865,326],{}," in a final release whose changelog names the successor. It stays installable and warns anybody who finds it.",[14,868,869,872,875,876,879,880,669,883,885],{},[172,870,871],{},"What version should a first release be?",[180,873,874],{},"0.1.0"," if you expect the interface to change, ",[180,877,878],{},"1.0.0"," if you are ready to treat it as stable. Going from ",[180,881,882],{},"0.x",[180,884,878],{}," is itself a useful signal.",[14,887,888,891],{},[172,889,890],{},"Do translations need a version bump?","\nYes — anything shipped is a release. New or updated translations are a minor bump, and worth a changelog line so speakers of that language know.",[161,893,895],{"id":894},"related","Related",[166,897,898,903,907,911,915],{},[169,899,900,902],{},[21,901,24],{"href":23}," — the guide this recipe belongs to",[169,904,905],{},[21,906,187],{"href":186},[169,908,909],{},[21,910,559],{"href":558},[169,912,913],{},[21,914,686],{"href":685},[169,916,917],{},[21,918,920],{"href":919},"\u002Fqgis-plugin-development\u002Ftesting-and-ci-for-plugins\u002Frun-qgis-plugin-tests-in-github-actions\u002F","Run QGIS Plugin Tests in GitHub Actions",[922,923,924],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":202,"searchDepth":215,"depth":215,"links":926},[927,928,929,930,931,932,933,934,935,936,937],{"id":163,"depth":215,"text":164},{"id":194,"depth":215,"text":195},{"id":289,"depth":215,"text":290},{"id":330,"depth":215,"text":331},{"id":486,"depth":215,"text":487},{"id":645,"depth":215,"text":646},{"id":690,"depth":215,"text":691},{"id":766,"depth":215,"text":767},{"id":818,"depth":215,"text":819},{"id":831,"depth":215,"text":832},{"id":894,"depth":215,"text":895},"Number releases so users and QGIS both understand them — semantic versions in metadata.txt, minimum and maximum QGIS versions, an experimental channel, and a changelog people actually read.","md",{"slug":941,"type":942,"breadcrumb":943,"datePublished":944,"dateModified":944},"version-and-changelog-qgis-plugin","article","Versions & Changelog","2026-08-15","\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fversion-and-changelog-qgis-plugin",{"title":5,"description":938},"qgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fversion-and-changelog-qgis-plugin\u002Findex","5FeDK7WOKz52jcciRyholS8WHwyaAzGaF9GtWTZ_R3U",1786789584661]