mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-20 07:43:07 +08:00
chore: gate release video publish candidates
This commit is contained in:
committed by
Affaan Mustafa
parent
f3cd006252
commit
855e8c8336
@@ -8,9 +8,9 @@ social announcement.
|
|||||||
|
|
||||||
| Field | Evidence |
|
| Field | Evidence |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Upstream main | `c07276a347f8dac4945d2ad294124a708c19b108` |
|
| Upstream main | `f3cd00625222fceedca00164b828db8803fe52d6` |
|
||||||
| Git remote | `https://github.com/affaan-m/ECC.git` |
|
| Git remote | `https://github.com/affaan-m/ECC.git` |
|
||||||
| Evidence scope | Current `main` after PR #1990 harness-audit GitHub integration scoring, PR #1991 canonical ECC identity gate, PR #1992 release video-suite gate, PR #1993 growth outreach pack, and PR #1994 May 19 publication evidence refresh |
|
| Evidence scope | Current `main` after PR #1990 harness-audit GitHub integration scoring, PR #1991 canonical ECC identity gate, PR #1992 release video-suite gate, PR #1993 growth outreach pack, PR #1994 May 19 publication evidence refresh, PR #1995 operator dashboard refresh, and PR #1996 primary render self-eval gate |
|
||||||
| Local status caveat | `git status --short --branch` was clean after pulling `origin/main`; generated evidence files are committed after the source snapshot they describe |
|
| Local status caveat | `git status --short --branch` was clean after pulling `origin/main`; generated evidence files are committed after the source snapshot they describe |
|
||||||
|
|
||||||
The release operator must repeat all publish-facing checks from the exact final
|
The release operator must repeat all publish-facing checks from the exact final
|
||||||
@@ -43,6 +43,8 @@ Tracked repositories in the platform audit were:
|
|||||||
| PR #1992 | Merged the release video-suite gate, production manifest, validator, package file surface, preview-pack smoke wiring, release-surface tests, and compact CI JSON output |
|
| PR #1992 | Merged the release video-suite gate, production manifest, validator, package file surface, preview-pack smoke wiring, release-surface tests, and compact CI JSON output |
|
||||||
| PR #1993 | Merged the partner, sponsor, consulting, conference, podcast, GitHub Discussion, and video CTA pack for the hypergrowth outbound lane |
|
| PR #1993 | Merged the partner, sponsor, consulting, conference, podcast, GitHub Discussion, and video CTA pack for the hypergrowth outbound lane |
|
||||||
| PR #1994 | Merged the May 19 publication-evidence refresh, platform-audit evidence gate, preview-pack smoke evidence gate, and URL/readiness/roadmap references |
|
| PR #1994 | Merged the May 19 publication-evidence refresh, platform-audit evidence gate, preview-pack smoke evidence gate, and URL/readiness/roadmap references |
|
||||||
|
| PR #1995 | Merged the May 19 operator dashboard refresh with the `$1,728/mo` MRR baseline, `$10,000/mo` target, and release/video/outbound top actions |
|
||||||
|
| PR #1996 | Merged the primary launch render self-eval gate for duration, size, resolution, video stream, and audio stream checks |
|
||||||
|
|
||||||
## Release And Growth Evidence
|
## Release And Growth Evidence
|
||||||
|
|
||||||
@@ -51,9 +53,9 @@ Tracked repositories in the platform audit were:
|
|||||||
| Release-surface tests | `node tests/docs/ecc2-release-surface.test.js` | 25 passed, 0 failed |
|
| Release-surface tests | `node tests/docs/ecc2-release-surface.test.js` | 25 passed, 0 failed |
|
||||||
| Preview-pack smoke | `npm run preview-pack:smoke -- --format json` | Ready true; digest `bc2bf157616e`; 30 required artifacts; 5 passed, 0 failed |
|
| Preview-pack smoke | `npm run preview-pack:smoke -- --format json` | Ready true; digest `bc2bf157616e`; 30 required artifacts; 5 passed, 0 failed |
|
||||||
| Operator dashboard | `npm run operator:dashboard -- --markdown --write docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md` | Generated May 19 dashboard with platform audit ready true, 0 tracked PRs, 0 tracked issues, 0 discussion gaps, `$1,728/mo` current MRR, `$10,000/mo` target MRR, and top actions for plugin publication, notifications, release video, outbound approval, AgentShield, and ECC Tools billing |
|
| Operator dashboard | `npm run operator:dashboard -- --markdown --write docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md` | Generated May 19 dashboard with platform audit ready true, 0 tracked PRs, 0 tracked issues, 0 discussion gaps, `$1,728/mo` current MRR, `$10,000/mo` target MRR, and top actions for plugin publication, notifications, release video, outbound approval, AgentShield, and ECC Tools billing |
|
||||||
| Release video suite | `npm run release:video-suite -- --format json --summary` with `ECC_VIDEO_SOURCE_ROOT` and `ECC_VIDEO_RELEASE_SUITE_ROOT` | Ready true; 15/15 source assets present; 13/13 render, timeline, caption, EDL, and segment artifacts present; primary rough render self-eval passed at 144.759 seconds, 1920x1080, 1 audio stream, and 106.78 MB |
|
| Release video suite | `npm run release:video-suite -- --format json --summary` with `ECC_VIDEO_SOURCE_ROOT` and `ECC_VIDEO_RELEASE_SUITE_ROOT` | Ready true; 15/15 source assets present; 13/13 render, timeline, caption, EDL, and segment artifacts present; 12/12 publish-candidate outputs present; primary rough render self-eval passed at 144.759 seconds, 1920x1080, 1 audio stream, and 106.78 MB |
|
||||||
| Full local suite | `node tests/run-all.js` | 2544 passed, 0 failed |
|
| Full local suite | `node tests/run-all.js` | 2544 passed, 0 failed |
|
||||||
| PR #1993 CI | GitHub Actions run `26093792219` | Completed successfully for `d9ac22c697d9a8a8771512ab01e6df857c16776d`; all reported checks passed, including lint, validation, security scan, coverage, GitGuardian, and the macOS/Ubuntu/Windows test matrix |
|
| PR #1996 CI | GitHub Actions run `26096847138` | Completed successfully for `f3cd00625222fceedca00164b828db8803fe52d6`; all reported checks passed, including lint, validation, security scan, coverage, GitGuardian, CodeRabbit, Cubic, and the macOS/Ubuntu/Windows test matrix |
|
||||||
| Public-path sanitization | `node scripts/ci/validate-no-personal-paths.js` through local suite and CI | Passed |
|
| Public-path sanitization | `node scripts/ci/validate-no-personal-paths.js` through local suite and CI | Passed |
|
||||||
| Markdown and whitespace | `markdownlint` focused release docs plus `git diff --check` before PR #1993 | Passed |
|
| Markdown and whitespace | `markdownlint` focused release docs plus `git diff --check` before PR #1993 | Passed |
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ Tracked repositories in the platform audit were:
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Canonical repo identity | Public URLs and release docs now use `https://github.com/affaan-m/ECC` where public links are needed |
|
| Canonical repo identity | Public URLs and release docs now use `https://github.com/affaan-m/ECC` where public links are needed |
|
||||||
| Release claim | Release notes and launch collateral frame ECC as the harness-native operator system for agentic work, not a Claude-only config pack |
|
| Release claim | Release notes and launch collateral frame ECC as the harness-native operator system for agentic work, not a Claude-only config pack |
|
||||||
| Video proof | `video-suite-production.md` gates the local rough render, timeline, captions, source inventory, self-eval, and no-private-path publication rules |
|
| Video proof | `video-suite-production.md` gates the local rough render, timeline, captions, source inventory, publish-candidate clip set, self-eval, and no-private-path publication rules |
|
||||||
| Growth proof | `partner-sponsor-talks-pack.md` provides approval-gated copy for sponsors, partners, consulting, talks, podcasts, GitHub Discussion, and video CTAs |
|
| Growth proof | `partner-sponsor-talks-pack.md` provides approval-gated copy for sponsors, partners, consulting, talks, podcasts, GitHub Discussion, and video CTAs |
|
||||||
| Business baseline | Hypergrowth command center and partner pack use `$1,728/mo` current MRR, `$10,000/mo` target MRR, and `$8,272/mo` gap |
|
| Business baseline | Hypergrowth command center and partner pack use `$1,728/mo` current MRR, `$10,000/mo` target MRR, and `$8,272/mo` gap |
|
||||||
| Operator dashboard | `operator-readiness-dashboard-2026-05-19.md` pulls the growth baseline into the same queue, publication, video, outbound, AgentShield, ECC Tools, Linear, and supply-chain control surface |
|
| Operator dashboard | `operator-readiness-dashboard-2026-05-19.md` pulls the growth baseline into the same queue, publication, video, outbound, AgentShield, ECC Tools, Linear, and supply-chain control surface |
|
||||||
@@ -92,8 +94,8 @@ Tracked repositories in the platform audit were:
|
|||||||
The tracked public PR queue, issue queue, discussion queue, canonical ECC
|
The tracked public PR queue, issue queue, discussion queue, canonical ECC
|
||||||
identity, release video suite, preview pack, and growth outreach packet are
|
identity, release video suite, preview pack, and growth outreach packet are
|
||||||
current on May 19, 2026 for `main` through
|
current on May 19, 2026 for `main` through
|
||||||
`c07276a347f8dac4945d2ad294124a708c19b108`, with the May 19 dashboard
|
`f3cd00625222fceedca00164b828db8803fe52d6`, with the publish-candidate
|
||||||
refresh staged for the next merge.
|
video gate staged for the next merge.
|
||||||
|
|
||||||
This improves publication readiness but does not replace the approval-gated
|
This improves publication readiness but does not replace the approval-gated
|
||||||
release, package, plugin, billing, Discord, and announcement steps in
|
release, package, plugin, billing, Discord, and announcement steps in
|
||||||
|
|||||||
@@ -106,6 +106,27 @@ Required local rough v1 artifacts:
|
|||||||
- `segments/primary-launch-v1/08-oss-paid-model.mp4`
|
- `segments/primary-launch-v1/08-oss-paid-model.mp4`
|
||||||
- `segments/primary-launch-v1/09-close-shipping-system.mp4`
|
- `segments/primary-launch-v1/09-close-shipping-system.mp4`
|
||||||
|
|
||||||
|
## Publish-Candidate Outputs
|
||||||
|
|
||||||
|
The release validator also expects the current publish-candidate set under
|
||||||
|
`renders/publish-candidates/`. These are still local review files, not public
|
||||||
|
uploads or committed media.
|
||||||
|
|
||||||
|
| Output | Target |
|
||||||
|
| --- | --- |
|
||||||
|
| `ecc-2-primary-launch.mp4` | 90-150s, 1920x1080, audio |
|
||||||
|
| `ecc-2-primary-launch.captions.srt` | primary captions |
|
||||||
|
| `ecc-2-install-proof-wide.mp4` | 25-35s, 1920x1080, audio |
|
||||||
|
| `ecc-2-install-proof-vertical.mp4` | 25-35s, 1080x1920, audio |
|
||||||
|
| `ecc-2-what-is-ecc-wide.mp4` | 45-60s, 1920x1080, audio |
|
||||||
|
| `ecc-2-what-is-ecc-vertical.mp4` | 45-60s, 1080x1920, audio |
|
||||||
|
| `ecc-2-security-proof-wide.mp4` | 45-60s, 1920x1080, audio |
|
||||||
|
| `ecc-2-security-proof-vertical.mp4` | 45-60s, 1080x1920, audio |
|
||||||
|
| `ecc-2-money-proof-wide.mp4` | 30-45s, 1920x1080, audio |
|
||||||
|
| `ecc-2-money-proof-vertical.mp4` | 30-45s, 1080x1920, audio |
|
||||||
|
| `ecc-2-social-proof-wide.mp4` | 30-45s, 1920x1080, audio |
|
||||||
|
| `ecc-2-social-proof-vertical.mp4` | 30-45s, 1080x1920, audio |
|
||||||
|
|
||||||
## video-use compatible workflow
|
## video-use compatible workflow
|
||||||
|
|
||||||
Use the same production shape as Video Use while keeping the ECC-specific media
|
Use the same production shape as Video Use while keeping the ECC-specific media
|
||||||
@@ -155,6 +176,8 @@ Then manually check the final render for:
|
|||||||
|
|
||||||
- validator self-eval passes for the primary render: 90-150 seconds, at least
|
- validator self-eval passes for the primary render: 90-150 seconds, at least
|
||||||
1280x720, video stream present, audio stream present, and non-empty output;
|
1280x720, video stream present, audio stream present, and non-empty output;
|
||||||
|
- validator self-eval passes for the publish-candidate set: primary MP4 plus
|
||||||
|
captions and five short clips in both wide and vertical formats;
|
||||||
- no blank frames or accidental desktop exposure;
|
- no blank frames or accidental desktop exposure;
|
||||||
- no stale repo name, pivot, rename, or Claude-only framing in captions;
|
- no stale repo name, pivot, rename, or Claude-only framing in captions;
|
||||||
- no captions that rewrite speech into a false claim;
|
- no captions that rewrite speech into a false claim;
|
||||||
|
|||||||
@@ -183,6 +183,135 @@ const REQUIRED_SUITE_ARTIFACTS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const REQUIRED_PUBLISH_CANDIDATES = [
|
||||||
|
{
|
||||||
|
id: 'publish-primary-launch',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-primary-launch.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 90,
|
||||||
|
maxDurationSeconds: 150,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 5,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-primary-launch-captions',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-primary-launch.captions.srt',
|
||||||
|
kind: 'captions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-install-proof-wide',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-install-proof-wide.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 25,
|
||||||
|
maxDurationSeconds: 35,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 1,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-install-proof-vertical',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-install-proof-vertical.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 25,
|
||||||
|
maxDurationSeconds: 35,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 1920,
|
||||||
|
minSizeMb: 1,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-what-is-ecc-wide',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-what-is-ecc-wide.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 45,
|
||||||
|
maxDurationSeconds: 60,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-what-is-ecc-vertical',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-what-is-ecc-vertical.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 45,
|
||||||
|
maxDurationSeconds: 60,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 1920,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-security-proof-wide',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-security-proof-wide.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 45,
|
||||||
|
maxDurationSeconds: 60,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-security-proof-vertical',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-security-proof-vertical.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 45,
|
||||||
|
maxDurationSeconds: 60,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 1920,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-money-proof-wide',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-money-proof-wide.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 30,
|
||||||
|
maxDurationSeconds: 45,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-money-proof-vertical',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-money-proof-vertical.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 30,
|
||||||
|
maxDurationSeconds: 45,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 1920,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-social-proof-wide',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-social-proof-wide.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 30,
|
||||||
|
maxDurationSeconds: 45,
|
||||||
|
minWidth: 1920,
|
||||||
|
minHeight: 1080,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publish-social-proof-vertical',
|
||||||
|
relativePath: 'renders/publish-candidates/ecc-2-social-proof-vertical.mp4',
|
||||||
|
kind: 'video',
|
||||||
|
minDurationSeconds: 30,
|
||||||
|
maxDurationSeconds: 45,
|
||||||
|
minWidth: 1080,
|
||||||
|
minHeight: 1920,
|
||||||
|
minSizeMb: 2,
|
||||||
|
requiresAudio: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function usage() {
|
function usage() {
|
||||||
console.log([
|
console.log([
|
||||||
'Usage: node scripts/release-video-suite.js [options]',
|
'Usage: node scripts/release-video-suite.js [options]',
|
||||||
@@ -471,22 +600,83 @@ function inspectSourceAssets(sourceRoot, skipProbe) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function inspectSuiteArtifacts(suiteRoot, skipProbe) {
|
function validateVideoArtifact(artifact, media, skipProbe) {
|
||||||
return REQUIRED_SUITE_ARTIFACTS.map(artifact => {
|
if (artifact.kind !== 'video' || skipProbe) {
|
||||||
if (!suiteRoot) {
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
if (media.probe !== 'ok') {
|
||||||
|
failures.push(`ffprobe ${media.probe}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isFinite(artifact.minDurationSeconds)
|
||||||
|
&& (
|
||||||
|
!Number.isFinite(media.durationSeconds)
|
||||||
|
|| media.durationSeconds < artifact.minDurationSeconds
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
failures.push(`duration below ${artifact.minDurationSeconds}s`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isFinite(artifact.maxDurationSeconds)
|
||||||
|
&& (
|
||||||
|
!Number.isFinite(media.durationSeconds)
|
||||||
|
|| media.durationSeconds > artifact.maxDurationSeconds
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
failures.push(`duration above ${artifact.maxDurationSeconds}s`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isFinite(artifact.minSizeMb)
|
||||||
|
&& (!Number.isFinite(media.sizeMb) || media.sizeMb < artifact.minSizeMb)
|
||||||
|
) {
|
||||||
|
failures.push(`size below ${artifact.minSizeMb} MB`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isFinite(artifact.minWidth)
|
||||||
|
&& (!Number.isFinite(media.width) || media.width < artifact.minWidth)
|
||||||
|
) {
|
||||||
|
failures.push(`width below ${artifact.minWidth}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isFinite(artifact.minHeight)
|
||||||
|
&& (!Number.isFinite(media.height) || media.height < artifact.minHeight)
|
||||||
|
) {
|
||||||
|
failures.push(`height below ${artifact.minHeight}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artifact.requiresAudio && (!Number.isFinite(media.audioStreams) || media.audioStreams < 1)) {
|
||||||
|
failures.push('audio stream missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectArtifactCollection(rootDir, artifacts, skipProbe) {
|
||||||
|
return artifacts.map(artifact => {
|
||||||
|
if (!rootDir) {
|
||||||
return {
|
return {
|
||||||
...artifact,
|
...artifact,
|
||||||
status: 'missing',
|
status: 'missing',
|
||||||
configured: false,
|
configured: false,
|
||||||
|
validationFailures: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = path.join(suiteRoot, artifact.relativePath);
|
const filePath = path.join(rootDir, artifact.relativePath);
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
return {
|
return {
|
||||||
...artifact,
|
...artifact,
|
||||||
status: 'missing',
|
status: 'missing',
|
||||||
configured: true,
|
configured: true,
|
||||||
|
validationFailures: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,44 +686,26 @@ function inspectSuiteArtifacts(suiteRoot, skipProbe) {
|
|||||||
durationSeconds: null,
|
durationSeconds: null,
|
||||||
probe: 'not-media',
|
probe: 'not-media',
|
||||||
};
|
};
|
||||||
|
const validationFailures = validateVideoArtifact(artifact, media, skipProbe);
|
||||||
let durationStatus = 'pass';
|
|
||||||
if (
|
|
||||||
artifact.kind === 'video'
|
|
||||||
&& Number.isFinite(artifact.minDurationSeconds)
|
|
||||||
&& Number.isFinite(media.durationSeconds)
|
|
||||||
&& media.durationSeconds < artifact.minDurationSeconds
|
|
||||||
) {
|
|
||||||
durationStatus = 'fail';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
artifact.kind === 'video'
|
|
||||||
&& Number.isFinite(artifact.maxDurationSeconds)
|
|
||||||
&& Number.isFinite(media.durationSeconds)
|
|
||||||
&& media.durationSeconds > artifact.maxDurationSeconds
|
|
||||||
) {
|
|
||||||
durationStatus = 'fail';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
artifact.kind === 'video'
|
|
||||||
&& Number.isFinite(artifact.minDurationSeconds)
|
|
||||||
&& !skipProbe
|
|
||||||
&& media.durationSeconds === null
|
|
||||||
) {
|
|
||||||
durationStatus = 'fail';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...artifact,
|
...artifact,
|
||||||
status: durationStatus === 'pass' ? 'present' : 'invalid',
|
status: validationFailures.length === 0 ? 'present' : 'invalid',
|
||||||
configured: true,
|
configured: true,
|
||||||
|
validationFailures,
|
||||||
...media,
|
...media,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inspectSuiteArtifacts(suiteRoot, skipProbe) {
|
||||||
|
return inspectArtifactCollection(suiteRoot, REQUIRED_SUITE_ARTIFACTS, skipProbe);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectPublishCandidates(suiteRoot, skipProbe) {
|
||||||
|
return inspectArtifactCollection(suiteRoot, REQUIRED_PUBLISH_CANDIDATES, skipProbe);
|
||||||
|
}
|
||||||
|
|
||||||
function evaluatePrimaryRender(suiteArtifacts, skipProbe) {
|
function evaluatePrimaryRender(suiteArtifacts, skipProbe) {
|
||||||
const primary = suiteArtifacts.find(artifact => artifact.id === 'primary-render-v1');
|
const primary = suiteArtifacts.find(artifact => artifact.id === 'primary-render-v1');
|
||||||
|
|
||||||
@@ -617,8 +789,10 @@ function buildReport(options = {}) {
|
|||||||
]);
|
]);
|
||||||
const sourceAssets = inspectSourceAssets(sourceRoot, skipProbe);
|
const sourceAssets = inspectSourceAssets(sourceRoot, skipProbe);
|
||||||
const suiteArtifacts = inspectSuiteArtifacts(suiteRoot, skipProbe);
|
const suiteArtifacts = inspectSuiteArtifacts(suiteRoot, skipProbe);
|
||||||
|
const publishCandidates = inspectPublishCandidates(suiteRoot, skipProbe);
|
||||||
const missingSourceAssets = sourceAssets.filter(asset => asset.status !== 'present');
|
const missingSourceAssets = sourceAssets.filter(asset => asset.status !== 'present');
|
||||||
const missingSuiteArtifacts = suiteArtifacts.filter(artifact => artifact.status !== 'present');
|
const missingSuiteArtifacts = suiteArtifacts.filter(artifact => artifact.status !== 'present');
|
||||||
|
const missingPublishCandidates = publishCandidates.filter(candidate => candidate.status !== 'present');
|
||||||
const primaryRenderSelfEval = evaluatePrimaryRender(suiteArtifacts, skipProbe);
|
const primaryRenderSelfEval = evaluatePrimaryRender(suiteArtifacts, skipProbe);
|
||||||
|
|
||||||
const checks = [
|
const checks = [
|
||||||
@@ -682,6 +856,23 @@ function buildReport(options = {}) {
|
|||||||
primaryRenderSelfEval.summary,
|
primaryRenderSelfEval.summary,
|
||||||
primaryRenderSelfEval.fix
|
primaryRenderSelfEval.fix
|
||||||
),
|
),
|
||||||
|
makeCheck(
|
||||||
|
'video-publish-candidates-present',
|
||||||
|
missingPublishCandidates.length === 0 ? 'pass' : 'fail',
|
||||||
|
missingPublishCandidates.length === 0
|
||||||
|
? `${publishCandidates.length} publish-candidate MP4/caption artifacts are present and self-evaluable`
|
||||||
|
: `missing or invalid publish candidates: ${missingPublishCandidates.map(candidate => {
|
||||||
|
const reason = candidate.validationFailures && candidate.validationFailures.length > 0
|
||||||
|
? ` (${candidate.validationFailures.join(', ')})`
|
||||||
|
: '';
|
||||||
|
return `${candidate.relativePath}${reason}`;
|
||||||
|
}).join(', ')}`,
|
||||||
|
'Render the publish-candidate MP4/caption set under renders/publish-candidates before release review.',
|
||||||
|
{
|
||||||
|
configured: Boolean(suiteRoot),
|
||||||
|
missing: missingPublishCandidates.map(candidate => candidate.relativePath),
|
||||||
|
}
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
const failed = checks.filter(check => check.status !== 'pass');
|
const failed = checks.filter(check => check.status !== 'pass');
|
||||||
@@ -713,6 +904,7 @@ function buildReport(options = {}) {
|
|||||||
checks,
|
checks,
|
||||||
sourceAssets,
|
sourceAssets,
|
||||||
suiteArtifacts,
|
suiteArtifacts,
|
||||||
|
publishCandidates,
|
||||||
top_actions: topActions,
|
top_actions: topActions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -748,6 +940,7 @@ function summarizeReport(report) {
|
|||||||
})),
|
})),
|
||||||
sourceAssetSummary: summarizeItems(report.sourceAssets),
|
sourceAssetSummary: summarizeItems(report.sourceAssets),
|
||||||
suiteArtifactSummary: summarizeItems(report.suiteArtifacts),
|
suiteArtifactSummary: summarizeItems(report.suiteArtifacts),
|
||||||
|
publishCandidateSummary: summarizeItems(report.publishCandidates),
|
||||||
primaryRender: primaryRender ? {
|
primaryRender: primaryRender ? {
|
||||||
status: primaryRender.status,
|
status: primaryRender.status,
|
||||||
durationSeconds: primaryRender.durationSeconds,
|
durationSeconds: primaryRender.durationSeconds,
|
||||||
@@ -780,6 +973,11 @@ function renderText(report) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (report.publishCandidates.length > 0) {
|
||||||
|
const present = report.publishCandidates.filter(item => item.status === 'present').length;
|
||||||
|
lines.push(`Publish candidates: ${present}/${report.publishCandidates.length} present`);
|
||||||
|
}
|
||||||
|
|
||||||
if (report.top_actions.length > 0) {
|
if (report.top_actions.length > 0) {
|
||||||
lines.push('');
|
lines.push('');
|
||||||
lines.push('Top actions:');
|
lines.push('Top actions:');
|
||||||
@@ -822,6 +1020,7 @@ if (require.main === module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
REQUIRED_PUBLISH_CANDIDATES,
|
||||||
REQUIRED_SOURCE_ASSETS,
|
REQUIRED_SOURCE_ASSETS,
|
||||||
REQUIRED_SUITE_ARTIFACTS,
|
REQUIRED_SUITE_ARTIFACTS,
|
||||||
buildReport,
|
buildReport,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const { execFileSync, spawnSync } = require('child_process');
|
|||||||
|
|
||||||
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'release-video-suite.js');
|
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'release-video-suite.js');
|
||||||
const {
|
const {
|
||||||
|
REQUIRED_PUBLISH_CANDIDATES,
|
||||||
REQUIRED_SOURCE_ASSETS,
|
REQUIRED_SOURCE_ASSETS,
|
||||||
REQUIRED_SUITE_ARTIFACTS,
|
REQUIRED_SUITE_ARTIFACTS,
|
||||||
buildReport,
|
buildReport,
|
||||||
@@ -75,6 +76,10 @@ function seedMedia(sourceRoot, suiteRoot) {
|
|||||||
for (const artifact of REQUIRED_SUITE_ARTIFACTS) {
|
for (const artifact of REQUIRED_SUITE_ARTIFACTS) {
|
||||||
writeFile(suiteRoot, artifact.relativePath, `artifact ${artifact.id}`);
|
writeFile(suiteRoot, artifact.relativePath, `artifact ${artifact.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const candidate of REQUIRED_PUBLISH_CANDIDATES) {
|
||||||
|
writeFile(suiteRoot, candidate.relativePath, `candidate ${candidate.id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(args = [], options = {}) {
|
function run(args = [], options = {}) {
|
||||||
@@ -175,8 +180,13 @@ function runTests() {
|
|||||||
)));
|
)));
|
||||||
assert.strictEqual(report.sourceAssets.length, REQUIRED_SOURCE_ASSETS.length);
|
assert.strictEqual(report.sourceAssets.length, REQUIRED_SOURCE_ASSETS.length);
|
||||||
assert.strictEqual(report.suiteArtifacts.length, REQUIRED_SUITE_ARTIFACTS.length);
|
assert.strictEqual(report.suiteArtifacts.length, REQUIRED_SUITE_ARTIFACTS.length);
|
||||||
|
assert.strictEqual(report.publishCandidates.length, REQUIRED_PUBLISH_CANDIDATES.length);
|
||||||
assert.ok(renderText(report).includes('Ready: yes'));
|
assert.ok(renderText(report).includes('Ready: yes'));
|
||||||
assert.strictEqual(summarizeReport(report).sourceAssetSummary.present, REQUIRED_SOURCE_ASSETS.length);
|
assert.strictEqual(summarizeReport(report).sourceAssetSummary.present, REQUIRED_SOURCE_ASSETS.length);
|
||||||
|
assert.strictEqual(
|
||||||
|
summarizeReport(report).publishCandidateSummary.present,
|
||||||
|
REQUIRED_PUBLISH_CANDIDATES.length
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
cleanup(rootDir);
|
cleanup(rootDir);
|
||||||
cleanup(sourceRoot);
|
cleanup(sourceRoot);
|
||||||
@@ -202,6 +212,7 @@ function runTests() {
|
|||||||
assert.ok(report.checks.some(check => check.id === 'video-source-assets-present' && check.status === 'fail'));
|
assert.ok(report.checks.some(check => check.id === 'video-source-assets-present' && check.status === 'fail'));
|
||||||
assert.ok(report.checks.some(check => check.id === 'video-release-artifacts-present' && check.status === 'fail'));
|
assert.ok(report.checks.some(check => check.id === 'video-release-artifacts-present' && check.status === 'fail'));
|
||||||
assert.ok(report.checks.some(check => check.id === 'video-primary-render-self-eval' && check.status === 'fail'));
|
assert.ok(report.checks.some(check => check.id === 'video-primary-render-self-eval' && check.status === 'fail'));
|
||||||
|
assert.ok(report.checks.some(check => check.id === 'video-publish-candidates-present' && check.status === 'fail'));
|
||||||
} finally {
|
} finally {
|
||||||
cleanup(rootDir);
|
cleanup(rootDir);
|
||||||
}
|
}
|
||||||
@@ -269,6 +280,7 @@ function runTests() {
|
|||||||
assert.strictEqual(parsed.suiteRootConfigured, true);
|
assert.strictEqual(parsed.suiteRootConfigured, true);
|
||||||
assert.strictEqual(parsed.sourceAssetSummary.present, REQUIRED_SOURCE_ASSETS.length);
|
assert.strictEqual(parsed.sourceAssetSummary.present, REQUIRED_SOURCE_ASSETS.length);
|
||||||
assert.strictEqual(parsed.suiteArtifactSummary.present, REQUIRED_SUITE_ARTIFACTS.length);
|
assert.strictEqual(parsed.suiteArtifactSummary.present, REQUIRED_SUITE_ARTIFACTS.length);
|
||||||
|
assert.strictEqual(parsed.publishCandidateSummary.present, REQUIRED_PUBLISH_CANDIDATES.length);
|
||||||
} finally {
|
} finally {
|
||||||
cleanup(rootDir);
|
cleanup(rootDir);
|
||||||
cleanup(sourceRoot);
|
cleanup(sourceRoot);
|
||||||
|
|||||||
Reference in New Issue
Block a user