File size: 42,330 Bytes
7838fee
3d12e0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7838fee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Pubspec Publish check — check your package before publishing</title>
<meta name="description" content="This free browser tool checks pubspec.yaml for pub.dev publishing issues and shows the affected entries.">
<style>
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
</style>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/pubspec-publish-gate"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static!important;justify-content:flex-end;margin:0 0 10px}h1,h2,h3,h4,p,li,dd,dt,blockquote,figcaption,summary,label,.res,.lead{overflow-wrap:anywhere}label{max-width:100%;box-sizing:border-box}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style>
</head>
<body>
<div class="wrap">
<h1>Pubspec Publish check — check your package before publishing</h1>
<p class="lede">Checks pubspec.yaml before dart pub publish: path and git dependencies pub.dev refuses, dependency_overrides your users never get, invalid name/version, description outside 60-180 chars. Runs entirely in your browser — nothing is uploaded.</p>
<textarea id="src" spellcheck="false">name: Geo-Helpers
description: Geo helpers.
version: 1.4
repository: https://github.com/example/geo_helpers

environment:
  sdk: ^3.4.0

dependencies:
  flutter:
    sdk: flutter
  geo_core:
    path: ../geo_core
  tile_math:
    git:
      url: https://github.com/example/tile_math.git
      ref: main
  collection: ^1.18.0

dev_dependencies:
  lints: ^4.0.0

