mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(packaging): enforce private Snap memory semantics
This commit is contained in:
1 parent
4b2e60a0ff
commit
5b168866e4
2 files changed
+203
-23
No files matched your search
@@ -367,6 +367,54 @@ function singleYamlMappingEntry(lines, key, indent, start, end) {
|
||||
return entries.length === 1 ? entries[0] : null;
|
||||
}
|
||||
|
||||
function directYamlMappingEntries(lines, parent, indent) {
|
||||
const entries = [];
|
||||
for (
|
||||
let index = parent.index + 1;
|
||||
index < parent.end;
|
||||
index += 1
|
||||
) {
|
||||
const line = lines[index];
|
||||
if (!line.trim() || line.trimStart().startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
const lineIndent = line.length - line.trimStart().length;
|
||||
if (lineIndent !== indent) {
|
||||
continue;
|
||||
}
|
||||
const match = line
|
||||
.slice(indent)
|
||||
.match(/^([A-Za-z0-9][A-Za-z0-9_-]*):(?:\s*(.*))?$/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
let blockEnd = parent.end;
|
||||
for (
|
||||
let candidateIndex = index + 1;
|
||||
candidateIndex < parent.end;
|
||||
candidateIndex += 1
|
||||
) {
|
||||
const candidate = lines[candidateIndex];
|
||||
if (!candidate.trim() || candidate.trimStart().startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
const candidateIndent =
|
||||
candidate.length - candidate.trimStart().length;
|
||||
if (candidateIndent <= indent) {
|
||||
blockEnd = candidateIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
entries.push({
|
||||
key: match[1],
|
||||
value: match[2] ?? '',
|
||||
index,
|
||||
end: blockEnd,
|
||||
});
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
function yamlScalarEquals(value, expected) {
|
||||
return (
|
||||
value === expected ||
|
||||
@@ -380,17 +428,40 @@ function yamlSequenceIncludes(lines, entry, expected) {
|
||||
if (!entry.value.startsWith('[') || !entry.value.endsWith(']')) {
|
||||
return false;
|
||||
}
|
||||
return entry.value
|
||||
const values = entry.value
|
||||
.slice(1, -1)
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.some((value) => yamlScalarEquals(value, expected));
|
||||
}
|
||||
return lines
|
||||
.slice(entry.index + 1, entry.end)
|
||||
.some((line) =>
|
||||
yamlScalarEquals(line.trim().replace(/^-\s*/, ''), expected)
|
||||
.map((value) => value.trim());
|
||||
return (
|
||||
values.length > 0 &&
|
||||
values.every(
|
||||
(value) =>
|
||||
value.length > 0 &&
|
||||
!/[:[\]{}&*]/.test(value) &&
|
||||
!value.startsWith('-')
|
||||
) &&
|
||||
values.some((value) => yamlScalarEquals(value, expected))
|
||||
);
|
||||
}
|
||||
const sequenceLines = lines
|
||||
.slice(entry.index + 1, entry.end)
|
||||
.filter(
|
||||
(line) => line.trim() && !line.trimStart().startsWith('#')
|
||||
);
|
||||
const itemIndent = 6;
|
||||
const values = sequenceLines.map((line) => {
|
||||
const lineIndent = line.length - line.trimStart().length;
|
||||
if (lineIndent !== itemIndent) {
|
||||
return null;
|
||||
}
|
||||
const match = line.trim().match(/^-\s+([^:[\]{}&*]+)$/);
|
||||
return match?.[1].trim() ?? null;
|
||||
});
|
||||
return (
|
||||
values.length > 0 &&
|
||||
values.every((value) => value !== null) &&
|
||||
values.some((value) => yamlScalarEquals(value, expected))
|
||||
);
|
||||
}
|
||||
|
||||
export function validateExtractedSnapMetadata(extractionRoot) {
|
||||
@@ -443,20 +514,23 @@ export function validateExtractedSnapMetadata(extractionRoot) {
|
||||
'Extracted Snap metadata must declare exactly one top-level shared-memory plug.'
|
||||
);
|
||||
} else {
|
||||
const interfaceEntry = singleYamlMappingEntry(
|
||||
lines,
|
||||
'interface',
|
||||
4,
|
||||
sharedMemory.index + 1,
|
||||
sharedMemory.end
|
||||
);
|
||||
const privateEntry = singleYamlMappingEntry(
|
||||
lines,
|
||||
'private',
|
||||
4,
|
||||
sharedMemory.index + 1,
|
||||
sharedMemory.end
|
||||
);
|
||||
const fields = directYamlMappingEntries(lines, sharedMemory, 4);
|
||||
const interfaceEntries =
|
||||
fields?.filter(({ key }) => key === 'interface') ?? [];
|
||||
const privateEntries =
|
||||
fields?.filter(({ key }) => key === 'private') ?? [];
|
||||
const interfaceEntry = interfaceEntries[0];
|
||||
const privateEntry = privateEntries[0];
|
||||
if (
|
||||
!fields ||
|
||||
fields.length !== 2 ||
|
||||
interfaceEntries.length !== 1 ||
|
||||
privateEntries.length !== 1
|
||||
) {
|
||||
errors.push(
|
||||
'Extracted Snap private shared-memory plug must contain exactly interface and private.'
|
||||
);
|
||||
}
|
||||
if (
|
||||
!interfaceEntry ||
|
||||
!yamlScalarEquals(interfaceEntry.value, 'shared-memory')
|
||||
@@ -470,6 +544,68 @@ export function validateExtractedSnapMetadata(extractionRoot) {
|
||||
'Extracted Snap top-level shared-memory plug must declare private: true.'
|
||||
);
|
||||
}
|
||||
|
||||
const plugEntries = directYamlMappingEntries(lines, plugs, 2);
|
||||
const sharedMemoryInterfaces =
|
||||
plugEntries?.filter((plugEntry) => {
|
||||
const plugFields = directYamlMappingEntries(
|
||||
lines,
|
||||
plugEntry,
|
||||
4
|
||||
);
|
||||
const interfaceFields =
|
||||
plugFields?.filter(({ key }) => key === 'interface') ?? [];
|
||||
return (
|
||||
interfaceFields.length === 1 &&
|
||||
yamlScalarEquals(
|
||||
interfaceFields[0].value,
|
||||
'shared-memory'
|
||||
)
|
||||
);
|
||||
}) ?? [];
|
||||
if (
|
||||
!plugEntries ||
|
||||
sharedMemoryInterfaces.length !== 1 ||
|
||||
sharedMemoryInterfaces[0].key !== 'shared-memory'
|
||||
) {
|
||||
errors.push(
|
||||
'Extracted Snap metadata must declare exactly one plug with the shared-memory interface.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const slotsEntries = yamlMappingEntries(
|
||||
lines,
|
||||
'slots',
|
||||
0,
|
||||
0,
|
||||
lines.length
|
||||
);
|
||||
let hasSharedMemorySlot = slotsEntries.length > 1;
|
||||
for (const slots of slotsEntries) {
|
||||
const slotEntries = directYamlMappingEntries(lines, slots, 2);
|
||||
if (!slotEntries) {
|
||||
hasSharedMemorySlot = true;
|
||||
continue;
|
||||
}
|
||||
hasSharedMemorySlot ||= slotEntries.some((slotEntry) => {
|
||||
if (slotEntry.key === 'shared-memory') {
|
||||
return true;
|
||||
}
|
||||
const slotFields = directYamlMappingEntries(lines, slotEntry, 4);
|
||||
return (
|
||||
slotFields?.some(
|
||||
({ key, value }) =>
|
||||
key === 'interface' &&
|
||||
yamlScalarEquals(value, 'shared-memory')
|
||||
) ?? false
|
||||
);
|
||||
});
|
||||
}
|
||||
if (hasSharedMemorySlot) {
|
||||
errors.push(
|
||||
'Extracted Snap metadata must not declare a shared-memory slot.'
|
||||
);
|
||||
}
|
||||
|
||||
const apps = singleYamlMappingEntry(lines, 'apps', 0, 0, lines.length);
|
||||
@@ -500,7 +636,7 @@ export function validateExtractedSnapMetadata(extractionRoot) {
|
||||
!yamlSequenceIncludes(lines, appPlugs, 'shared-memory')
|
||||
) {
|
||||
errors.push(
|
||||
'Extracted Snap iptvnator app must use the shared-memory plug.'
|
||||
'Extracted Snap iptvnator app scalar sequence must contain the shared-memory plug.'
|
||||
);
|
||||
}
|
||||
return errors;
|
||||
|
||||
@@ -611,6 +611,50 @@ test('requires a private top-level shared-memory plug used by the Snap app', ()
|
||||
),
|
||||
/top-level shared-memory plug/i,
|
||||
],
|
||||
[
|
||||
(contents) =>
|
||||
contents.replace(
|
||||
' private: true\n',
|
||||
' private: true\n shared-memory: named-area\n'
|
||||
),
|
||||
/private shared-memory plug.*exactly interface and private/i,
|
||||
],
|
||||
[
|
||||
(contents) =>
|
||||
contents.replace(
|
||||
'\nplugs:\n',
|
||||
[
|
||||
'',
|
||||
'plugs:',
|
||||
' second-shared-memory:',
|
||||
' interface: shared-memory',
|
||||
' private: true',
|
||||
'',
|
||||
].join('\n')
|
||||
),
|
||||
/exactly one plug.*shared-memory interface/i,
|
||||
],
|
||||
[
|
||||
(contents) =>
|
||||
`${contents}slots:\n shared-memory:\n interface: shared-memory\n`,
|
||||
/must not declare.*shared-memory slot/i,
|
||||
],
|
||||
[
|
||||
(contents) =>
|
||||
contents.replace(
|
||||
[
|
||||
' plugs:',
|
||||
' - desktop',
|
||||
' - shared-memory',
|
||||
].join('\n'),
|
||||
[
|
||||
' plugs:',
|
||||
' nested:',
|
||||
' - shared-memory',
|
||||
].join('\n')
|
||||
),
|
||||
/scalar sequence.*shared-memory/i,
|
||||
],
|
||||
]) {
|
||||
fs.writeFileSync(snapYamlPath, mutate(validSnapYaml));
|
||||
assert.match(
|
||||
|
||||
Reference in new issue
Block a user