Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changeset/whole-pens-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
"@rebilly/client-php": patch
---
23 changes: 8 additions & 15 deletions .github/workflows/update.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,6 @@ jobs:
-v "$PWD/api-definitions:/adef" \
tufin/oasdiff:v1.18.1 \
changelog /sdk/.openapi-snapshot.yaml /adef/catalog/all.yaml -f json > /tmp/spec-diff.json
counts=$(docker run --rm \
-v "$PWD/sdk:/sdk" \
-v "$PWD/api-definitions:/adef" \
tufin/oasdiff:v1.18.1 \
changelog /sdk/.openapi-snapshot.yaml /adef/catalog/all.yaml -f singleline 2>/dev/null | head -1)
echo "summary=$counts" >> $GITHUB_OUTPUT
if echo "$counts" | grep -qE "[1-9][0-9]* error"; then echo "bump=major" >> $GITHUB_OUTPUT
elif echo "$counts" | grep -qE "[1-9][0-9]* warning"; then echo "bump=minor" >> $GITHUB_OUTPUT
else echo "bump=patch" >> $GITHUB_OUTPUT
fi
- name: Summarize spec diff
id: summarize
Expand Down Expand Up @@ -149,14 +139,17 @@ jobs:
working-directory: ./sdk
run: cp ../api-definitions/catalog/all.yaml .openapi-snapshot.yaml

- name: Add changeset
- name: Write spec-diff changeset
if: steps.summarize.outputs.has_content == 'true'
working-directory: ./sdk
run: |
npm ci
node ./scripts/add-changeset.js ${{ steps.oasdiff.outputs.bump }} "$(cat /tmp/changeset-body.md)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove add-changeset.js file and @changesets/write from package.json?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good call, removed in 17ad7a8

env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
{
echo '---'
echo '"@rebilly/client-php": patch'
echo '---'
echo ''
cat /tmp/changeset-body.md
} > .changeset/spec-diff.md
- name: Create PR
id: cpr
Expand Down
121 changes: 89 additions & 32 deletions scripts/summarize-spec-diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,22 +209,53 @@ function suppressCompositionNoise(data) {
return kept;
}

