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
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ jobs:
--docker-tags-file "${RUNNER_TEMP}/docker-tags.txt"
)

notes_file="tools/release/notes/${{ steps.plan.outputs.tag }}.md"
if [[ -f "${notes_file}" ]]; then
args+=(--notes-file "${notes_file}")
fi

if [[ "${{ steps.plan.outputs.prerelease }}" == "true" ]]; then
args+=(--prerelease)
fi
Expand Down
10 changes: 9 additions & 1 deletion tools/release/create-github-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const valueArgumentNames = new Set([
"--image-name",
"--image-digest",
"--docker-tags-file",
"--notes-file",
]);
const requiredArguments = [
"repository",
Expand Down Expand Up @@ -106,6 +107,7 @@ function readDockerTags(filePath) {
}

export function composeReleaseBody({
releaseNotes = "",
generatedBody,
imageName,
imageDigest,
Expand All @@ -132,7 +134,9 @@ export function composeReleaseBody({
dockerLines.push("", `Digest: \`${imageDigest}\``);
}

return `${generatedBody.trim()}\n\n${dockerLines.join("\n")}\n`;
return `${[releaseNotes.trim(), generatedBody.trim(), dockerLines.join("\n")]
.filter(Boolean)
.join("\n\n")}\n`;
}

function writeGithubOutput(outputs) {
Expand All @@ -159,6 +163,9 @@ export async function main(argv = process.argv.slice(2)) {
}

const dockerTags = readDockerTags(arguments_.dockerTagsFile);
const releaseNotes = arguments_.notesFile
? readFileSync(arguments_.notesFile, "utf8")
: "";
const generatedNotes = await githubApi(
`/repos/${arguments_.repository}/releases/generate-notes`,
{
Expand All @@ -172,6 +179,7 @@ export async function main(argv = process.argv.slice(2)) {
},
);
const body = composeReleaseBody({
releaseNotes,
generatedBody: generatedNotes.body,
imageName: arguments_.imageName,
imageDigest: arguments_.imageDigest,
Expand Down
32 changes: 32 additions & 0 deletions tools/release/create-github-release.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,38 @@ void test("rejects release arguments with missing values", () => {
);
});

void test("accepts a release notes file and rejects a missing path", () => {
const arguments_ = parseArguments([
...requiredArguments,
"--notes-file",
"tools/release/notes/v1.3.0.md",
]);

assert.equal(arguments_.notesFile, "tools/release/notes/v1.3.0.md");
assert.throws(
() => parseArguments([...requiredArguments, "--notes-file"]),
/--notes-file requires a value\./u,
);
});

void test("places upgrade notes before generated changes and preserves Docker details", () => {
const body = composeReleaseBody({
releaseNotes: " ## Upgrade notes\n\nRemote URLs require sign-in.\n",
generatedBody: "## What's Changed\n\n- Improve the editor",
imageName: "ghcr.io/techsquidtv/cliparr",
imageDigest: "sha256:example",
dockerTags: ["ghcr.io/techsquidtv/cliparr:1.3.0"],
});

assert.ok(
body.startsWith(
"## Upgrade notes\n\nRemote URLs require sign-in.\n\n## What's Changed",
),
);
assert.match(body, /docker pull ghcr\.io\/techsquidtv\/cliparr:1\.3\.0/u);
assert.match(body, /Digest: `sha256:example`/u);
});

void test("omits the digest line when no image digest exists", () => {
const body = composeReleaseBody({
generatedBody: "## What's Changed\n\n- fix release dry runs",
Expand Down
13 changes: 13 additions & 0 deletions tools/release/notes/v1.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Highlights

- Edit clip ranges directly on the media track, with improved timeline zoom, viewport controls, subtitle cue tracks, and mobile trimming controls.
- Undo and redo edits, discover keyboard shortcuts, and recover editor drafts saved on this device.
- Use simpler export settings, cancel exports in progress, and see size estimates based on the selected encoding plan.
- Get more reliable HLS trimming, video track selection, ProRes handling, and preview recovery.
- Connect providers more easily, including regular Jellyfin accounts, with improved Plex token selection.

## Upgrade notes

**Remote video URLs now require a signed-in provider session.** Sign in to Plex or Jellyfin before opening a remote URL. Previously, this workflow was available without signing in. Local files still work without an account. This access restriction is included in v1.3.0 as a security correction.

Proxied content can no longer execute scripts, HLS playlists larger than 8 MB are rejected, and exports fail when the selected video would otherwise be silently discarded.