ruby_raider 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ffmpeg/build.sh +208 -0
- data/.github/ffmpeg/check.sh +72 -0
- data/.github/ffmpeg/frames.rb +34 -0
- data/.github/ffmpeg/probe.sh +17 -0
- data/.github/workflows/ffmpeg.yml +160 -0
- data/README.md +168 -33
- data/RELEASE.md +7 -0
- data/lib/generators/templates/common/read_me.tt +6 -7
- data/lib/generators/templates/helpers/video_helper.tt +1 -2
- data/lib/ruby_raider/ffmpeg.rb +38 -11
- data/lib/version +1 -1
- data/sig/ruby_raider/ffmpeg.rbs +1 -0
- data/spec/generators/video_helper_spec.rb +21 -4
- data/spec/integration/content/readme_content_spec.rb +7 -2
- data/spec/integration/end_to_end_spec.rb +20 -8
- data/spec/utilities/ffmpeg_spec.rb +80 -7
- data/spec/utilities/video_command_spec.rb +13 -0
- metadata +7 -7
- data/docs/superpowers/plans/2026-10-25-failure-video-replay.md +0 -400
- data/docs/superpowers/plans/2026-10-26-parity-cleanup.md +0 -233
- data/docs/superpowers/plans/2026-10-28-failure-card.md +0 -295
- data/docs/superpowers/specs/2026-10-25-failure-video-replay-design.md +0 -214
- data/docs/superpowers/specs/2026-10-28-failure-card-design.md +0 -210
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d516f44eddaa410a882fb64a08755338486c30d9eca4755f97c1716f5b5754ca
|
|
4
|
+
data.tar.gz: de1a0fd5c654301482a09830072991d89793e117092bd23515b44e3e8f428c90
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f580ff7a46dd54bc6c792d273045e1041e5bd29dceb302468f909661b4f52fbb837786765e3cb65d701526d7c3ab75aacbfa730ae677a0d8443448a292b667c7
|
|
7
|
+
data.tar.gz: bf9a38d296a6b08ff3f2154e35168d75045431076c65a4a9ea376a4c3a76d928c163d5d5b840dc22ab7444c88e2021a7f66157b2760c84eb40a064a9941b98d6
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Builds the ffmpeg binary that RubyRaider::Ffmpeg downloads for failure videos.
|
|
3
|
+
#
|
|
4
|
+
# .github/ffmpeg/build.sh <darwin-arm64|darwin-x64|linux-x64|windows-x64> [out_dir]
|
|
5
|
+
#
|
|
6
|
+
# LGPL build (no --enable-gpl, no --enable-nonfree) with only what the generated
|
|
7
|
+
# VideoHelper needs: PNG frames listed in a concat file, scaled, encoded with libvpx
|
|
8
|
+
# (VP8, VP9 kept for later) into WebM. Every source is pinned by SHA-256 or commit.
|
|
9
|
+
# Also writes the license notices for what is linked in and, in <out_dir>/sources/, the complete
|
|
10
|
+
# corresponding source (FFmpeg and zlib tarballs, a git archive of the libvpx commit) for the release.
|
|
11
|
+
# Runs on: macOS (arm64 or x86_64 runner), Alpine Linux (fully static, musl) and
|
|
12
|
+
# MSYS2 MINGW64 on Windows (static libgcc/winpthread). Needs bash, make, a C compiler,
|
|
13
|
+
# curl, tar, xz, git, pkg-config, perl and nasm (x86_64 targets only).
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
FFMPEG_VERSION=7.1.5
|
|
17
|
+
FFMPEG_SHA256=de668509caf9e35e3cd162473441fdb29538c6d96ed080292b3cf9e6fc5d558f
|
|
18
|
+
ZLIB_VERSION=1.3.1
|
|
19
|
+
ZLIB_SHA256=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
|
|
20
|
+
LIBVPX_TAG=v1.15.2
|
|
21
|
+
LIBVPX_COMMIT=d168454ecd099805c675d4a98c66f4891373302a
|
|
22
|
+
# Only for its license notice: the Linux binary links musl statically (Alpine 3.20 ships musl 1.2.5).
|
|
23
|
+
MUSL_VERSION=1.2.5
|
|
24
|
+
MUSL_SHA256=a9a118bbe84d8764da0ea0d28b3ab3fae8477fc7e4085d90102b8596fc7c75e4
|
|
25
|
+
MACOS_MIN=11.0
|
|
26
|
+
|
|
27
|
+
FFMPEG_URL="https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz"
|
|
28
|
+
ZLIB_URL="https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz"
|
|
29
|
+
LIBVPX_REPO=https://chromium.googlesource.com/webm/libvpx
|
|
30
|
+
MUSL_URL="https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz"
|
|
31
|
+
|
|
32
|
+
target="${1:?usage: build.sh <darwin-arm64|darwin-x64|linux-x64|windows-x64> [out_dir]}"
|
|
33
|
+
here="$(cd "$(dirname "$0")" && pwd)"
|
|
34
|
+
out_dir="$(mkdir -p "${2:-dist}" && cd "${2:-dist}" && pwd)"
|
|
35
|
+
work="${FFMPEG_BUILD_DIR:-$(pwd)/build-ffmpeg-$target}"
|
|
36
|
+
prefix="$work/prefix"
|
|
37
|
+
jobs="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)"
|
|
38
|
+
|
|
39
|
+
cflags=""
|
|
40
|
+
ldflags=""
|
|
41
|
+
exe=""
|
|
42
|
+
ffmpeg_flags=(--enable-pthreads)
|
|
43
|
+
zlib_make=()
|
|
44
|
+
case "$target" in
|
|
45
|
+
darwin-arm64)
|
|
46
|
+
vpx_target=arm64-darwin20-gcc
|
|
47
|
+
arch=arm64
|
|
48
|
+
;;
|
|
49
|
+
darwin-x64)
|
|
50
|
+
vpx_target=x86_64-darwin20-gcc
|
|
51
|
+
arch=x86_64
|
|
52
|
+
;;
|
|
53
|
+
linux-x64)
|
|
54
|
+
vpx_target=x86_64-linux-gcc
|
|
55
|
+
ldflags="-static"
|
|
56
|
+
;;
|
|
57
|
+
windows-x64)
|
|
58
|
+
vpx_target=x86_64-win64-gcc
|
|
59
|
+
ldflags="-static"
|
|
60
|
+
exe=".exe"
|
|
61
|
+
ffmpeg_flags=(--enable-w32threads)
|
|
62
|
+
zlib_make=(-f win32/Makefile.gcc)
|
|
63
|
+
;;
|
|
64
|
+
*)
|
|
65
|
+
echo "unknown target: $target" >&2
|
|
66
|
+
exit 2
|
|
67
|
+
;;
|
|
68
|
+
esac
|
|
69
|
+
|
|
70
|
+
if [[ "$target" == darwin-* ]]; then
|
|
71
|
+
if [[ "$(uname -m)" != "$arch" ]]; then
|
|
72
|
+
echo "build $target on a $arch Mac (this one is $(uname -m))" >&2
|
|
73
|
+
exit 2
|
|
74
|
+
fi
|
|
75
|
+
export MACOSX_DEPLOYMENT_TARGET="$MACOS_MIN"
|
|
76
|
+
cflags="-mmacosx-version-min=$MACOS_MIN"
|
|
77
|
+
ldflags="-mmacosx-version-min=$MACOS_MIN"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
sha256() {
|
|
81
|
+
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fetch() { # url sha256 file
|
|
85
|
+
local url="$1" want="$2" file="$work/src/$3"
|
|
86
|
+
if [[ ! -f "$file" ]]; then
|
|
87
|
+
echo "fetching $url"
|
|
88
|
+
curl --proto '=https' --tlsv1.2 -sSfL --retry 5 --retry-all-errors --retry-delay 10 -o "$file" "$url"
|
|
89
|
+
fi
|
|
90
|
+
local got
|
|
91
|
+
got="$(sha256 "$file")"
|
|
92
|
+
if [[ "$got" != "$want" ]]; then
|
|
93
|
+
echo "checksum mismatch for $url: got $got, want $want" >&2
|
|
94
|
+
rm -f "$file"
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
mkdir -p "$work/src" "$prefix"
|
|
100
|
+
cd "$work/src"
|
|
101
|
+
|
|
102
|
+
fetch "$FFMPEG_URL" "$FFMPEG_SHA256" "ffmpeg-${FFMPEG_VERSION}.tar.xz"
|
|
103
|
+
fetch "$ZLIB_URL" "$ZLIB_SHA256" "zlib-${ZLIB_VERSION}.tar.gz"
|
|
104
|
+
rm -rf "ffmpeg-${FFMPEG_VERSION}" "zlib-${ZLIB_VERSION}" libvpx
|
|
105
|
+
tar -xJf "ffmpeg-${FFMPEG_VERSION}.tar.xz"
|
|
106
|
+
tar -xzf "zlib-${ZLIB_VERSION}.tar.gz"
|
|
107
|
+
git -c advice.detachedHead=false clone -q --depth 1 --branch "$LIBVPX_TAG" "$LIBVPX_REPO" libvpx
|
|
108
|
+
got_commit="$(git -C libvpx rev-parse HEAD)"
|
|
109
|
+
if [[ "$got_commit" != "$LIBVPX_COMMIT" ]]; then
|
|
110
|
+
echo "libvpx $LIBVPX_TAG is $got_commit, want $LIBVPX_COMMIT" >&2
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
echo "== zlib $ZLIB_VERSION"
|
|
115
|
+
(
|
|
116
|
+
cd "zlib-${ZLIB_VERSION}"
|
|
117
|
+
if [[ "$target" == windows-x64 ]]; then
|
|
118
|
+
make -s "${zlib_make[@]}" -j"$jobs" libz.a CFLAGS="-O2"
|
|
119
|
+
mkdir -p "$prefix/include" "$prefix/lib"
|
|
120
|
+
cp zlib.h zconf.h "$prefix/include/"
|
|
121
|
+
cp libz.a "$prefix/lib/"
|
|
122
|
+
else
|
|
123
|
+
CFLAGS="-O2 -fPIC $cflags" ./configure --static --prefix="$prefix" >/dev/null
|
|
124
|
+
make -s -j"$jobs"
|
|
125
|
+
make -s install >/dev/null
|
|
126
|
+
fi
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
echo "== libvpx $LIBVPX_TAG ($vpx_target)"
|
|
130
|
+
(
|
|
131
|
+
cd libvpx
|
|
132
|
+
./configure --prefix="$prefix" --target="$vpx_target" \
|
|
133
|
+
--enable-static --disable-shared --enable-pic \
|
|
134
|
+
--enable-vp8 --enable-vp9 --disable-examples --disable-tools --disable-docs \
|
|
135
|
+
--disable-unit-tests --disable-install-bins --disable-install-docs \
|
|
136
|
+
${cflags:+--extra-cflags="$cflags"} >/dev/null
|
|
137
|
+
make -s -j"$jobs" >/dev/null
|
|
138
|
+
make -s install >/dev/null
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
echo "== ffmpeg $FFMPEG_VERSION"
|
|
142
|
+
configure_args=(
|
|
143
|
+
--prefix="$prefix"
|
|
144
|
+
--pkg-config-flags=--static
|
|
145
|
+
--extra-cflags="-I$prefix/include $cflags"
|
|
146
|
+
--extra-ldflags="-L$prefix/lib $ldflags"
|
|
147
|
+
--disable-everything --disable-autodetect
|
|
148
|
+
--disable-doc --disable-debug --disable-network
|
|
149
|
+
--disable-ffplay --disable-ffprobe --disable-avdevice --disable-swresample
|
|
150
|
+
--enable-static --disable-shared
|
|
151
|
+
"${ffmpeg_flags[@]}"
|
|
152
|
+
--enable-zlib --enable-libvpx
|
|
153
|
+
--enable-protocol=file
|
|
154
|
+
--enable-demuxer=concat,image2,png_pipe
|
|
155
|
+
--enable-decoder=png
|
|
156
|
+
--enable-encoder=libvpx_vp8,libvpx_vp9
|
|
157
|
+
--enable-bsf=vp9_superframe
|
|
158
|
+
--enable-muxer=webm
|
|
159
|
+
--enable-filter=scale,format,null
|
|
160
|
+
)
|
|
161
|
+
(
|
|
162
|
+
cd "ffmpeg-${FFMPEG_VERSION}"
|
|
163
|
+
PKG_CONFIG_PATH="$prefix/lib/pkgconfig" ./configure "${configure_args[@]}" >"$work/configure.log" 2>&1 ||
|
|
164
|
+
{ tail -40 ffbuild/config.log; exit 1; }
|
|
165
|
+
make -s -j"$jobs" "ffmpeg$exe" >/dev/null
|
|
166
|
+
cp "ffmpeg$exe" "$out_dir/ffmpeg-7.1-$target$exe"
|
|
167
|
+
grep -q '^CONFIG_GPL=yes' ffbuild/config.mak && { echo 'GPL build: refusing' >&2; exit 1; }
|
|
168
|
+
grep -q '^CONFIG_NONFREE=yes' ffbuild/config.mak && { echo 'nonfree build: refusing' >&2; exit 1; }
|
|
169
|
+
cp COPYING.LGPLv2.1 "$out_dir/"
|
|
170
|
+
)
|
|
171
|
+
cp libvpx/LICENSE "$out_dir/libvpx-LICENSE"
|
|
172
|
+
cp libvpx/PATENTS "$out_dir/libvpx-PATENTS"
|
|
173
|
+
sed -n '/^Copyright notice:/,$p' "zlib-${ZLIB_VERSION}/README" >"$out_dir/zlib-LICENSE"
|
|
174
|
+
case "$target" in
|
|
175
|
+
linux-x64)
|
|
176
|
+
fetch "$MUSL_URL" "$MUSL_SHA256" "musl-${MUSL_VERSION}.tar.gz"
|
|
177
|
+
tar -xzOf "musl-${MUSL_VERSION}.tar.gz" "musl-${MUSL_VERSION}/COPYRIGHT" >"$out_dir/musl-COPYRIGHT"
|
|
178
|
+
;;
|
|
179
|
+
windows-x64)
|
|
180
|
+
# Static libgcc, mingw-w64 CRT and winpthreads: copy MSYS2's license files for them.
|
|
181
|
+
notices="$out_dir/mingw-w64-notices"
|
|
182
|
+
mkdir -p "$notices"
|
|
183
|
+
for dir in /mingw64/share/licenses/*; do
|
|
184
|
+
case "$(basename "$dir")" in
|
|
185
|
+
*winpthread* | *crt* | *gcc-libs* | *libwinpthread* | *headers*) cp -R "$dir" "$notices/" ;;
|
|
186
|
+
esac
|
|
187
|
+
done
|
|
188
|
+
[[ -n "$(ls -A "$notices")" ]] || { echo 'no mingw-w64 license files found' >&2; exit 1; }
|
|
189
|
+
;;
|
|
190
|
+
esac
|
|
191
|
+
mkdir -p "$out_dir/sources"
|
|
192
|
+
cp "ffmpeg-${FFMPEG_VERSION}.tar.xz" "zlib-${ZLIB_VERSION}.tar.gz" "$out_dir/sources/"
|
|
193
|
+
git -C libvpx archive --format=tar.gz --prefix="libvpx-${LIBVPX_TAG#v}/" -o "$out_dir/sources/libvpx-${LIBVPX_TAG#v}.tar.gz" HEAD
|
|
194
|
+
|
|
195
|
+
binary="$out_dir/ffmpeg-7.1-$target$exe"
|
|
196
|
+
# arm64 macOS will not run an unsigned binary; re-apply the linker's ad-hoc signature.
|
|
197
|
+
[[ "$target" == darwin-* ]] && codesign --force --sign - "$binary"
|
|
198
|
+
|
|
199
|
+
{
|
|
200
|
+
echo "target: $target"
|
|
201
|
+
echo "ffmpeg: $FFMPEG_VERSION $FFMPEG_URL sha256 $FFMPEG_SHA256"
|
|
202
|
+
echo "zlib: $ZLIB_VERSION $ZLIB_URL sha256 $ZLIB_SHA256"
|
|
203
|
+
echo "libvpx: $LIBVPX_TAG $LIBVPX_REPO commit $LIBVPX_COMMIT"
|
|
204
|
+
echo "configure: ${configure_args[*]}" | sed "s#$prefix#\$PREFIX#g"
|
|
205
|
+
echo "sha256: $(sha256 "$binary") $(basename "$binary")"
|
|
206
|
+
} >"$out_dir/BUILD-$target.txt"
|
|
207
|
+
cat "$out_dir/BUILD-$target.txt"
|
|
208
|
+
"$here/check.sh" "$binary" "$work/check"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Proves a built ffmpeg does what the generated VideoHelper asks of it:
|
|
3
|
+
# .github/ffmpeg/check.sh <ffmpeg binary> [work_dir]
|
|
4
|
+
# 1. links only (unless SKIP_LINKAGE is set) to libraries every machine of that OS has;
|
|
5
|
+
# 2. runs VideoHelper#ffmpeg_arguments' exact encode on sample PNG frames;
|
|
6
|
+
# 3. encodes the same frames with libvpx-vp9 too (built for later use);
|
|
7
|
+
# 4. if FFPROBE is set (or ffprobe is on PATH), checks both WebMs (probe.sh).
|
|
8
|
+
# Writes <work_dir>/video.webm and <work_dir>/vp9.webm.
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
binary="$1"
|
|
12
|
+
work="${2:-$(mktemp -d)}"
|
|
13
|
+
here="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
rm -rf "$work" && mkdir -p "$work"
|
|
15
|
+
|
|
16
|
+
echo "== linkage"
|
|
17
|
+
case "${SKIP_LINKAGE:+skip}$(uname -s)" in
|
|
18
|
+
skip*) echo '(skipped)' ;;
|
|
19
|
+
Darwin)
|
|
20
|
+
otool -L "$binary" | tail -n +2 | tee "$work/libs.txt"
|
|
21
|
+
if grep -v -E '^\s*/(usr/lib|System/Library)/' "$work/libs.txt"; then
|
|
22
|
+
echo 'links to a non-system library' >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
codesign --verify --verbose "$binary"
|
|
26
|
+
otool -l "$binary" | grep -A4 -E 'LC_BUILD_VERSION|LC_VERSION_MIN_MACOSX' | grep -E 'minos|version' | head -2
|
|
27
|
+
;;
|
|
28
|
+
Linux)
|
|
29
|
+
file "$binary"
|
|
30
|
+
file "$binary" | grep -q 'statically linked' || { echo 'not statically linked' >&2; exit 1; }
|
|
31
|
+
;;
|
|
32
|
+
MINGW* | MSYS* | CYGWIN*)
|
|
33
|
+
objdump -p "$binary" | grep 'DLL Name' | tee "$work/libs.txt"
|
|
34
|
+
# Only Windows system DLLs: no libgcc, libwinpthread, libvpx, zlib.
|
|
35
|
+
if grep -v -i -E 'DLL Name: (KERNEL32|msvcrt|ADVAPI32|USER32|bcrypt|ole32|shell32|api-ms-win-[a-z0-9-]+)\.dll' "$work/libs.txt"; then
|
|
36
|
+
echo 'links to a non-system DLL' >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
echo "== version"
|
|
43
|
+
"$binary" -hide_banner -version | head -3
|
|
44
|
+
if "$binary" -hide_banner -version | grep -q -- '--enable-gpl\|--enable-nonfree'; then
|
|
45
|
+
echo 'GPL or nonfree build' >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
"$binary" -hide_banner -encoders 2>/dev/null | grep -E 'libvpx' || { echo 'no libvpx encoder' >&2; exit 1; }
|
|
49
|
+
|
|
50
|
+
echo "== encode (VideoHelper#ffmpeg_arguments)"
|
|
51
|
+
ruby "$here/frames.rb" "$work"
|
|
52
|
+
# Relative names in list.txt resolve against the list's directory, as in the helper's tmpdir.
|
|
53
|
+
"$binary" -y -loglevel error -f concat -safe 0 -i "$work/list.txt" \
|
|
54
|
+
-vf "scale='min(1280,iw)':-2" -c:v libvpx -deadline realtime \
|
|
55
|
+
-b:v 1M -r 5 -pix_fmt yuv420p "$work/video.webm"
|
|
56
|
+
size="$(wc -c <"$work/video.webm" | tr -d ' ')"
|
|
57
|
+
echo "video.webm: $size bytes"
|
|
58
|
+
[[ "$size" -gt 1000 ]] || { echo 'video too small' >&2; exit 1; }
|
|
59
|
+
|
|
60
|
+
echo "== encode (VP9)"
|
|
61
|
+
"$binary" -y -loglevel error -f concat -safe 0 -i "$work/list.txt" \
|
|
62
|
+
-vf "scale='min(1280,iw)':-2" -c:v libvpx-vp9 -deadline realtime \
|
|
63
|
+
-b:v 1M -r 5 -pix_fmt yuv420p "$work/vp9.webm"
|
|
64
|
+
[[ "$(wc -c <"$work/vp9.webm" | tr -d ' ')" -gt 1000 ]] || { echo 'vp9 video too small' >&2; exit 1; }
|
|
65
|
+
|
|
66
|
+
probe="${FFPROBE:-$(command -v ffprobe || true)}"
|
|
67
|
+
if [[ -n "$probe" ]]; then
|
|
68
|
+
"$here/probe.sh" "$probe" "$work/video.webm"
|
|
69
|
+
"$here/probe.sh" "$probe" "$work/vp9.webm" vp9
|
|
70
|
+
else
|
|
71
|
+
echo "(no ffprobe here: the validate job probes this video)"
|
|
72
|
+
fi
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Writes sample "screenshots" and the concat list the generated VideoHelper builds:
|
|
4
|
+
# ruby frames.rb <dir> [width] [height] [count]
|
|
5
|
+
# RGBA PNGs with odd dimensions (so scale='min(1280,iw)':-2 has to round), stdlib only.
|
|
6
|
+
require 'zlib'
|
|
7
|
+
|
|
8
|
+
dir, width, height, count = ARGV
|
|
9
|
+
width = (width || 1366).to_i
|
|
10
|
+
height = (height || 657).to_i
|
|
11
|
+
count = (count || 12).to_i
|
|
12
|
+
|
|
13
|
+
def chunk(type, data)
|
|
14
|
+
[data.bytesize].pack('N') + type + data + [Zlib.crc32(type + data)].pack('N')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def png(width, height, shift)
|
|
18
|
+
rows = Array.new(height) do |y|
|
|
19
|
+
row = Array.new(width) do |x|
|
|
20
|
+
[(x + shift * 20) % 256, (y + shift * 7) % 256, (x ^ y) % 256, 255].pack('C4')
|
|
21
|
+
end
|
|
22
|
+
"\x00#{row.join}"
|
|
23
|
+
end
|
|
24
|
+
"\x89PNG\r\n\x1A\n".b +
|
|
25
|
+
chunk('IHDR', [width, height, 8, 6, 0, 0, 0].pack('NNCCCCC')) +
|
|
26
|
+
chunk('IDAT', Zlib::Deflate.deflate(rows.join, 1)) +
|
|
27
|
+
chunk('IEND', '')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Same shape as VideoHelper#concat_list: each frame lasts until the next, clamped to [1/fps, 2.0].
|
|
31
|
+
names = Array.new(count) { |i| format('frame-%06d.png', i + 1) }
|
|
32
|
+
names.each_with_index { |name, i| File.binwrite(File.join(dir, name), png(width, height, i)) }
|
|
33
|
+
lines = names.each_with_index.flat_map { |name, i| ["file '#{name}'", format('duration %.3f', i.even? ? 0.2 : 1.1)] }
|
|
34
|
+
File.write(File.join(dir, 'list.txt'), (lines << "file '#{names.last}'").join("\n") << "\n")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# probe.sh <ffprobe> <video.webm> [codec, default vp8]: one video stream of that codec, 1280x616 (1366x657 scaled to an even height),
|
|
3
|
+
# about 8 s long, and the whole file decodes without errors.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
probe="$1"
|
|
7
|
+
video="$2"
|
|
8
|
+
codec="${3:-vp8}"
|
|
9
|
+
stream="$("$probe" -v error -select_streams v -show_entries stream=codec_name,width,height,pix_fmt -of csv=p=0 "$video")"
|
|
10
|
+
format="$("$probe" -v error -show_entries format=format_name -of default=nw=1:nk=1 "$video")"
|
|
11
|
+
duration="$("$probe" -v error -show_entries format=duration -of default=nw=1:nk=1 "$video")"
|
|
12
|
+
echo "$(basename "$video"): stream $stream; format $format; duration $duration"
|
|
13
|
+
[[ "$stream" == "$codec,1280,616,yuv420p" ]] || { echo "unexpected stream: $stream" >&2; exit 1; }
|
|
14
|
+
[[ "$format" == "matroska,webm" ]] || { echo "unexpected format: $format" >&2; exit 1; }
|
|
15
|
+
awk -v d="$duration" 'BEGIN { exit !(d > 7 && d < 9) }' || { echo "unexpected duration: $duration" >&2; exit 1; }
|
|
16
|
+
errors="$("${probe%probe}mpeg" -v error -i "$video" -f null - 2>&1 || true)"
|
|
17
|
+
[[ -z "$errors" ]] || { echo "decode errors: $errors" >&2; exit 1; }
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
name: ffmpeg build
|
|
2
|
+
|
|
3
|
+
# Builds the four ffmpeg binaries that RubyRaider::Ffmpeg downloads for failure videos
|
|
4
|
+
# (.github/ffmpeg/build.sh), tests each one on a second runner, and uploads them as
|
|
5
|
+
# workflow artifacts. It never publishes: the ffmpeg-7.1 release is created by hand from
|
|
6
|
+
# the downloaded artifacts of one run, so the release bytes are the bytes MANIFEST pins.
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
pull_request:
|
|
10
|
+
paths:
|
|
11
|
+
- .github/ffmpeg/**
|
|
12
|
+
- .github/workflows/ffmpeg.yml
|
|
13
|
+
# check.sh repeats VideoHelper#ffmpeg_arguments: a new filter or codec there needs a rebuild.
|
|
14
|
+
- lib/generators/templates/helpers/video_helper.tt
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ffmpeg-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
name: build ${{ matrix.target }}
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
timeout-minutes: 60
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
include:
|
|
32
|
+
- { target: darwin-arm64, os: macos-15 }
|
|
33
|
+
- { target: darwin-x64, os: macos-15-intel }
|
|
34
|
+
- { target: linux-x64, os: ubuntu-24.04 }
|
|
35
|
+
- { target: windows-x64, os: windows-2022 }
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
38
|
+
with:
|
|
39
|
+
persist-credentials: false
|
|
40
|
+
|
|
41
|
+
- name: Build (macOS)
|
|
42
|
+
if: runner.os == 'macOS'
|
|
43
|
+
run: |
|
|
44
|
+
brew install nasm pkgconf
|
|
45
|
+
.github/ffmpeg/build.sh ${{ matrix.target }} dist
|
|
46
|
+
|
|
47
|
+
# Alpine/musl gives a fully static binary that runs on any x86_64 Linux.
|
|
48
|
+
- name: Build (Linux, Alpine container)
|
|
49
|
+
if: runner.os == 'Linux'
|
|
50
|
+
run: |
|
|
51
|
+
docker run --rm -v "$PWD:/w" -w /w alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc sh -c '
|
|
52
|
+
apk add --no-cache bash build-base nasm perl pkgconf git curl xz tar diffutils file ruby &&
|
|
53
|
+
.github/ffmpeg/build.sh linux-x64 dist'
|
|
54
|
+
|
|
55
|
+
- uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2
|
|
56
|
+
if: runner.os == 'Windows'
|
|
57
|
+
with:
|
|
58
|
+
msystem: MINGW64
|
|
59
|
+
update: false
|
|
60
|
+
install: >-
|
|
61
|
+
make diffutils git tar xz curl perl
|
|
62
|
+
mingw-w64-x86_64-gcc mingw-w64-x86_64-nasm mingw-w64-x86_64-pkgconf mingw-w64-x86_64-ruby
|
|
63
|
+
|
|
64
|
+
- name: Build (Windows, MSYS2 MINGW64)
|
|
65
|
+
if: runner.os == 'Windows'
|
|
66
|
+
shell: msys2 {0}
|
|
67
|
+
run: .github/ffmpeg/build.sh windows-x64 dist
|
|
68
|
+
|
|
69
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
70
|
+
with:
|
|
71
|
+
name: ffmpeg-7.1-${{ matrix.target }}
|
|
72
|
+
path: |
|
|
73
|
+
dist/ffmpeg-7.1-*
|
|
74
|
+
dist/BUILD-*.txt
|
|
75
|
+
dist/COPYING.LGPLv2.1
|
|
76
|
+
dist/libvpx-LICENSE
|
|
77
|
+
dist/libvpx-PATENTS
|
|
78
|
+
dist/zlib-LICENSE
|
|
79
|
+
dist/musl-COPYRIGHT
|
|
80
|
+
dist/mingw-w64-notices/
|
|
81
|
+
if-no-files-found: error
|
|
82
|
+
retention-days: 90
|
|
83
|
+
|
|
84
|
+
# The complete corresponding source, once (every target builds from the same pinned sources).
|
|
85
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
86
|
+
if: matrix.target == 'linux-x64'
|
|
87
|
+
with:
|
|
88
|
+
name: ffmpeg-7.1-sources
|
|
89
|
+
path: dist/sources/
|
|
90
|
+
if-no-files-found: error
|
|
91
|
+
retention-days: 90
|
|
92
|
+
|
|
93
|
+
# Each binary on a fresh runner (another macOS version, older glibc Linux, newer Windows; darwin-x64
|
|
94
|
+
# only has the macos-15-intel label), running VideoHelper's exact encode.
|
|
95
|
+
test:
|
|
96
|
+
name: test ${{ matrix.target }} on ${{ matrix.os }}
|
|
97
|
+
needs: build
|
|
98
|
+
runs-on: ${{ matrix.os }}
|
|
99
|
+
timeout-minutes: 15
|
|
100
|
+
strategy:
|
|
101
|
+
fail-fast: false
|
|
102
|
+
matrix:
|
|
103
|
+
include:
|
|
104
|
+
- { target: darwin-arm64, os: macos-26 }
|
|
105
|
+
- { target: darwin-x64, os: macos-15-intel }
|
|
106
|
+
- { target: linux-x64, os: ubuntu-22.04 }
|
|
107
|
+
- { target: windows-x64, os: windows-2025 }
|
|
108
|
+
defaults:
|
|
109
|
+
run:
|
|
110
|
+
shell: bash
|
|
111
|
+
steps:
|
|
112
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
113
|
+
with:
|
|
114
|
+
persist-credentials: false
|
|
115
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
116
|
+
with:
|
|
117
|
+
name: ffmpeg-7.1-${{ matrix.target }}
|
|
118
|
+
path: dist
|
|
119
|
+
- name: Encode like VideoHelper
|
|
120
|
+
run: |
|
|
121
|
+
bin="dist/ffmpeg-7.1-${{ matrix.target }}"
|
|
122
|
+
[ "${{ runner.os }}" = Windows ] && bin="$bin.exe"
|
|
123
|
+
chmod +x "$bin"
|
|
124
|
+
# Linkage was audited in the build job, where the toolchain's tools are.
|
|
125
|
+
SKIP_LINKAGE=1 .github/ffmpeg/check.sh "$bin" "$RUNNER_TEMP/check"
|
|
126
|
+
mv "$RUNNER_TEMP/check/video.webm" "video-${{ matrix.target }}.webm"
|
|
127
|
+
mv "$RUNNER_TEMP/check/vp9.webm" "vp9-${{ matrix.target }}.webm"
|
|
128
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
129
|
+
with:
|
|
130
|
+
name: video-${{ matrix.target }}
|
|
131
|
+
path: |
|
|
132
|
+
video-${{ matrix.target }}.webm
|
|
133
|
+
vp9-${{ matrix.target }}.webm
|
|
134
|
+
retention-days: 14
|
|
135
|
+
|
|
136
|
+
validate:
|
|
137
|
+
name: validate videos and print checksums
|
|
138
|
+
needs: test
|
|
139
|
+
runs-on: ubuntu-24.04
|
|
140
|
+
steps:
|
|
141
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
142
|
+
with:
|
|
143
|
+
persist-credentials: false
|
|
144
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
145
|
+
with:
|
|
146
|
+
path: artifacts
|
|
147
|
+
pattern: '{ffmpeg-7.1-*,video-*}'
|
|
148
|
+
- name: Probe every video with a reference ffprobe
|
|
149
|
+
run: |
|
|
150
|
+
sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg >/dev/null
|
|
151
|
+
for video in artifacts/video-*/video-*.webm; do .github/ffmpeg/probe.sh "$(command -v ffprobe)" "$video"; done
|
|
152
|
+
for video in artifacts/video-*/vp9-*.webm; do .github/ffmpeg/probe.sh "$(command -v ffprobe)" "$video" vp9; done
|
|
153
|
+
- name: Checksums for MANIFEST
|
|
154
|
+
run: |
|
|
155
|
+
{
|
|
156
|
+
echo '### ffmpeg-7.1 assets'
|
|
157
|
+
echo '```'
|
|
158
|
+
cd artifacts && sha256sum ffmpeg-7.1-*/ffmpeg-7.1-*
|
|
159
|
+
echo '```'
|
|
160
|
+
} | tee -a "$GITHUB_STEP_SUMMARY"
|