function renderOne(verb, side, leafName, schemas, breaking, endpointCount, examples, bucket) {
const sidePart = side ? ` ${side} property` : '';
function renderOne(verb, sides, leafName, schemas, breaking, bucket) {
const schemaPart = schemas.size ? ` on \`${[...schemas].sort().join(', ')}\`` : '';
const bk = breaking ? ' **(breaking)**' : '';
let endpointPart;
if (endpointCount === 1) {
endpointPart = examples.length ? ` — ${examples[0]}` : '';
} else {
const sample = examples.slice(0, 2).join(', ');
const more = endpointCount > 2 ? ` + ${endpointCount - 2} more` : '';
endpointPart = examples.length ? ` — ${sample}${more}` : ` (${endpointCount} endpoints)`;
}
if (bucket === 'endpoint') {
return `- ${verb}${endpointPart}${bk}`;
return `- ${verb} \`${leafName}\`${bk}`;
}
let sideHint = '';
if (sides.size === 1) {
const only = [...sides][0];
if (only) sideHint = ` (${only} only)`;
}
return `- ${verb}${sidePart} \`${leafName}\`${schemaPart}${endpointPart}${bk}`;
return `- ${verb} \`${leafName}\`${schemaPart}${sideHint}${bk}`;
}

const OTHER_VERBS = {
'min-decreased': 'Lowered minimum on',
'min-increased': 'Raised minimum on',
'max-decreased': 'Lowered maximum on',
'max-increased': 'Raised maximum on',
'min-length-decreased': 'Shortened minimum length on',
'min-length-increased': 'Raised minimum length on',
'max-length-decreased': 'Shortened maximum length on',
'max-length-increased': 'Raised maximum length on',
'discriminator-mapping-added': 'Added discriminator mapping for',
'discriminator-mapping-deleted': 'Removed discriminator mapping for',
'discriminator-mapping-changed': 'Changed discriminator mapping for',
'all-of-added': 'Composed schema with',
'all-of-removed': 'Uncomposed schema from',
'any-of-added': 'Added union member to',
'any-of-removed': 'Removed union member from',
'one-of-added': 'Added discriminator variant to',
'one-of-removed': 'Removed discriminator variant from',
'became-nullable': 'Made nullable',
'became-not-nullable': 'Made not nullable',
'default-value-added': 'Default added on',
'default-value-removed': 'Default removed on',
'min-items-decreased': 'Lowered minItems on',
'max-items-decreased': 'Lowered maxItems on',
'min-items-increased': 'Raised minItems on',
'max-items-increased': 'Raised maxItems on',
};

function otherChangeFamily(id) {
// Strip request/response/-body-/-property- prefixes and discriminator wrappers.
return id
.replace(/^(request|response)(-body|-property)?-/, '')
.replace(/^(request|response)-/, '');
}

function recordEndpoint(rec, ep) {
Expand Down Expand Up @@ -252,15 +283,15 @@ function main() {
for (const it of data) {
if (ENUM_IDS.has(it.id)) {
const [value, prop] = enumValueAndProp(it.text);
const verb = it.id.includes('added') ? 'Enum value added' : 'Enum value removed';
const verb = it.id.includes('added') ? 'added' : 'removed';
const side = it.id.includes('request') ? 'request' : 'response';
const key = `${verb}\x1f${side}\x1f${leaf(prop) || '?'}`;
const key = `${verb}\x1f${leaf(prop) || '?'}`;
const rec = getOrCreate(enumRows, key, () => ({
verb, side, prop: leaf(prop) || '?',
values: [], endpoints: new Set(), examples: [], breaking: false, schemas: new Set(),
verb, prop: leaf(prop) || '?',
values: [], sides: new Set(), breaking: false, schemas: new Set(),
}));
if (value && !rec.values.includes(value)) rec.values.push(value);
recordEndpoint(rec, endpointOf(it));
rec.sides.add(side);
const sch = deepestSchema(it.text);
if (sch) rec.schemas.add(sch);
if ((it.level || 1) >= 3) rec.breaking = true;
Expand All @@ -277,21 +308,33 @@ function main() {
}
const rule = RULES.get(it.id);
if (!rule) {
const rec = getOrCreate(unknown, it.id, () => []);
rec.push(it);
const prop = firstProp(it.text) || '';
const leafName = leaf(prop);
const family = otherChangeFamily(it.id);
const key = `${family}\x1f${leafName || '?'}`;
const rec = getOrCreate(unknown, key, () => ({
family, leafName,
sides: new Set(), schemas: new Set(), items: [],
}));
rec.sides.add(it.id.includes('request') ? 'request' : 'response');
const sch = deepestSchema(it.text);
if (sch) rec.schemas.add(sch);
rec.items.push(it);
continue;
}
const [verb, side, bucket] = rule;
const prop = firstProp(it.text) || '';
const leafName = leaf(prop) || it.text.slice(0, 50);
const schema = deepestSchema(it.text);
const key = `${bucket}\x1f${verb}\x1f${leafName}\x1f${side || ''}`;
// Strip the request/response split: collapse same logical change.
const baseVerb = verb.replace(/^Added (required|optional|union member|discriminator variant|parameter)$/, 'Added').replace(/^Removed (required|optional|union member|discriminator variant|parameter)$/, 'Removed');
const key = `${bucket}\x1f${baseVerb}\x1f${leafName}`;
const rec = getOrCreate(grouped, key, () => ({
bucket, verb, leafName, side,
schemas: new Set(), endpoints: new Set(), examples: [], breaking: false,
bucket, verb: baseVerb, leafName,
schemas: new Set(), sides: new Set(), breaking: false,
}));
if (schema) rec.schemas.add(schema);
recordEndpoint(rec, endpointOf(it));
if (side) rec.sides.add(side);
if ((it.level || 1) >= 3) rec.breaking = true;
}

Expand All @@ -312,23 +355,24 @@ function main() {
return byCodePoint(a.leafName, b.leafName);
});
out.push(sections[bucket]);
for (const r of rows) out.push(renderOne(r.verb, r.side, r.leafName, r.schemas, r.breaking, r.endpoints.size, r.examples, bucket));
for (const r of rows) out.push(renderOne(r.verb, r.sides, r.leafName, r.schemas, r.breaking, bucket));
out.push('');
}

if (enumRows.size) {
out.push('### Enum changes');
const rows = [...enumRows.values()].sort((a, b) =>
byCodePoint(a.verb + a.side + a.prop, b.verb + b.side + b.prop)
byCodePoint(a.verb + a.prop, b.verb + b.prop)
);
for (const r of rows) {
const schemasS = r.schemas.size ? ` on \`${[...r.schemas].sort().join(', ')}\`` : '';
const bk = r.breaking ? ' **(breaking)**' : '';
const sampleV = r.values.slice(0, 6).map(v => `\`${v}\``).join(', ');
const moreV = r.values.length > 6 ? ` + ${r.values.length - 6} more` : '';
const sampleEp = r.examples.slice(0, 2).join(', ');
const moreEp = r.endpoints.size > 2 ? ` + ${r.endpoints.size - 2} more` : '';
out.push(`- ${r.verb} on ${r.side} \`${r.prop}\`${schemasS}: ${sampleV}${moreV} — ${sampleEp}${moreEp}${bk}`);
const verbPhrase = r.verb === 'added' ? 'accepts new values' : 'no longer accepts';
let sideHint = '';
if (r.sides.size === 1) sideHint = ` (${[...r.sides][0]} only)`;
out.push(`- \`${r.prop}\`${schemasS}${sideHint} ${verbPhrase}: ${sampleV}${moreV}${bk}`);
}
out.push('');
}
Expand All @@ -350,10 +394,23 @@ function main() {

if (unknown.size) {
out.push('### Other');
const entries = [...unknown.entries()].sort((a, b) => b[1].length - a[1].length).slice(0, 10);
for (const [changeId, items] of entries) {
const ex = items[0].text.slice(0, 120);
out.push(`- ${changeId} (${items.length}x) — e.g. ${ex}`);
const rows = [...unknown.values()].sort((a, b) =>
byCodePoint((a.family || '') + (a.leafName || ''), (b.family || '') + (b.leafName || ''))
);
for (const r of rows) {
const verb = OTHER_VERBS[r.family];
const schemaPart = r.schemas.size ? ` on \`${[...r.schemas].sort().join(', ')}\`` : '';
let sideHint = '';
if (r.sides.size === 1) sideHint = ` (${[...r.sides][0]} only)`;
if (verb && r.leafName) {
out.push(`- ${verb} \`${r.leafName}\`${schemaPart}${sideHint}`);
} else if (r.leafName) {
out.push(`- ${r.family} on \`${r.leafName}\`${schemaPart}${sideHint}`);
} else {
// Fallback for changes without a clear property path.
const ex = r.items[0].text.slice(0, 120);
out.push(`- ${r.family} (${r.items.length}x) — e.g. ${ex}`);
}
}
out.push('');
}
Expand Down
Loading