[{"data":1,"prerenderedAt":811},["ShallowReactive",2],{"footer-links":3,"content-\u002Fdevelop\u002Frelease":12},[4,9],{"path":5,"title":6,"type":7,"isExternal":8},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite","GitHub Repo","link",true,{"path":10,"title":11,"type":7,"isExternal":8},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@life-and-dev\u002Fmdsite","NPM Registry",{"id":13,"title":14,"body":15,"description":804,"extension":805,"meta":806,"navigation":8,"path":807,"seo":808,"stem":809,"__hash__":810},"content\u002Fdevelop\u002Frelease.md","Release",{"type":16,"value":17,"toc":791},"minimark",[18,22,37,45,50,53,112,116,119,206,217,309,316,321,330,380,397,404,409,413,416,502,510,514,527,561,587,590,598,610,614,627,663,666,670,673,721,725,728,778,781,787],[19,20,14],"h1",{"id":21},"release",[23,24,25,26,30,31,36],"p",{},"This page explains how to cut a new release of ",[27,28,29],"code",{},"@life-and-dev\u002Fmdsite",". For the contributor overview, start at ",[32,33,35],"a",{"href":34},"..\u002Fdevelop","Developing mdsite",".",[23,38,39,40,44],{},"Releases are ",[41,42,43],"strong",{},"tag-driven",": a local script bumps the version, builds, verifies, commits, tags, and pushes. Pushing the tag triggers a GitHub Actions workflow that publishes to npm with provenance and creates the GitHub Release.",[46,47,49],"h2",{"id":48},"_1-prerequisites","1. Prerequisites",[23,51,52],{},"Before cutting a release, confirm:",[54,55,56,72,81,88,98],"ul",{},[57,58,59,60,67,68,71],"li",{},"You have push access to ",[32,61,64],{"href":5,"rel":62},[63],"nofollow",[27,65,66],{},"life-and-dev\u002Fmdsite"," on the ",[27,69,70],{},"main"," branch.",[57,73,74,75,77,78,36],{},"The working tree is clean on ",[27,76,70],{}," and up to date with ",[27,79,80],{},"origin\u002Fmain",[57,82,83,84,87],{},"The ",[27,85,86],{},"mdsite-nuxt"," submodule pointer is at the commit you want to ship.",[57,89,90,91,94,95,97],{},"npm ",[41,92,93],{},"Trusted Publisher"," is configured for ",[27,96,29],{}," (see section 5 below).",[57,99,100,103,104,107,108,111],{},[27,101,102],{},"npm publish --provenance"," will work from GitHub Actions — the workflow needs ",[27,105,106],{},"id-token: write"," permission (already set in ",[27,109,110],{},".github\u002Fworkflows\u002Fnpm-publish.yml",").",[46,113,115],{"id":114},"_2-cut-the-release-locally","2. Cut the release locally",[23,117,118],{},"From the repository root, choose a semantic-version bump:",[120,121,126],"pre",{"className":122,"code":123,"language":124,"meta":125,"style":125},"language-bash shiki shiki-themes github-light github-dark","npm run release:version -- patch    # 0.0.x bugfix\nnpm run release:version -- minor    # 0.x.0 feature\nnpm run release:version -- major    # x.0.0 breaking\nnpm run release:version -- 1.2.3    # exact version\n","bash","",[27,127,128,155,172,189],{"__ignoreMap":125},[129,130,133,137,141,144,148,151],"span",{"class":131,"line":132},"line",1,[129,134,136],{"class":135},"sScJk","npm",[129,138,140],{"class":139},"sZZnC"," run",[129,142,143],{"class":139}," release:version",[129,145,147],{"class":146},"sj4cs"," --",[129,149,150],{"class":139}," patch",[129,152,154],{"class":153},"sJ8bj","    # 0.0.x bugfix\n",[129,156,158,160,162,164,166,169],{"class":131,"line":157},2,[129,159,136],{"class":135},[129,161,140],{"class":139},[129,163,143],{"class":139},[129,165,147],{"class":146},[129,167,168],{"class":139}," minor",[129,170,171],{"class":153},"    # 0.x.0 feature\n",[129,173,175,177,179,181,183,186],{"class":131,"line":174},3,[129,176,136],{"class":135},[129,178,140],{"class":139},[129,180,143],{"class":139},[129,182,147],{"class":146},[129,184,185],{"class":139}," major",[129,187,188],{"class":153},"    # x.0.0 breaking\n",[129,190,192,194,196,198,200,203],{"class":131,"line":191},4,[129,193,136],{"class":135},[129,195,140],{"class":139},[129,197,143],{"class":139},[129,199,147],{"class":146},[129,201,202],{"class":146}," 1.2.3",[129,204,205],{"class":153},"    # exact version\n",[23,207,208,216],{},[32,209,83,212,215],{"href":210,"rel":211},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite\u002Fblob\u002Fmain\u002Fscripts\u002Frelease-version.ts",[63],[27,213,214],{},"release:version"," script"," automates the boring parts:",[218,219,220,237,246,259,272,281,291],"ol",{},[57,221,222,225,226,229,230,233,234,36],{},[41,223,224],{},"Bumps the version"," in ",[27,227,228],{},"package.json"," and ",[27,231,232],{},"package-lock.json"," via ",[27,235,236],{},"npm version --no-git-tag-version",[57,238,239,242,243,111],{},[41,240,241],{},"Type-checks"," the CLI (",[27,244,245],{},"npm run typecheck",[57,247,248,251,252,255,256,111],{},[41,249,250],{},"Builds"," ",[27,253,254],{},"dist\u002F"," (",[27,257,258],{},"npm run build",[57,260,261,255,264,267,268,271],{},[41,262,263],{},"Verifies the package layout",[27,265,266],{},"npm run verify:package","). This catches missing files, wrong bin path, wrong package name, and missing ",[27,269,270],{},"files"," entries before publish.",[57,273,274,277,278,36],{},[41,275,276],{},"Commits"," as ",[27,279,280],{},"chore: release v\u003Cversion>",[57,282,283,286,287,290],{},[41,284,285],{},"Tags"," the commit as ",[27,288,289],{},"v\u003Cversion>"," (annotated).",[57,292,293,296,297,299,300,233,302,305,306,111],{},[41,294,295],{},"Pushes"," the local commit and the ",[27,298,289],{}," tag to ",[27,301,80],{},[27,303,304],{},"git push --follow-tags origin HEAD:main"," (using ",[27,307,308],{},"spawnSync",[23,310,311,312,315],{},"The push is non-interactive and runs from inside the script. It fails loudly if ",[27,313,314],{},"origin"," cannot be reached, authentication fails, or the push is rejected — in that case no commit or tag is left on the remote and the script exits non-zero.",[317,318,320],"h3",{"id":319},"what-gets-published","What gets published",[23,322,323,324,326,327,329],{},"The published package contains only the paths listed in ",[27,325,228],{},"'s ",[27,328,270],{}," field:",[120,331,335],{"className":332,"code":333,"language":334,"meta":125,"style":125},"language-json shiki shiki-themes github-light github-dark","\"files\": [\n  \"bin\u002F\",\n  \"dist\u002F\",\n  \"mdsite-nuxt\u002F\",\n  \"README.md\"\n]\n","json",[27,336,337,346,354,361,368,374],{"__ignoreMap":125},[129,338,339,342],{"class":131,"line":132},[129,340,341],{"class":139},"\"files\"",[129,343,345],{"class":344},"sVt8B",": [\n",[129,347,348,351],{"class":131,"line":157},[129,349,350],{"class":139},"  \"bin\u002F\"",[129,352,353],{"class":344},",\n",[129,355,356,359],{"class":131,"line":174},[129,357,358],{"class":139},"  \"dist\u002F\"",[129,360,353],{"class":344},[129,362,363,366],{"class":131,"line":191},[129,364,365],{"class":139},"  \"mdsite-nuxt\u002F\"",[129,367,353],{"class":344},[129,369,371],{"class":131,"line":370},5,[129,372,373],{"class":139},"  \"README.md\"\n",[129,375,377],{"class":131,"line":376},6,[129,378,379],{"class":344},"]\n",[23,381,382,385,386,389,390,392,393,396],{},[27,383,384],{},"scripts\u002Fverify-package-artifacts.ts"," enforces this — release will fail if ",[27,387,388],{},"bin\u002F",", ",[27,391,254],{},", or ",[27,394,395],{},"mdsite-nuxt\u002F"," are missing or misconfigured.",[317,398,83,400,403],{"id":399},"the-prepublishonly-safety-net",[27,401,402],{},"prepublishOnly"," safety net",[23,405,406,408],{},[27,407,228],{}," defines:",[46,410,412],{"id":411},"_3-review-the-local-commit-and-tag","3. Review the local commit and tag",[23,414,415],{},"After the script completes (and before CI picks up the tag), inspect what was created:",[120,417,419],{"className":122,"code":418,"language":124,"meta":125,"style":125},"git log -1 --stat                    # the chore: release v\u003Cversion> commit\ngit show v\u003Cversion>                  # the annotated tag\ngit tag --list 'v*' | tail           # recent tags\ngit ls-remote origin 'refs\u002Ftags\u002Fv*'  # confirm the tag reached origin\n",[27,420,421,438,464,486],{"__ignoreMap":125},[129,422,423,426,429,432,435],{"class":131,"line":132},[129,424,425],{"class":135},"git",[129,427,428],{"class":139}," log",[129,430,431],{"class":146}," -1",[129,433,434],{"class":146}," --stat",[129,436,437],{"class":153},"                    # the chore: release v\u003Cversion> commit\n",[129,439,440,442,445,448,452,455,458,461],{"class":131,"line":157},[129,441,425],{"class":135},[129,443,444],{"class":139}," show",[129,446,447],{"class":139}," v",[129,449,451],{"class":450},"szBVR","\u003C",[129,453,454],{"class":139},"versio",[129,456,457],{"class":344},"n",[129,459,460],{"class":450},">",[129,462,463],{"class":153},"                  # the annotated tag\n",[129,465,466,468,471,474,477,480,483],{"class":131,"line":174},[129,467,425],{"class":135},[129,469,470],{"class":139}," tag",[129,472,473],{"class":146}," --list",[129,475,476],{"class":139}," 'v*'",[129,478,479],{"class":450}," |",[129,481,482],{"class":135}," tail",[129,484,485],{"class":153},"           # recent tags\n",[129,487,488,490,493,496,499],{"class":131,"line":191},[129,489,425],{"class":135},[129,491,492],{"class":139}," ls-remote",[129,494,495],{"class":139}," origin",[129,497,498],{"class":139}," 'refs\u002Ftags\u002Fv*'",[129,500,501],{"class":153},"  # confirm the tag reached origin\n",[23,503,504,505,299,507,509],{},"If anything looks wrong, the script has already pushed the commit and the ",[27,506,289],{},[27,508,314],{},", so recovery involves the local branch and the remote tag — see section 6 for the full cleanup path.",[46,511,513],{"id":512},"what-the-script-pushed","What the script pushed",[23,515,83,516,518,519,299,521,523,524,526],{},[27,517,214],{}," script's final step pushes the local commit and the ",[27,520,289],{},[27,522,80],{}," using ",[27,525,304],{},". There is nothing to push manually.",[120,528,530],{"className":122,"code":529,"language":124,"meta":125,"style":125},"git ls-remote origin 'refs\u002Ftags\u002Fv*'  # confirm the tag reached origin\ngit log --oneline -1 origin\u002Fmain     # confirm origin\u002Fmain points at the chore: release commit\n",[27,531,532,544],{"__ignoreMap":125},[129,533,534,536,538,540,542],{"class":131,"line":132},[129,535,425],{"class":135},[129,537,492],{"class":139},[129,539,495],{"class":139},[129,541,498],{"class":139},[129,543,501],{"class":153},[129,545,546,548,550,553,555,558],{"class":131,"line":157},[129,547,425],{"class":135},[129,549,428],{"class":139},[129,551,552],{"class":146}," --oneline",[129,554,431],{"class":146},[129,556,557],{"class":139}," origin\u002Fmain",[129,559,560],{"class":153},"     # confirm origin\u002Fmain points at the chore: release commit\n",[562,563,565],"markdown-alert",{"type":564},"warning",[23,566,567,568,570,571,574,575,578,579,582,583,586],{},"Pushing the ",[27,569,289],{}," tag triggers the GitHub Actions ",[27,572,573],{},"npm publish"," workflow. The workflow checks out the tag (with submodules), runs ",[27,576,577],{},"npm ci",", and publishes to the npm registry with ",[27,580,581],{},"--provenance --access public",". It also creates a GitHub Release using ",[27,584,585],{},"softprops\u002Faction-gh-release@v3"," with auto-generated notes.",[23,588,589],{},"Watch the workflow run:",[120,591,596],{"className":592,"code":594,"language":595},[593],"language-text","https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite\u002Factions\u002Fworkflows\u002Fnpm-publish.yml\n","text",[27,597,594],{"__ignoreMap":125},[23,599,600,601,604,605,609],{},"A successful run publishes ",[27,602,603],{},"\u003Cversion>"," to ",[32,606,608],{"href":10,"rel":607},[63],"npmjs.com\u002Fpackage\u002F@life-and-dev\u002Fmdsite"," within a minute or two.",[46,611,613],{"id":612},"_5-one-time-setup-npm-trusted-publisher","5. One-time setup: npm Trusted Publisher",[23,615,616,617,622,623,626],{},"The publish workflow uses ",[32,618,621],{"href":619,"rel":620},"https:\u002F\u002Fdocs.npmjs.com\u002Fgenerating-provenance-statements",[63],"npm provenance"," and Trusted Publisher instead of a long-lived ",[27,624,625],{},"NPM_TOKEN",". To (re)configure it:",[218,628,629,641,646,654],{},[57,630,631,632,637,638,36],{},"Sign in to ",[32,633,636],{"href":634,"rel":635},"https:\u002F\u002Fwww.npmjs.com\u002F",[63],"npmjs.com"," as an owner of ",[27,639,640],{},"@life-and-dev",[57,642,643,644,36],{},"Open the package settings for ",[27,645,29],{},[57,647,648,649,651,652,36],{},"Add a Trusted Publisher linking to ",[27,650,66],{}," and the workflow file ",[27,653,110],{},[57,655,656,657,229,659,662],{},"Confirm the workflow's ",[27,658,106],{},[27,660,661],{},"contents: write"," permissions match what's in the repo.",[23,664,665],{},"Once configured, no secrets are needed — GitHub Actions obtains a short-lived OIDC token signed by GitHub, and npm verifies it against the Trusted Publisher config.",[46,667,669],{"id":668},"_6-what-if-the-publish-workflow-fails","6. What if the publish workflow fails?",[23,671,672],{},"If CI fails after the script pushed the tag:",[218,674,675,683,715],{},[57,676,677,680,681,36],{},[41,678,679],{},"Do not re-tag the same version."," A failed publish does not consume the version on npm if the workflow errored before ",[27,682,573],{},[57,684,685,686],{},"Read the failed step's logs. Common causes:\n",[54,687,688,694],{},[57,689,690,691,111],{},"Trusted Publisher not configured → fix on npmjs.com and re-run the workflow from the Actions UI (",[27,692,693],{},"workflow_dispatch",[57,695,696,699,700,299,702,704,705,707,708,711,712,36],{},[27,697,698],{},"verify:package"," failed → a required file is missing from the tag. The script has already pushed the local commit and the ",[27,701,289],{},[27,703,314],{},", so cleanup touches both sides: delete the remote tag, reset the local ",[27,706,70],{}," branch back past the ",[27,709,710],{},"chore: release"," commit, fix the issue, then re-run ",[27,713,714],{},"npm run release:version -- \u003Cbump>",[57,716,717,718,720],{},"To re-run without cutting a new version, use the workflow's ",[27,719,693],{}," input and pass the existing tag name.",[46,722,724],{"id":723},"_7-release-checklist","7. Release checklist",[23,726,727],{},"A quick summary you can paste into a PR description:",[54,729,732,744,752,760,769],{"className":730},[731],"contains-task-list",[57,733,736,740,741,111],{"className":734},[735],"task-list-item",[737,738],"input",{"disabled":8,"type":739},"checkbox"," All tests pass (",[27,742,743],{},"npm test",[57,745,747,251,749,751],{"className":746},[735],[737,748],{"disabled":8,"type":739},[27,750,86],{}," submodule is at the desired commit.",[57,753,755,251,757,759],{"className":754},[735],[737,756],{"disabled":8,"type":739},[27,758,714],{}," succeeded locally.",[57,761,763,765,766,768],{"className":762},[735],[737,764],{"disabled":8,"type":739}," GitHub Actions ",[27,767,573],{}," workflow went green.",[57,770,772,774,775,36],{"className":771},[735],[737,773],{"disabled":8,"type":739}," New version is visible on ",[32,776,636],{"href":10,"rel":777},[63],[779,780],"hr",{},[562,782,784],{"type":783},"tip",[23,785,786],{},"Releases are immutable on npm. Once a version is published it cannot be overwritten — only deprecated or bumped. That is why the release script verifies everything locally before tagging, and why CI re-verifies before publishing.",[788,789,790],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":125,"searchDepth":157,"depth":157,"links":792},[793,794,799,800,801,802,803],{"id":48,"depth":157,"text":49},{"id":114,"depth":157,"text":115,"children":795},[796,797],{"id":319,"depth":174,"text":320},{"id":399,"depth":174,"text":798},"The prepublishOnly safety net",{"id":411,"depth":157,"text":412},{"id":512,"depth":157,"text":513},{"id":612,"depth":157,"text":613},{"id":668,"depth":157,"text":669},{"id":723,"depth":157,"text":724},"This page explains how to cut a new release of @life-and-dev\u002Fmdsite. For the contributor overview, start at Developing mdsite.","md",{},"\u002Fdevelop\u002Frelease",{"title":14,"description":804},"develop\u002Frelease","BHEN8ouQ0Z7W-bkHcx5vxUoAWEgvIqTCnxJ4eLW7a-o",1784017753071]