Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
46 changes: 25 additions & 21 deletions scripts/prepare-public-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ function publicManifest({ pkg, tag, commit, artifact, candidate, dmgAttestation

function publicNotes(manifest, { changelogPath, publicationNotesRequired = false } = {}) {
const artifact = manifest.artifact.filename;
const downloadUrl = `https://github.com/FernandoAbishai/ScriptCut/releases/download/${manifest.releaseTag}/${artifact}`;
const curated = selectReleaseNotes({
releaseTag: manifest.releaseTag,
changelogPath,
Expand All @@ -155,15 +156,29 @@ function publicNotes(manifest, { changelogPath, publicationNotesRequired = false
const dryRunLabel = curated.source === 'Unreleased'
? `> Dry-run / planned release-note content from \`CHANGELOG.md\` → \`Unreleased\`; it is not part of a published release history.\n\n`
: '';
return `# ScriptCut ${manifest.releaseTag} alpha
return `# ScriptCut ${manifest.releaseTag}

## What's changed
## Download

[Download ScriptCut for macOS — Apple Silicon](${downloadUrl})

macOS Apple Silicon / arm64 · Public prerelease alpha · ad-hoc signed · not notarized

## Install

1. Download the DMG above.
2. Open it and move ScriptCut to Applications.
3. Launch ScriptCut.

If macOS blocks the first launch of the official ScriptCut GitHub download, open **System Settings → Privacy & Security → Open Anyway**, confirm the macOS prompt, and launch ScriptCut normally. Do not use this approval path for random applications or downloads.

## What's new

${dryRunLabel}${curated.markdown}

## ScriptCut alpha status

This is a public prerelease alpha for creator validation. It is provided from the official ScriptCut GitHub repository and uses an ad-hoc code signature for package integrity.
This is a public prerelease alpha for creator validation. It is provided from the official ScriptCut GitHub repository and uses an ad-hoc code signature for package integrity. It is not signed with Apple Developer ID and is not notarized by Apple.

## Supported platform

Expand All @@ -174,18 +189,7 @@ macOS Apple Silicon (arm64) only. Intel, Windows, Linux, and browser builds are
- A self-contained Electron application with portable Python ${manifest.runtime.pythonVersion} and the pinned core runtime.
- Bundled FFmpeg and FFprobe for local export.
- Baseline Whisper transcription code and a trusted model manifest.
- Optional capabilities may not be included in this build.

## Install

1. Open the official [ScriptCut Releases page](https://github.com/FernandoAbishai/ScriptCut/releases).
2. Download the Apple Silicon DMG for **${manifest.releaseTag}**.
3. Open the DMG and move or open ScriptCut as appropriate for the DMG layout.
4. Attempt to launch ScriptCut and select a video.

## macOS first-launch notice

This macOS build uses an ad-hoc code signature for package integrity, but it is not signed with Apple Developer ID and is not notarized by Apple. macOS may block the first launch because the app is not from an identified developer. If you obtained this DMG from the official ScriptCut GitHub release, open **System Settings → Privacy & Security → Open Anyway**, confirm the macOS prompt, and then open ScriptCut normally. Do not use this approval path for random applications or downloads.
- Optional capabilities may be absent from this build.

## Baseline transcription model behavior

Expand All @@ -195,6 +199,12 @@ If the verified baseline model is not present, ScriptCut downloads and verifies

WhisperX, NeMo/Parakeet, pyannote, DeepFilterNet, MediaPipe, OpenCV, MoviePy, and other optional stacks may not be bundled. The packaged baseline path remains the supported transcription path for this alpha.

## Known alpha limitations

- The app uses an ad-hoc code signature but is not signed with Apple Developer ID or notarized, so the first launch requires the macOS approval path above.
- This release is a prerelease alpha; keep original media and project backups.
- Optional capabilities and some caption/export behavior depend on the packaged resources and current baseline support.

## Verify download

From the directory containing the downloaded files:
Expand Down Expand Up @@ -222,12 +232,6 @@ gh attestation verify release-manifest.json \\
\`\`\`

Attestation establishes build provenance; it does not prove that the software is free of bugs or vulnerabilities. Checksums establish integrity and do not make macOS treat this app as notarized.

## Known alpha limitations

- The app uses an ad-hoc code signature but is not signed with Apple Developer ID or notarized, so the first launch requires the macOS approval path above.
- This release is a prerelease alpha; keep original media and project backups.
- Optional capabilities and some caption/export behavior depend on the packaged resources and current baseline support.
`;
}

Expand Down
36 changes: 35 additions & 1 deletion scripts/smoke-release-notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,43 @@ async function main() {
changelogPath: plannedFixture,
});
const notes = fs.readFileSync(path.join(result.outputDir, 'RELEASE_NOTES.md'), 'utf8');
assert(notes.includes("## What's changed"), 'generated notes lack curated section');
const downloadIndex = notes.indexOf('## Download');
const installIndex = notes.indexOf('## Install');
const whatsNewIndex = notes.indexOf("## What's new");
const verifyIndex = notes.indexOf('## Verify download');
const provenanceIndex = notes.indexOf('## Build provenance');
const expectedDownloadUrl = `https://github.com/FernandoAbishai/ScriptCut/releases/download/${result.manifest.releaseTag}/${result.manifest.artifact.filename}`;
assert(notes.startsWith(`# ScriptCut ${result.manifest.releaseTag}\n\n## Download`), 'generated notes do not begin with creator download path');
assert(downloadIndex >= 0, 'generated notes lack Download section');
assert(installIndex >= 0, 'generated notes lack Install section');
assert(whatsNewIndex >= 0, 'generated notes lack What\'s new section');
assert(verifyIndex >= 0, 'generated notes lack verification section');
assert(provenanceIndex >= 0, 'generated notes lack provenance section');
assert(notes.includes(expectedDownloadUrl), 'generated notes lack exact direct DMG URL');
assert(downloadIndex < installIndex, 'Download must precede Install');
assert(installIndex < whatsNewIndex, 'Install must precede What\'s new');
assert(whatsNewIndex < verifyIndex, 'What\'s new must precede verification');
assert(whatsNewIndex < provenanceIndex, 'What\'s new must precede provenance');
assert(notes.includes("## What's new"), 'generated notes lack curated section');
assert(notes.includes('Dry-run / planned release-note content'), 'Unreleased dry-run content is not labeled');
assert(notes.includes('Planned.'), 'generated notes omit Unreleased content');
assert(!notes.includes('/releases/latest'), 'generated notes use a moving latest-release URL');
assert(notes.includes('Privacy & Security → Open Anyway'), 'generated notes omit Open Anyway guidance');
assert(notes.includes('shasum -a 256 -c SHA256SUMS.txt'), 'generated notes omit checksum verification command');
assert(notes.includes('gh attestation verify'), 'generated notes omit attestation verification command');
const exactChangelog = writeChangelog(fixtureRoot, '# Changelog\n\n## Unreleased\n\n- Planned.\n\n## v0.1.0-alpha.4\n\n- Exact selected.\n');
const publicationResult = await preparePublicRelease({
tag: 'v0.1.0-alpha.4',
existingTags: ['v0.1.0-alpha.1', 'v0.1.0-alpha.2', 'v0.1.0-alpha.3'],
candidateDir,
outputDir: path.join(fixtureRoot, 'publication-with-exact-notes'),
commit: 'a'.repeat(40),
changelogPath: exactChangelog,
publicationNotesRequired: true,
});
const publicationNotes = fs.readFileSync(path.join(publicationResult.outputDir, 'RELEASE_NOTES.md'), 'utf8');
assert(publicationNotes.includes('Exact selected.'), 'generated publication notes omit the exact CHANGELOG section');
assert(!publicationNotes.includes('Planned.'), 'generated publication notes used Unreleased despite an exact section');
await expectFailureAsync(() => preparePublicRelease({
tag: 'v0.1.0-alpha.5',
existingTags: ['v0.1.0-alpha.1', 'v0.1.0-alpha.2', 'v0.1.0-alpha.3'],
Expand Down