Compare commits
6
Commits
31f2629ddf
..
sdlc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53cd5c36e0 | ||
|
|
46f334fe96 | ||
|
|
13ac9958f3 | ||
|
|
073d8f8f37 | ||
|
|
2def1ef921 | ||
|
|
4f1c2a0ad1 |
@@ -16,3 +16,6 @@ lib/zxcvbn-php-master/vendor/sebastian/
|
||||
notes/
|
||||
docs/
|
||||
SESSION.php
|
||||
|
||||
# SDLC delivery zip is packaged by hand, outside the repo
|
||||
sdlc-delivery.zip
|
||||
|
||||
@@ -32,7 +32,7 @@ while IFS= read -r -d '' source; do
|
||||
destination="$STAGING_DIR/${relative%.md}.pdf"
|
||||
echo "Rendering ${relative%.md}.pdf"
|
||||
node "$TOOL_DIR/render-sdlc.mjs" "$source" "$destination"
|
||||
done < <(find "$SOURCE_DIR" -type f -name '*.md' -print0 | sort -z)
|
||||
done < <(find "$SOURCE_DIR" -mindepth 2 -type f -name '*.md' -print0 | sort -z)
|
||||
|
||||
# Every .pdf gets a Word copy beside it, reconstructed from the rendered page.
|
||||
# The CI header bands need Playwright, so they are rendered here with node and
|
||||
|
||||
@@ -104,6 +104,7 @@ table:not(.document-control) td.a-center { text-align: center; }
|
||||
taking an equal share of the table width. */
|
||||
table:not(.document-control) th.tight,
|
||||
table:not(.document-control) td.tight { white-space: nowrap; width: 1%; }
|
||||
table:not(.document-control) tr.group td { background: var(--brn-tint); font-weight: 600; }
|
||||
|
||||
thead { display: table-header-group; }
|
||||
tr { break-inside: avoid; }
|
||||
|
||||
@@ -18,12 +18,13 @@ const SCALE = 3; // the band is drawn at 3x so it stays crisp in print
|
||||
const applyTemplate = (template, values) => template.replace(/{{(\w+)}}/g, (_, key) => values[key] ?? '');
|
||||
const slug = (title) => title.replace(/[^\w-]+/g, '-').replace(/^-|-$/g, '').toLowerCase() || 'band';
|
||||
|
||||
async function markdownFiles(directory) {
|
||||
// Work products sit in the process folders; top-level files in sdlc/ are notes.
|
||||
async function markdownFiles(directory, depth = 0) {
|
||||
const entries = await fs.readdir(directory, { withFileTypes: true });
|
||||
const results = await Promise.all(entries.map(async (entry) => {
|
||||
const target = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) return markdownFiles(target);
|
||||
return entry.isFile() && entry.name.endsWith('.md') ? [target] : [];
|
||||
if (entry.isDirectory()) return markdownFiles(target, depth + 1);
|
||||
return depth > 0 && entry.isFile() && entry.name.endsWith('.md') ? [target] : [];
|
||||
}));
|
||||
return results.flat();
|
||||
}
|
||||
|
||||
@@ -71,13 +71,18 @@ function renderTable(lines, className = '') {
|
||||
const hasSeparator = isSeparator(rows[1] ?? []);
|
||||
const align = hasSeparator ? columnAlignment(rows[1]) : header.map(() => '');
|
||||
const data = hasSeparator ? rows.slice(2) : rows.slice(1);
|
||||
// A row with only its first cell filled is a group heading (CR01, SR06, UN04,
|
||||
// WP 1.0 …): it spans the full width and does not size the columns. Signature
|
||||
// tables keep their blank cells for signing.
|
||||
const isGroup = (row) => className !== 'signature-table' && header.length >= 3
|
||||
&& Boolean(row[0]) && row.slice(1).every((cell) => !cell);
|
||||
|
||||
// A column whose longest cell is short (IDs, dates, counts, statuses) is
|
||||
// marked "tight" so auto table layout shrinks it to its content instead of
|
||||
// handing every column an equal share of the page width.
|
||||
const widest = header.map((_, column) => Math.max(
|
||||
(header[column] ?? '').length,
|
||||
...data.map((row) => (row[column] ?? '').replace(/[`*]/g, '').length)
|
||||
...data.filter((row) => !isGroup(row)).map((row) => (row[column] ?? '').replace(/[`*]/g, '').length)
|
||||
));
|
||||
const classFor = (column) => [
|
||||
align[column] ?? '',
|
||||
@@ -89,7 +94,9 @@ function renderTable(lines, className = '') {
|
||||
return `<${tag}${attr ? ` class="${attr}"` : ''}>${renderInline(cell)}</${tag}>`;
|
||||
}).join('');
|
||||
const classes = [className, header.length >= 8 ? 'dense' : ''].filter(Boolean).join(' ');
|
||||
return `<table${classes ? ` class="${classes}"` : ''}><thead><tr>${cells('th', header)}</tr></thead><tbody>${data.map((row) => `<tr>${cells('td', row)}</tr>`).join('')}</tbody></table>`;
|
||||
return `<table${classes ? ` class="${classes}"` : ''}><thead><tr>${cells('th', header)}</tr></thead><tbody>${data.map((row) => isGroup(row)
|
||||
? `<tr class="group"><td colspan="${header.length}">${renderInline(row[0])}</td></tr>`
|
||||
: `<tr>${cells('td', row)}</tr>`).join('')}</tbody></table>`;
|
||||
}
|
||||
|
||||
function renderParagraph(lines) {
|
||||
|
||||
@@ -26,12 +26,13 @@ function compact(text) {
|
||||
return text.normalize('NFC').toLocaleLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
|
||||
}
|
||||
|
||||
async function markdownFiles(directory) {
|
||||
// Work products sit in the process folders; top-level files in sdlc/ are notes.
|
||||
async function markdownFiles(directory, depth = 0) {
|
||||
const entries = await fs.readdir(directory, { withFileTypes: true });
|
||||
const results = await Promise.all(entries.map(async (entry) => {
|
||||
const target = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) return markdownFiles(target);
|
||||
return entry.isFile() && entry.name.endsWith('.md') ? [target] : [];
|
||||
if (entry.isDirectory()) return markdownFiles(target, depth + 1);
|
||||
return depth > 0 && entry.isFile() && entry.name.endsWith('.md') ? [target] : [];
|
||||
}));
|
||||
return results.flat();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,9 @@ async function main() {
|
||||
const hindsight = hindsightErrors(docs, root);
|
||||
if (hindsight.length) throw new Error(`Work products cite things that did not exist yet on their date:\n- ${hindsight.join('\n- ')}`);
|
||||
|
||||
await fs.rm(target, { recursive: true, force: true });
|
||||
// Only the process folders are generated; files at the top of sdlc/ are
|
||||
// working notes (e.g. the audit prep) and are neither seeded nor rendered.
|
||||
for (const folder of FOLDERS) await fs.rm(path.join(target, folder), { recursive: true, force: true });
|
||||
for (const folder of FOLDERS) await fs.mkdir(path.join(target, folder), { recursive: true });
|
||||
for (const d of docs) {
|
||||
await fs.writeFile(path.join(target, d.dir, `${d.name}.md`), d.md, 'utf8');
|
||||
|
||||
@@ -234,7 +234,7 @@ export function listOfEvidence(inventory) {
|
||||
[3, `Progress Status Record (${files('3.Progress Status Record').length} Report)`, multi('3.Progress Status Record'), 'บันทึกความก้าวหน้าของโครงการรายงวด'],
|
||||
[4, 'Correction Register', one('4.Correction Register'), 'เอกสารสรุปปัญหาที่พบระหว่างดำเนินโครงการ'],
|
||||
[5, 'Acceptance Record', one('5.Acceptance Report'), 'บันทึกการส่งมอบงาน (Sign off Letter)'],
|
||||
[6, `Change Requests (${files('6.Change Report').length} ฉบับ)`, multi('6.Change Report'), 'บันทึกขอการเปลี่ยนแปลงความต้องการ'],
|
||||
[6, 'Change Request (Change Report)', multi('6.Change Report'), 'เกณฑ์และทะเบียนการพิจารณาคำขอเปลี่ยนแปลง (ไม่มีคำขอที่เข้าเกณฑ์ตลอดโครงการ)'],
|
||||
[7, `Meeting Record (${files('7.Meeting Record').length} MoM Report)`, multi('7.Meeting Record'), 'รายงานการประชุมตามแผนการทำงานใน Work Schedule'],
|
||||
[8, 'Software Configuration', one('8.Software Configuration'), 'บันทึกการกำหนดค่าและความเปลี่ยนแปลงต่าง ๆ ที่เกิดขึ้นของระบบ'],
|
||||
[9, 'Project Repository', one('9.Project Repository'), `พื้นที่เก็บข้อมูล เอกสาร และหลักฐานทุกอย่างของโครงการ (Git Server: \`${ORIGIN}\`)`],
|
||||
|
||||
@@ -9,7 +9,7 @@ PACKAGE_DIR="$(realpath -m "${1:-$ROOT_DIR/sdlc-delivery}")"
|
||||
|
||||
[[ -d "$PACKAGE_DIR" ]] || { echo "Delivery package not found: $PACKAGE_DIR" >&2; exit 2; }
|
||||
|
||||
expected="$(find "$SOURCE_DIR" -type f -name '*.md' | wc -l | tr -d ' ')"
|
||||
expected="$(find "$SOURCE_DIR" -mindepth 2 -type f -name '*.md' | wc -l | tr -d ' ')"
|
||||
produced="$(find "$PACKAGE_DIR" -type f -name '*.pdf' | wc -l | tr -d ' ')"
|
||||
[[ "$expected" == "$produced" ]] || { echo "Expected $expected PDFs, found $produced." >&2; exit 1; }
|
||||
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user