[{"data":1,"prerenderedAt":525},["ShallowReactive",2],{"\u002Fdocs\u002Fclients":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"body":10,"_type":519,"_id":520,"_source":521,"_file":522,"_stem":523,"_extension":524},"\u002Fdocs\u002Fclients","docs",false,"","Open-source clients","i3deploy's public contract is the JSON manifest, not a proprietary widget or\nSDK. Anything that can do a GET and parse JSON can render a changelog from it — a static\nsite, a mobile app, a Slack bot, a curl | jq one-liner in a build script. There's no official\nclient library you're required to use, and that's on purpose: the feed is the product.",{"type":11,"children":12,"toc":514},"root",[13,21,53,60,339,374,380,405,447,476,482,508],{"type":14,"tag":15,"props":16,"children":18},"element","h1",{"id":17},"open-source-clients",[19],{"type":20,"value":8},"text",{"type":14,"tag":22,"props":23,"children":24},"p",{},[25,27,34,36,43,45,51],{"type":20,"value":26},"i3deploy's public contract is the ",{"type":14,"tag":28,"props":29,"children":31},"a",{"href":30},"\u002Fdocs\u002Fmanifest",[32],{"type":20,"value":33},"JSON manifest",{"type":20,"value":35},", not a proprietary widget or\nSDK. Anything that can do a ",{"type":14,"tag":37,"props":38,"children":40},"code",{"className":39},[],[41],{"type":20,"value":42},"GET",{"type":20,"value":44}," and parse JSON can render a changelog from it — a static\nsite, a mobile app, a Slack bot, a ",{"type":14,"tag":37,"props":46,"children":48},{"className":47},[],[49],{"type":20,"value":50},"curl | jq",{"type":20,"value":52}," one-liner in a build script. There's no official\nclient library you're required to use, and that's on purpose: the feed is the product.",{"type":14,"tag":54,"props":55,"children":57},"h2",{"id":56},"a-minimal-consumer",[58],{"type":20,"value":59},"A minimal consumer",{"type":14,"tag":61,"props":62,"children":66},"pre",{"className":63,"code":64,"language":65,"meta":7,"style":7},"language-js shiki shiki-themes github-dark","const res = await fetch(\n  \"https:\u002F\u002Fstorage.googleapis.com\u002Fi3deploy-public\u002F\u003Corg>\u002Fprojects\u002F\u003Cslug>\u002Freleases.json\"\n)\nconst { releases } = await res.json()\n\nfor (const release of releases) {\n  console.log(`${release.version} — ${release.name} (${release.released_at})`)\n  console.log(release.short_summary)\n  \u002F\u002F release.changelog_markdown is Markdown — render it with any Markdown library\n}\n","js",[67],{"type":14,"tag":37,"props":68,"children":69},{"__ignoreMap":7},[70,110,120,129,176,186,219,303,320,330],{"type":14,"tag":71,"props":72,"children":75},"span",{"class":73,"line":74},"line",1,[76,82,88,93,98,104],{"type":14,"tag":71,"props":77,"children":79},{"style":78},"--shiki-default:#F97583",[80],{"type":20,"value":81},"const",{"type":14,"tag":71,"props":83,"children":85},{"style":84},"--shiki-default:#79B8FF",[86],{"type":20,"value":87}," res",{"type":14,"tag":71,"props":89,"children":90},{"style":78},[91],{"type":20,"value":92}," =",{"type":14,"tag":71,"props":94,"children":95},{"style":78},[96],{"type":20,"value":97}," await",{"type":14,"tag":71,"props":99,"children":101},{"style":100},"--shiki-default:#B392F0",[102],{"type":20,"value":103}," fetch",{"type":14,"tag":71,"props":105,"children":107},{"style":106},"--shiki-default:#E1E4E8",[108],{"type":20,"value":109},"(\n",{"type":14,"tag":71,"props":111,"children":113},{"class":73,"line":112},2,[114],{"type":14,"tag":71,"props":115,"children":117},{"style":116},"--shiki-default:#9ECBFF",[118],{"type":20,"value":119},"  \"https:\u002F\u002Fstorage.googleapis.com\u002Fi3deploy-public\u002F\u003Corg>\u002Fprojects\u002F\u003Cslug>\u002Freleases.json\"\n",{"type":14,"tag":71,"props":121,"children":123},{"class":73,"line":122},3,[124],{"type":14,"tag":71,"props":125,"children":126},{"style":106},[127],{"type":20,"value":128},")\n",{"type":14,"tag":71,"props":130,"children":132},{"class":73,"line":131},4,[133,137,142,147,152,157,161,166,171],{"type":14,"tag":71,"props":134,"children":135},{"style":78},[136],{"type":20,"value":81},{"type":14,"tag":71,"props":138,"children":139},{"style":106},[140],{"type":20,"value":141}," { ",{"type":14,"tag":71,"props":143,"children":144},{"style":84},[145],{"type":20,"value":146},"releases",{"type":14,"tag":71,"props":148,"children":149},{"style":106},[150],{"type":20,"value":151}," } ",{"type":14,"tag":71,"props":153,"children":154},{"style":78},[155],{"type":20,"value":156},"=",{"type":14,"tag":71,"props":158,"children":159},{"style":78},[160],{"type":20,"value":97},{"type":14,"tag":71,"props":162,"children":163},{"style":106},[164],{"type":20,"value":165}," res.",{"type":14,"tag":71,"props":167,"children":168},{"style":100},[169],{"type":20,"value":170},"json",{"type":14,"tag":71,"props":172,"children":173},{"style":106},[174],{"type":20,"value":175},"()\n",{"type":14,"tag":71,"props":177,"children":179},{"class":73,"line":178},5,[180],{"type":14,"tag":71,"props":181,"children":183},{"emptyLinePlaceholder":182},true,[184],{"type":20,"value":185},"\n",{"type":14,"tag":71,"props":187,"children":189},{"class":73,"line":188},6,[190,195,200,204,209,214],{"type":14,"tag":71,"props":191,"children":192},{"style":78},[193],{"type":20,"value":194},"for",{"type":14,"tag":71,"props":196,"children":197},{"style":106},[198],{"type":20,"value":199}," (",{"type":14,"tag":71,"props":201,"children":202},{"style":78},[203],{"type":20,"value":81},{"type":14,"tag":71,"props":205,"children":206},{"style":84},[207],{"type":20,"value":208}," release",{"type":14,"tag":71,"props":210,"children":211},{"style":78},[212],{"type":20,"value":213}," of",{"type":14,"tag":71,"props":215,"children":216},{"style":106},[217],{"type":20,"value":218}," releases) {\n",{"type":14,"tag":71,"props":220,"children":222},{"class":73,"line":221},7,[223,228,233,238,243,248,253,258,263,267,271,276,281,285,289,294,299],{"type":14,"tag":71,"props":224,"children":225},{"style":106},[226],{"type":20,"value":227},"  console.",{"type":14,"tag":71,"props":229,"children":230},{"style":100},[231],{"type":20,"value":232},"log",{"type":14,"tag":71,"props":234,"children":235},{"style":106},[236],{"type":20,"value":237},"(",{"type":14,"tag":71,"props":239,"children":240},{"style":116},[241],{"type":20,"value":242},"`${",{"type":14,"tag":71,"props":244,"children":245},{"style":106},[246],{"type":20,"value":247},"release",{"type":14,"tag":71,"props":249,"children":250},{"style":116},[251],{"type":20,"value":252},".",{"type":14,"tag":71,"props":254,"children":255},{"style":106},[256],{"type":20,"value":257},"version",{"type":14,"tag":71,"props":259,"children":260},{"style":116},[261],{"type":20,"value":262},"} — ${",{"type":14,"tag":71,"props":264,"children":265},{"style":106},[266],{"type":20,"value":247},{"type":14,"tag":71,"props":268,"children":269},{"style":116},[270],{"type":20,"value":252},{"type":14,"tag":71,"props":272,"children":273},{"style":106},[274],{"type":20,"value":275},"name",{"type":14,"tag":71,"props":277,"children":278},{"style":116},[279],{"type":20,"value":280},"} (${",{"type":14,"tag":71,"props":282,"children":283},{"style":106},[284],{"type":20,"value":247},{"type":14,"tag":71,"props":286,"children":287},{"style":116},[288],{"type":20,"value":252},{"type":14,"tag":71,"props":290,"children":291},{"style":106},[292],{"type":20,"value":293},"released_at",{"type":14,"tag":71,"props":295,"children":296},{"style":116},[297],{"type":20,"value":298},"})`",{"type":14,"tag":71,"props":300,"children":301},{"style":106},[302],{"type":20,"value":128},{"type":14,"tag":71,"props":304,"children":306},{"class":73,"line":305},8,[307,311,315],{"type":14,"tag":71,"props":308,"children":309},{"style":106},[310],{"type":20,"value":227},{"type":14,"tag":71,"props":312,"children":313},{"style":100},[314],{"type":20,"value":232},{"type":14,"tag":71,"props":316,"children":317},{"style":106},[318],{"type":20,"value":319},"(release.short_summary)\n",{"type":14,"tag":71,"props":321,"children":323},{"class":73,"line":322},9,[324],{"type":14,"tag":71,"props":325,"children":327},{"style":326},"--shiki-default:#6A737D",[328],{"type":20,"value":329},"  \u002F\u002F release.changelog_markdown is Markdown — render it with any Markdown library\n",{"type":14,"tag":71,"props":331,"children":333},{"class":73,"line":332},10,[334],{"type":14,"tag":71,"props":335,"children":336},{"style":106},[337],{"type":20,"value":338},"}\n",{"type":14,"tag":22,"props":340,"children":341},{},[342,344,350,352,357,359,365,367,372],{"type":20,"value":343},"That's the whole integration: one ",{"type":14,"tag":37,"props":345,"children":347},{"className":346},[],[348],{"type":20,"value":349},"fetch",{"type":20,"value":351},", no auth, no SDK. Keep the request a plain ",{"type":14,"tag":37,"props":353,"children":355},{"className":354},[],[356],{"type":20,"value":42},{"type":20,"value":358},"\nwith no extra headers — see the ",{"type":14,"tag":28,"props":360,"children":362},{"href":361},"\u002Fdocs\u002Fmanifest#cors-wildcard-but-simple-requests-only",[363],{"type":20,"value":364},"CORS note",{"type":20,"value":366},"\non the manifest page for why. From here, plug ",{"type":14,"tag":37,"props":368,"children":370},{"className":369},[],[371],{"type":20,"value":146},{"type":20,"value":373}," into whatever you already use to\nrender lists — a \"what's new\" panel, a toast on first login after a new release, a public\nchangelog page — the manifest doesn't care who's reading it or how.",{"type":14,"tag":54,"props":375,"children":377},{"id":376},"where-django-i3version-fits-in",[378],{"type":20,"value":379},"Where django-i3version fits in",{"type":14,"tag":22,"props":381,"children":382},{},[383,395,397,403],{"type":14,"tag":28,"props":384,"children":388},{"href":385,"rel":386},"https:\u002F\u002Fgithub.com\u002Fsajlx\u002Fdjango-i3version",[387],"nofollow",[389],{"type":14,"tag":390,"props":391,"children":392},"strong",{},[393],{"type":20,"value":394},"django-i3version",{"type":20,"value":396}," is a separate, sibling\nopen-source project — a standalone, reusable Django app (",{"type":14,"tag":37,"props":398,"children":400},{"className":399},[],[401],{"type":20,"value":402},"pip install django-i3version",{"type":20,"value":404},") for\ntracking an application's own release versions and fanning out per-user \"there's a new version\"\nnotifications inside a Django project. It has its own models, its own API, and its own\nhistory — it does not import from or depend on i3deploy-back.",{"type":14,"tag":22,"props":406,"children":407},{},[408,410,415,417,423,425,430,432,437,439,445],{"type":20,"value":409},"To be precise about where things stand today: django-i3version is ",{"type":14,"tag":390,"props":411,"children":412},{},[413],{"type":20,"value":414},"not yet",{"type":20,"value":416}," a drop-in\nconsumer of the i3deploy public manifest documented on this page. It doesn't currently fetch\n",{"type":14,"tag":37,"props":418,"children":420},{"className":419},[],[421],{"type":20,"value":422},"releases.json",{"type":20,"value":424}," or render an i3deploy changelog out of the box. It's the sibling project that's\non track to become the ",{"type":14,"tag":390,"props":426,"children":427},{},[428],{"type":20,"value":429},"reference open-source client",{"type":20,"value":431}," for this manifest — a future version\nwould poll a project's ",{"type":14,"tag":37,"props":433,"children":435},{"className":434},[],[436],{"type":20,"value":422},{"type":20,"value":438}," and use it to drive the same per-user notification flow\nit already has for its own, locally-created ",{"type":14,"tag":37,"props":440,"children":442},{"className":441},[],[443],{"type":20,"value":444},"Version",{"type":20,"value":446}," rows. Until that lands, the two projects\nare related in intent (both model \"a release happened, tell people about it\") but not wired\ntogether in code.",{"type":14,"tag":22,"props":448,"children":449},{},[450,452,458,460,466,468,474],{"type":20,"value":451},"If you want release-tracking + per-user notifications inside your own Django project today,\ndjango-i3version works standalone against its own API — see its\n",{"type":14,"tag":28,"props":453,"children":455},{"href":385,"rel":454},[387],[456],{"type":20,"value":457},"README",{"type":20,"value":459}," for ",{"type":14,"tag":37,"props":461,"children":463},{"className":462},[],[464],{"type":20,"value":465},"INSTALLED_APPS",{"type":20,"value":467}," setup and the\npublisher API. If you want to consume i3deploy's manifest specifically, the ",{"type":14,"tag":28,"props":469,"children":471},{"href":470},"#a-minimal-consumer",[472],{"type":20,"value":473},"minimal\nsnippet above",{"type":20,"value":475}," is all you need — no need to wait for the two to converge.",{"type":14,"tag":54,"props":477,"children":479},{"id":478},"next",[480],{"type":20,"value":481},"Next",{"type":14,"tag":483,"props":484,"children":485},"ul",{},[486,497],{"type":14,"tag":487,"props":488,"children":489},"li",{},[490,495],{"type":14,"tag":28,"props":491,"children":492},{"href":30},[493],{"type":20,"value":494},"The public manifest",{"type":20,"value":496}," — the full JSON contract, CORS behavior, and a real\nsample response.",{"type":14,"tag":487,"props":498,"children":499},{},[500,506],{"type":14,"tag":28,"props":501,"children":503},{"href":502},"\u002Fdocs\u002Freleases",[504],{"type":20,"value":505},"Cutting releases",{"type":20,"value":507}," — how releases get published in the first place, on the\ni3deploy side.",{"type":14,"tag":509,"props":510,"children":511},"style",{},[512],{"type":20,"value":513},"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);}",{"title":7,"searchDepth":112,"depth":112,"links":515},[516,517,518],{"id":56,"depth":112,"text":59},{"id":376,"depth":112,"text":379},{"id":478,"depth":112,"text":481},"markdown","content:docs:clients.md","content","docs\u002Fclients.md","docs\u002Fclients","md",1784716250634]