From 39d2e801452f2fab8b07f9f9ebd0cf2a06ef027a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Sun, 20 Sep 2026 02:01:36 +0200 Subject: [PATCH] ci(release): refuse empty assets and verify what the release serves Assisted-by: DeepSeek V4.1 Flash --- .gitea/workflows/release.yml | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index fda6674..1bf69cc 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -325,6 +325,17 @@ jobs: chomp $id; my @files = grep { -f $_ } glob(q{dist/*/*}); @files or die qq{ERROR: no assets under dist/\n}; + # A file that arrived empty from the artifact step would be uploaded as an + # empty attachment, every status would still be 201, and the run would go + # green over a release nobody can install. Refuse it here, before the + # upload, and verify what was stored afterwards. + my %size; + for my $path (@files) { + my $n = -s $path // 0; + (my $name = $path) =~ s{.*/}{}; + $n > 0 or die qq{ERROR: $path is empty, so there is nothing to upload\n}; + $size{$name} = $n; + } my $bad = 0; for my $path (@files) { (my $name = $path) =~ s{.*/}{}; @@ -346,5 +357,31 @@ jobs: printf qq{%s: HTTP %s\n}, $name, $code; $bad = 1 if $code ne q{201}; } + # Read every asset back through the release download route and require the + # served length to be the file that was sent: stored but empty is a broken + # release however green the run looks. + open(my $v, q{<}, q{version-no-v.txt}) or die qq{version-no-v.txt: $!}; + my $v = <$v>; + close($v); + chomp $v; + for my $name (sort keys %size) { + my $url = qq{$ENV{GITEA_SERVER_URL}/$ENV{GITEA_REPOSITORY}/releases/download/v$v/$name}; + my @head = (q{curl}, q{-sS}, q{-I}, q{-H}, qq{Authorization: token $ENV{GITEA_TOKEN}}, $url); + open(my $h, q{-|}, @head) or die qq{curl: $!}; + my $len; + my $status; + while (my $l = <$h>) { + $status = $1 if $l =~ m{^HTTP/\S+\s+(\d+)}; + $len = $1 if $l =~ m{^content-length:\s*(\d+)}i; + } + my $ok = close($h); + $len = defined $len ? $len : 0; + if (!$ok || $status != 200 || $len != $size{$name}) { + printf qq{ERROR: %s serves %s bytes, expected %d\n}, $name, $len, $size{$name}; + $bad = 1; + next; + } + printf qq{%s: serves %d bytes\n}, $name, $len; + } exit($bad ? 1 : 0); '