dependency_overrides:
  collection: 1.17.2</textarea>
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
<div id="out" aria-live="polite"></div>
<div id="paid"><strong>Free here:</strong> Maps every pubspec.yaml line that pub.dev will refuse or silently ignore, and prints the exact replacement line for each one - no key needed.. <strong>In the editor:</strong> Dated pre-publish report for every pubspec.yaml in a workspace, plus a CI exit-code gate that stops a bad version before it becomes permanent on pub.dev. — that part asks for a licence key.</div>
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
</div>
<script>var PUB_RULES = [
 {"id": "path-dependency", "sev": "error", "title": "path dependency blocks upload", "fix": "Replace `path:` with a hosted constraint, e.g. `my_pkg: ^1.2.0`, after publishing my_pkg first.", "source": "https://dart.dev/tools/pub/dependencies#path-packages"},
 {"id": "git-dependency", "sev": "error", "title": "git dependency blocks upload", "fix": "Replace `git:` with a hosted constraint from pub.dev, e.g. `my_pkg: ^1.2.0`.", "source": "https://dart.dev/tools/pub/dependencies#git-packages"},
 {"id": "dependency-overrides", "sev": "warn", "title": "dependency_overrides are ignored by every user of the package", "fix": "Delete the dependency_overrides block before publishing and widen the real constraint under dependencies instead.", "source": "https://dart.dev/tools/pub/dependencies#dependency-overrides"},
 {"id": "publish-to-none", "sev": "warn", "title": "publish_to: none prevents publication", "fix": "Remove `publish_to: none` if this package is meant for pub.dev.", "source": "https://dart.dev/tools/pub/pubspec#publish_to"},
 {"id": "name-invalid", "sev": "error", "title": "package name must be [a-z0-9_] and not start with a digit", "fix": "Rename to lowercase with underscores, e.g. `geo_helpers`.", "source": "https://dart.dev/tools/pub/pubspec#name"},
 {"id": "version-format", "sev": "error", "title": "version must be three numbers separated by dots", "fix": "Write a full semantic version, e.g. `version: 1.4.0`.", "source": "https://dart.dev/tools/pub/pubspec#version"},
 {"id": "description-length", "sev": "warn", "title": "description should be 60 to 180 characters", "fix": "Write one plain-text sentence of 60-180 characters saying what the package does.", "source": "https://dart.dev/tools/pub/pubspec#description"},
 {"id": "sdk-constraint-missing", "sev": "error", "title": "environment: sdk constraint is required", "fix": "Add `environment:` with `  sdk: ^3.0.0` (or your real minimum).", "source": "https://dart.dev/tools/pub/pubspec#sdk-constraints"},
 {"id": "repository-missing", "sev": "info", "title": "no homepage or repository", "fix": "Add `repository: https://github.com/\u003cyou\u003e/\u003cpkg\u003e` so pub.dev links the source and issue tracker.", "source": "https://dart.dev/tools/pub/pubspec#repository"},
 {"id": "topics-invalid", "sev": "warn", "title": "topics: at most 5, each 2-32 chars, lowercase letters, digits and single hyphens, starting with a letter", "fix": "Keep 5 topics or fewer, e.g. `topics: [maps, geolocation]`.", "source": "https://dart.dev/tools/pub/pubspec#topics"}
]
;</script>
<script>// pubspec Publish Gate engine: reads a pubspec.yaml as text (no YAML library) and
// reports the lines pub.dev refuses at upload or silently ignores for your users.
(function () {
  const RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.PUB_RULES;
  const BY_ID = {};
  RULES.forEach(r => { BY_ID[r.id] = r; });
  const DEP_SECTIONS = ['dependencies', 'dev_dependencies', 'dependency_overrides'];

  function unquote(s) {
    s = s.replace(/\s+#.*$/, '').trim();
    if ((s[0] === '"' && s.endsWith('"')) || (s[0] === "'" && s.endsWith("'"))) s = s.slice(1, -1);
    return s;
  }
  function indentOf(l) { return l.match(/^ */)[0].length; }

  function parse(text) {
    const lines = String(text || '').replace(/\r\n?/g, '\n').split('\n');
    const top = {};          // key -> {line, value, block:[lines]}
    const deps = [];         // {section, name, line, kind, raw}
    let cur = null, curDep = null;
    lines.forEach((raw, i) => {
      const ln = i + 1;
      if (/^\s*(#|$)/.test(raw)) { if (cur) cur.block.push(''); return; }
      const ind = indentOf(raw);
      const body = raw.trim();
      if (ind === 0) {
        const m = body.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/);
        cur = m ? { key: m[1], line: ln, value: m[2], block: [] } : null;
        if (cur) top[cur.key] = cur;
        curDep = null;
        return;
      }
      if (!cur) return;
      cur.block.push(body);
      if (DEP_SECTIONS.indexOf(cur.key) >= 0) {
        const m = body.match(/^([A-Za-z0-9_]+)\s*:\s*(.*)$/);
        if (m && (curDep === null || ind <= curDep.ind)) {
          curDep = { section: cur.key, name: m[1], line: ln, ind: ind, kind: 'hosted', raw: body };
          const inline = m[2];
          if (/\bpath\s*:/.test(inline)) curDep.kind = 'path';
          else if (/\bgit\s*:/.test(inline)) curDep.kind = 'git';
          else if (/^\s*sdk\s*:/.test(inline) || /\bsdk\s*:/.test(inline)) curDep.kind = 'sdk';
          deps.push(curDep);
        } else if (curDep && ind > curDep.ind) {
          if (/^path\s*:/.test(body)) { curDep.kind = 'path'; curDep.line = ln; curDep.raw = body; }
          else if (/^git\s*:/.test(body)) { curDep.kind = 'git'; curDep.line = ln; curDep.raw = body; }
          else if (/^sdk\s*:/.test(body) && curDep.kind === 'hosted') curDep.kind = 'sdk';
        }
      }
    });
    return { top, deps };
  }

  // Scalar value that may be folded over several lines (">-", "|", or plain continuation).
  function scalar(entry) {
    if (!entry) return null;
    let v = entry.value.trim();
    if (/^[>|][-+]?\s*(#.*)?$/.test(v)) v = entry.block.join(' ');
    else v = [v].concat(entry.block).join(' ');
    return unquote(v.replace(/\s+/g, ' ').trim());
  }

  function check(text, opts) {
    const { top, deps } = parse(text);
    const findings = [];
    const add = (id, line, msg) => {
      const r = BY_ID[id];
      findings.push({ check: id, sev: r.sev, msg: msg + ' Fix: ' + r.fix, line: line });
    };
    if (!top.name && !top.version && !top.dependencies && !top.environment) return { findings };

    const pubTo = top.publish_to ? unquote(top.publish_to.value) : '';
    if (pubTo === 'none') add('publish-to-none', top.publish_to.line, 'publish_to: none - dart pub publish will not upload this package.');

    if (top.name) {
      const n = unquote(top.name.value);
      if (!/^[a-z_][a-z0-9_]*$/.test(n)) add('name-invalid', top.name.line, 'name "' + n + '" is not lowercase [a-z0-9_] starting with a letter or underscore.');
    }
    if (top.version) {
      const v = unquote(top.version.value);
      if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/.test(v)) add('version-format', top.version.line, 'version "' + v + '" is not three dot-separated numbers.');
    }
    if (pubTo !== 'none') {
      const d = scalar(top.description);
      if (d === null || d === '') add('description-length', 1, 'No description - pub.dev asks for 60 to 180 characters.');
      else if (d.length < 60 || d.length > 180) add('description-length', top.description.line, 'description is ' + d.length + ' characters; dart.dev asks for 60 to 180.');
      if (!top.homepage && !top.repository) add('repository-missing', 1, 'Neither homepage nor repository is set.');
    }
    const env = top.environment;
    if (!env || !env.block.some(b => /^sdk\s*:/.test(b))) add('sdk-constraint-missing', env ? env.line : 1, 'No environment: sdk constraint.');

    if (pubTo !== 'none') deps.forEach(dep => {
      if (dep.section === 'dependency_overrides') return;
      if (dep.kind === 'path') add('path-dependency', dep.line, dep.section + ' ' + dep.name + ' uses path: - pub.dev refuses any path dependency.');
      if (dep.kind === 'git' && dep.section === 'dependencies') add('git-dependency', dep.line, 'dependencies ' + dep.name + ' uses git: - pub.dev refuses git dependencies.');
    });
    if (top.dependency_overrides && pubTo !== 'none') {
      const ov = deps.filter(d => d.section === 'dependency_overrides').map(d => d.name);
      add('dependency-overrides', top.dependency_overrides.line, 'dependency_overrides (' + (ov.join(', ') || 'empty') + ') apply only to you; every user of the published package resolves without them.');
    }
    if (top.topics) {
      let t = top.topics.value.trim();
      let list = /^\[/.test(t) ? t.replace(/[\[\]]/g, '').split(',') : top.topics.block.filter(b => /^-/.test(b)).map(b => b.slice(1));
      list = list.map(unquote).filter(Boolean);
      const bad = list.filter(x => !/^[a-z](?:[a-z0-9]|-(?!-))*[a-z0-9]$/.test(x) || x.length < 2 || x.length > 32);
      if (list.length > 5 || bad.length) add('topics-invalid', top.topics.line, list.length + ' topics' + (bad.length ? ', invalid: ' + bad.join(', ') : '') + ' (limit 5).');
    }
    findings.sort((a, b) => a.line - b.line);
    return { findings };
  }

  const API = { engine: { check }, RULES, RULE_COUNT: RULES.length };
  if (typeof module !== 'undefined' && module.exports) module.exports = API;
  if (typeof window !== 'undefined') window.PUBGATE = API;
})();
</script>
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
'use strict';
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }
function summaryLine(res) {
  const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
  return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
}
function toText(res, label) {
  const f = (res && res.findings) || [];
  const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
  f.forEach(function (x) { lines.push('  ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + '  ' + (x.line ? 'L' + x.line + '  ' : '') + (x.check || x.id || '') + '  ' + (x.msg || x.message || '')); });
  return lines.join('\n');
}
function toHtml(res) {
  const f = (res && res.findings) || [];
  if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
  const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
  return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
var PUBREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
if (typeof module !== 'undefined' && module.exports) module.exports = PUBREPORT;
else window.PUBREPORT = PUBREPORT;
</script>
<script>
(function(){
  var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
  today.value=new Date().toISOString().slice(0,10);
  function run(){ try { var res=window.PUBGATE.engine.check(src.value,{today:today.value}); out.innerHTML=window.PUBREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
  go.onclick=run; if (src.value.trim()) run();
})();
</script>


<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://open-vsx.org/extension/readystack/pubspec-publish-gate" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'pubspec-publish-gate',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind pubspec Publish Gate - pub.dev pre-publish check every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"pubspec-publish-gate",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},true);})();</script></section>

<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">Dated pre-publish report for every pubspec.yaml in a workspace, plus a CI exit-code gate that stops a bad version before it becomes permanent on pub.dev.</p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_IhVfFhiVs73PkDTXwRi0KtJl170ibyq2wCMHd2n01Vu" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>

<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-10-01">Updated 2026-10-01</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-10-01","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku439548_result_card.jpg" alt="pubspec Publish Gate - pub.dev pre-publish check" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>pubspec publish: 6 findings in one 24-line pubspec.yaml - that is what a Dart or Flutter package author gets from the example file before running dart pub publish, and every one of the six is a line that works on the author&#x27;s own machine.</p>
<p>The reason this matters is written on dart.dev: &quot;a published package lasts forever&quot;, and the pub.dev policy &quot;disallows unpublishing packages except for very few cases&quot;. A version can be retracted only within 7 days of publication, and a retracted version is still visible with a RETRACTED badge. So the useful moment to read a pubspec.yaml is before the upload, not after.</p>
<p>Here is the example file, a small geo package developed next to two sibling packages:</p>
<p>name: Geo-Helpers description: Geo helpers. version: 1.4 dependencies: geo_core: path: ../geo_core tile_math: git: url: https://github.com/example/tile_math.git dependency_overrides: collection: 1.17.2</p>
<p>pubspec Publish Gate reads it as text and reports six findings, each with its line and the replacement line:</p>
<ol><li>name: Geo-Helpers - dart.dev says package names are lowercase [a-z0-9_] and do not start with a digit. Fix: name: geo_helpers.</li><li>description: Geo helpers. - 12 characters. dart.dev asks for 60 to 180 characters of plain text. Fix: one sentence saying what the package does.</li><li>version: 1.4 - a version is three numbers separated by dots. Fix: version: 1.4.0.</li><li>geo_core with path: ../geo_core - &quot;you cannot upload a package to the pub.dev site if it has any path dependencies in its pubspec.&quot; Fix: publish geo_core first and depend on a hosted constraint.</li><li>tile_math with git: - &quot;Git dependencies are not allowed as dependencies for packages uploaded to pub.dev.&quot; Fix: a hosted constraint from pub.dev.</li><li>dependency_overrides with collection: 1.17.2 - this one is the quiet one. It is legal, the resolver accepts it and your tests pass with it. But dart.dev: &quot;your package&#x27;s dependency overrides are ignored by all users of your package.&quot; Whatever the override was fixing, it is not fixed for anyone who adds your package. Fix: delete the block and widen the real constraint under dependencies.</li></ol>
<p>The full rule set is 10 rules, all from the dart.dev pubspec, dependencies and publishing pages: path-dependency, git-dependency, dependency-overrides, publish-to-none, name-invalid, version-format, description-length, sdk-constraint-missing, repository-missing and topics-invalid (at most 5 topics, 2-32 characters).</p>
<p>Three runs of the same engine show that the answer follows the file. The example gives 6 findings. A tile_math package with seven topics and no environment block gives 2: topics-invalid and sdk-constraint-missing. The clean geo_helpers pubspec with a folded description, hosted dependencies and sdk: ^3.4.0 gives 0.</p>
<p>Why not just run dart pub publish and read what comes back? You should, as the last step. But that check happens at the moment of upload, and dependency_overrides are not an upload problem - they are a problem for the people who install the package later.</p>
<p>The extension checks every pubspec.yaml in the workspace on open and on save. The same engine runs on a free web page where you paste a pubspec.yaml and read the same findings; nothing is uploaded. Every finding and fix is free with no key. The full version adds a dated pre-publish report for every pubspec.yaml in a workspace and a CI exit-code gate for release jobs.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku439548_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku439548_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does pubspec Publish Gate check in a pubspec.yaml?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It checks every pubspec.yaml against 10 rules from the dart.dev pubspec, dependencies and publishing pages: path dependencies, git dependencies, dependency_overrides, publish_to: none, package name, version format, description length, the environment sdk constraint, homepage or repository, and topics. On the example file it reports 6 findings, each with its line number and the exact replacement line.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is pubspec Publish Gate for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Dart and Flutter package authors who are about to run dart pub publish, especially maintainers who develop several packages side by side with path or git dependencies and dependency_overrides. Those lines work on their own machine, but pub.dev refuses the first two at upload and ignores the overrides for every user who adds the package.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just run dart pub publish and see what fails?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A version uploaded to pub.dev is permanent; dart.dev says it can be retracted only within 7 days and stays visible with a RETRACTED badge. dart pub publish checks at the moment of upload, while this extension flags the lines while you edit, including dependency_overrides, which are legal locally but ignored by all users of the published package.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what does the full version add?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free, with no key: every finding with its line and replacement, in VS Code and on the web page, for the 10 rules; the example shows 6 findings: name, description, version, path dependency, git dependency and dependency_overrides. The full version adds a dated pre-publish report for every pubspec.yaml in a workspace and a CI exit-code gate for release jobs.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does a wrong pub.dev release cost compared with checking first?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The cost is a version you cannot delete. pub.dev policy disallows unpublishing except in very few cases, and retraction only works within 7 days of publication, so users can already depend on the broken version. Checking the pubspec.yaml before upload takes seconds, runs locally, and uploads nothing.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does pubspec Publish Gate check in a pubspec.yaml?", "acceptedAnswer": {"@type": "Answer", "text": "It checks every pubspec.yaml against 10 rules from the dart.dev pubspec, dependencies and publishing pages: path dependencies, git dependencies, dependency_overrides, publish_to: none, package name, version format, description length, the environment sdk constraint, homepage or repository, and topics. On the example file it reports 6 findings, each with its line number and the exact replacement line."}}, {"@type": "Question", "name": "Who is pubspec Publish Gate for?", "acceptedAnswer": {"@type": "Answer", "text": "Dart and Flutter package authors who are about to run dart pub publish, especially maintainers who develop several packages side by side with path or git dependencies and dependency_overrides. Those lines work on their own machine, but pub.dev refuses the first two at upload and ignores the overrides for every user who adds the package."}}, {"@type": "Question", "name": "Why not just run dart pub publish and see what fails?", "acceptedAnswer": {"@type": "Answer", "text": "A version uploaded to pub.dev is permanent; dart.dev says it can be retracted only within 7 days and stays visible with a RETRACTED badge. dart pub publish checks at the moment of upload, while this extension flags the lines while you edit, including dependency_overrides, which are legal locally but ignored by all users of the published package."}}, {"@type": "Question", "name": "What is free and what does the full version add?", "acceptedAnswer": {"@type": "Answer", "text": "Free, with no key: every finding with its line and replacement, in VS Code and on the web page, for the 10 rules; the example shows 6 findings: name, description, version, path dependency, git dependency and dependency_overrides. The full version adds a dated pre-publish report for every pubspec.yaml in a workspace and a CI exit-code gate for release jobs."}}, {"@type": "Question", "name": "What does a wrong pub.dev release cost compared with checking first?", "acceptedAnswer": {"@type": "Answer", "text": "The cost is a version you cannot delete. pub.dev policy disallows unpublishing except in very few cases, and retraction only works within 7 days of publication, so users can already depend on the broken version. Checking the pubspec.yaml before upload takes seconds, runs locally, and uploads nothing."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:439548,slug:"pubspec-publish-gate",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/package-json-release-gate"><span class="yj-rel-t">npm publish package.json gate: engines &amp; SPDX</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cargo-publish-gate"><span class="yj-rel-t">Cargo.toml Publish Gate - crates.io</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/maven-central-publish-gate"><span class="yj-rel-t">Maven Central Publish Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/open-vsx-publish-gate"><span class="yj-rel-t">Open VSX Publish Gate - vsce &amp; ovsx preflight</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/wp-plugin-review-gate"><span class="yj-rel-t">WP Plugin Review Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/chart-yaml-release-gate"><span class="yj-rel-t">helm chart lint Chart.yaml - Helm 4 Release Gate</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
</html>