Merge fix/release-test
@@ -165,7 +165,6 @@ release:
165
165
needs: [verify:packages, package:appimage, package:ubuntu, package:arch, package:windows]
166
166
resource_group: release
167
167
script:
168
- apk add --no-cache jq
169
168
- bash scripts/ci/release.sh
170
169
171
170
# AUR (approach of timfa/legacy-cycles): after the release, publish
@@ -165,7 +165,6 @@ release:165
needs: [verify:packages, package:appimage, package:ubuntu, package:arch, package:windows]165
needs: [verify:packages, package:appimage, package:ubuntu, package:arch, package:windows]166
resource_group: release166
resource_group: release167
script:167
script:168
- apk add --no-cache jq169
- bash scripts/ci/release.sh168
- bash scripts/ci/release.sh170
169
171
# AUR (approach of timfa/legacy-cycles): after the release, publish170
# AUR (approach of timfa/legacy-cycles): after the release, publish@@ -18,23 +18,11 @@ tag="$CI_COMMIT_TAG"
18
18
[[ "$tag" == "v$version" ]] || { echo "tag $tag does not match package version $version" >&2; exit 1; }
19
19
20
20
b="paperdoll-renderer-${tag}"
21
asset() { jq -n --arg j "$1" --arg p "$2" --arg n "$3" '{job:$j,path:$p,name:$n}'; }
22
assets="$(jq -s . \
23
<(asset package:appimage dist/paperdoll-renderer-x86_64.AppImage "${b}-x86_64.AppImage") \
24
<(asset package:ubuntu dist/paperdoll-renderer_amd64.deb "${b}-amd64.deb") \
25
<(asset package:arch dist/paperdoll-renderer-x86_64.pkg.tar.zst "${b}-x86_64.pkg.tar.zst") \
26
<(asset package:windows dist/paperdoll-renderer-windows-x86_64.zip "${b}-windows-x86_64.zip") \
27
<(asset verify:packages dist/SHA256SUMS "${b}-SHA256SUMS.txt"))"
28
desc="Paperdoll Renderer ${version}.
29
30
* Linux AppImage (portable).
31
* Debian/Ubuntu .deb: /usr/bin/paperdoll-renderer, desktop entry, icons, .ppd MIME type.
32
* Arch .pkg.tar.zst, built from packaging/arch/PKGBUILD. AUR: paperdoll-renderer-bin.
33
* Windows zip (x86_64, MinGW build).
34
* SHA256SUMS of the package files.
35
36
The default starter bodies are \"Animated walking Male Base Mesh\" and \"Animated Female Base Mesh\" by assetfactory (Sketchfab Standard licence); see THIRD_PARTY_NOTICES.md."
37
body="$(jq -n --arg n "Paperdoll Renderer ${tag}" --arg b "$desc" --argjson a "$assets" '{name:$n,body:$b,assets:$a}')"
21
asset() { printf '{"job":"%s","path":"%s","name":"%s"}' "$1" "$2" "$3"; }
22
assets="$(asset package:appimage dist/paperdoll-renderer-x86_64.AppImage "${b}-x86_64.AppImage"),$(asset package:ubuntu dist/paperdoll-renderer_amd64.deb "${b}-amd64.deb"),$(asset package:arch dist/paperdoll-renderer-x86_64.pkg.tar.zst "${b}-x86_64.pkg.tar.zst"),$(asset package:windows dist/paperdoll-renderer-windows-x86_64.zip "${b}-windows-x86_64.zip"),$(asset verify:packages dist/SHA256SUMS "${b}-SHA256SUMS.txt")"
23
# JSON-escaped already (\n, no double quotes inside).
24
desc="Paperdoll Renderer ${version}.\n\n* Linux AppImage (portable).\n* Debian/Ubuntu .deb: /usr/bin/paperdoll-renderer, desktop entry, icons, .ppd MIME type.\n* Arch .pkg.tar.zst, built from packaging/arch/PKGBUILD. AUR: paperdoll-renderer-bin.\n* Windows zip (x86_64, MinGW build).\n* SHA256SUMS of the package files.\n\nThe default starter bodies are Animated walking Male Base Mesh and Animated Female Base Mesh by assetfactory (Sketchfab Standard licence); see THIRD_PARTY_NOTICES.md."
25
body="$(printf '{"name":"Paperdoll Renderer %s","body":"%s","assets":[%s]}' "$tag" "$desc" "$assets")"
38
26
39
27
echo "Release $tag on $CI_COMMIT_SHA (existing tag, create-only)"
40
28
resp="$(mktemp)"
@@ -18,23 +18,11 @@ tag="$CI_COMMIT_TAG"18
[[ "$tag" == "v$version" ]] || { echo "tag $tag does not match package version $version" >&2; exit 1; }18
[[ "$tag" == "v$version" ]] || { echo "tag $tag does not match package version $version" >&2; exit 1; }19
19
20
b="paperdoll-renderer-${tag}"20
b="paperdoll-renderer-${tag}"21
asset() { jq -n --arg j "$1" --arg p "$2" --arg n "$3" '{job:$j,path:$p,name:$n}'; }21
asset() { printf '{"job":"%s","path":"%s","name":"%s"}' "$1" "$2" "$3"; }22
assets="$(jq -s . \22
assets="$(asset package:appimage dist/paperdoll-renderer-x86_64.AppImage "${b}-x86_64.AppImage"),$(asset package:ubuntu dist/paperdoll-renderer_amd64.deb "${b}-amd64.deb"),$(asset package:arch dist/paperdoll-renderer-x86_64.pkg.tar.zst "${b}-x86_64.pkg.tar.zst"),$(asset package:windows dist/paperdoll-renderer-windows-x86_64.zip "${b}-windows-x86_64.zip"),$(asset verify:packages dist/SHA256SUMS "${b}-SHA256SUMS.txt")"23
<(asset package:appimage dist/paperdoll-renderer-x86_64.AppImage "${b}-x86_64.AppImage") \23
# JSON-escaped already (\n, no double quotes inside).24
<(asset package:ubuntu dist/paperdoll-renderer_amd64.deb "${b}-amd64.deb") \24
desc="Paperdoll Renderer ${version}.\n\n* Linux AppImage (portable).\n* Debian/Ubuntu .deb: /usr/bin/paperdoll-renderer, desktop entry, icons, .ppd MIME type.\n* Arch .pkg.tar.zst, built from packaging/arch/PKGBUILD. AUR: paperdoll-renderer-bin.\n* Windows zip (x86_64, MinGW build).\n* SHA256SUMS of the package files.\n\nThe default starter bodies are Animated walking Male Base Mesh and Animated Female Base Mesh by assetfactory (Sketchfab Standard licence); see THIRD_PARTY_NOTICES.md."25
<(asset package:arch dist/paperdoll-renderer-x86_64.pkg.tar.zst "${b}-x86_64.pkg.tar.zst") \25
body="$(printf '{"name":"Paperdoll Renderer %s","body":"%s","assets":[%s]}' "$tag" "$desc" "$assets")"26
<(asset package:windows dist/paperdoll-renderer-windows-x86_64.zip "${b}-windows-x86_64.zip") \27
<(asset verify:packages dist/SHA256SUMS "${b}-SHA256SUMS.txt"))"28
desc="Paperdoll Renderer ${version}.29
30
* Linux AppImage (portable).31
* Debian/Ubuntu .deb: /usr/bin/paperdoll-renderer, desktop entry, icons, .ppd MIME type.32
* Arch .pkg.tar.zst, built from packaging/arch/PKGBUILD. AUR: paperdoll-renderer-bin.33
* Windows zip (x86_64, MinGW build).34
* SHA256SUMS of the package files.35
36
The default starter bodies are \"Animated walking Male Base Mesh\" and \"Animated Female Base Mesh\" by assetfactory (Sketchfab Standard licence); see THIRD_PARTY_NOTICES.md."37
body="$(jq -n --arg n "Paperdoll Renderer ${tag}" --arg b "$desc" --argjson a "$assets" '{name:$n,body:$b,assets:$a}')"38
26
39
echo "Release $tag on $CI_COMMIT_SHA (existing tag, create-only)"27
echo "Release $tag on $CI_COMMIT_SHA (existing tag, create-only)"40
resp="$(mktemp)"28
resp="$(mktemp)"@@ -1,6 +1,6 @@
1
//! The tag publish step must not recreate the git tag and must not send
2
//! replace. BlueGit has no attach-to-existing-tag route; the script refuses
3
//! the create-release call instead of issuing it.
1
//! The tag publish step publishes onto the existing tag with BlueGit's
2
//! create-only route POST /repos/{owner}/{repo}/releases/{tag}/assets: it
3
//! must never call the tag-creating POST .../releases and never send replace.
4
4
5
5
#![cfg(unix)]
6
6
@@ -24,7 +24,7 @@ fn install_curl_trap(dir: &std::path::Path) -> PathBuf {
24
24
let curl = dir.join("curl");
25
25
fs::write(
26
26
&curl,
27
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"${CURL_TRAP_LOG:?}\"\nexit 0\n",
27
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"${CURL_TRAP_LOG:?}\"\nprintf 201\nexit 0\n",
28
28
)
29
29
.unwrap();
30
30
let mut permissions = fs::metadata(&curl).unwrap().permissions();
@@ -43,7 +43,7 @@ fn executable_lines(script: &str) -> String {
43
43
}
44
44
45
45
#[test]
46
fn tag_publish_does_not_call_create_release_or_send_replace() {
46
fn tag_publish_uses_only_the_existing_tag_route_without_replace() {
47
47
let dir = std::env::temp_dir().join(format!("paperdoll-release-trap-{}", std::process::id()));
48
48
let _ = fs::remove_dir_all(&dir);
49
49
fs::create_dir_all(&dir).unwrap();
@@ -67,24 +67,34 @@ fn tag_publish_does_not_call_create_release_or_send_replace() {
67
67
let stdout = String::from_utf8_lossy(&output.stdout);
68
68
assert_eq!(
69
69
output.status.code(),
70
Some(3),
70
Some(0),
71
71
"stdout:\n{stdout}\nstderr:\n{stderr}"
72
72
);
73
assert!(
74
stderr.contains("release-blocked: no-attach-endpoint"),
75
"{stderr}"
73
let calls = fs::read_to_string(&log).unwrap();
74
assert_eq!(
75
calls.lines().filter(|l| l.contains("http")).count(),
76
1,
77
"{calls}"
76
78
);
77
79
assert!(
78
!log.exists(),
79
"curl was invoked:\n{}",
80
fs::read_to_string(&log).unwrap_or_default()
80
calls.contains(
81
"https://git.example.invalid/api/v1/repos/golden-age/paperdoll-renderer/releases/v0.1.0/assets"
82
),
83
"{calls}"
81
84
);
85
assert!(!calls.contains("replace"), "{calls}");
86
assert!(!calls.contains("\"tag\""), "{calls}");
87
for name in [
88
"paperdoll-renderer-v0.1.0-x86_64.AppImage",
89
"paperdoll-renderer-v0.1.0-amd64.deb",
90
"paperdoll-renderer-v0.1.0-x86_64.pkg.tar.zst",
91
"paperdoll-renderer-v0.1.0-windows-x86_64.zip",
92
"paperdoll-renderer-v0.1.0-SHA256SUMS.txt",
93
] {
94
assert!(calls.contains(name), "{name} missing: {calls}");
95
}
82
96
let script = fs::read_to_string(repo_root().join("scripts/ci/release.sh")).unwrap();
83
97
let executable = executable_lines(&script);
84
assert!(
85
!executable.contains("curl"),
86
"executable release script must not call curl:\n{executable}"
87
);
88
98
assert!(
89
99
!executable.contains("replace"),
90
100
"executable release script must not send replace:\n{executable}"
@@ -93,7 +103,7 @@ fn tag_publish_does_not_call_create_release_or_send_replace() {
93
103
}
94
104
95
105
#[test]
96
fn tag_publish_still_rejects_a_non_semver_tag_before_the_api_block() {
106
fn tag_publish_rejects_a_non_semver_tag_before_the_api() {
97
107
let dir =
98
108
std::env::temp_dir().join(format!("paperdoll-release-trap-bad-{}", std::process::id()));
99
109
let _ = fs::remove_dir_all(&dir);
@@ -1,6 +1,6 @@1
//! The tag publish step must not recreate the git tag and must not send1
//! The tag publish step publishes onto the existing tag with BlueGit's2
//! replace. BlueGit has no attach-to-existing-tag route; the script refuses2
//! create-only route POST /repos/{owner}/{repo}/releases/{tag}/assets: it3
//! the create-release call instead of issuing it.3
//! must never call the tag-creating POST .../releases and never send replace.4
4
5
#![cfg(unix)]5
#![cfg(unix)]6
6
@@ -24,7 +24,7 @@ fn install_curl_trap(dir: &std::path::Path) -> PathBuf {24
let curl = dir.join("curl");24
let curl = dir.join("curl");25
fs::write(25
fs::write(26
&curl,26
&curl,27
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"${CURL_TRAP_LOG:?}\"\nexit 0\n",27
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"${CURL_TRAP_LOG:?}\"\nprintf 201\nexit 0\n",28
)28
)29
.unwrap();29
.unwrap();30
let mut permissions = fs::metadata(&curl).unwrap().permissions();30
let mut permissions = fs::metadata(&curl).unwrap().permissions();@@ -43,7 +43,7 @@ fn executable_lines(script: &str) -> String {43
}43
}44
44
45
#[test]45
#[test]46
fn tag_publish_does_not_call_create_release_or_send_replace() {46
fn tag_publish_uses_only_the_existing_tag_route_without_replace() {47
let dir = std::env::temp_dir().join(format!("paperdoll-release-trap-{}", std::process::id()));47
let dir = std::env::temp_dir().join(format!("paperdoll-release-trap-{}", std::process::id()));48
let _ = fs::remove_dir_all(&dir);48
let _ = fs::remove_dir_all(&dir);49
fs::create_dir_all(&dir).unwrap();49
fs::create_dir_all(&dir).unwrap();@@ -67,24 +67,34 @@ fn tag_publish_does_not_call_create_release_or_send_replace() {67
let stdout = String::from_utf8_lossy(&output.stdout);67
let stdout = String::from_utf8_lossy(&output.stdout);68
assert_eq!(68
assert_eq!(69
output.status.code(),69
output.status.code(),70
Some(3),70
Some(0),71
"stdout:\n{stdout}\nstderr:\n{stderr}"71
"stdout:\n{stdout}\nstderr:\n{stderr}"72
);72
);73
assert!(73
let calls = fs::read_to_string(&log).unwrap();74
stderr.contains("release-blocked: no-attach-endpoint"),74
assert_eq!(75
"{stderr}"75
calls.lines().filter(|l| l.contains("http")).count(),76
1,77
"{calls}"76
);78
);77
assert!(79
assert!(78
!log.exists(),80
calls.contains(79
"curl was invoked:\n{}",81
"https://git.example.invalid/api/v1/repos/golden-age/paperdoll-renderer/releases/v0.1.0/assets"80
fs::read_to_string(&log).unwrap_or_default()82
),83
"{calls}"81
);84
);85
assert!(!calls.contains("replace"), "{calls}");86
assert!(!calls.contains("\"tag\""), "{calls}");87
for name in [88
"paperdoll-renderer-v0.1.0-x86_64.AppImage",89
"paperdoll-renderer-v0.1.0-amd64.deb",90
"paperdoll-renderer-v0.1.0-x86_64.pkg.tar.zst",91
"paperdoll-renderer-v0.1.0-windows-x86_64.zip",92
"paperdoll-renderer-v0.1.0-SHA256SUMS.txt",93
] {94
assert!(calls.contains(name), "{name} missing: {calls}");95
}82
let script = fs::read_to_string(repo_root().join("scripts/ci/release.sh")).unwrap();96
let script = fs::read_to_string(repo_root().join("scripts/ci/release.sh")).unwrap();83
let executable = executable_lines(&script);97
let executable = executable_lines(&script);84
assert!(85
!executable.contains("curl"),86
"executable release script must not call curl:\n{executable}"87
);88
assert!(98
assert!(89
!executable.contains("replace"),99
!executable.contains("replace"),90
"executable release script must not send replace:\n{executable}"100
"executable release script must not send replace:\n{executable}"@@ -93,7 +103,7 @@ fn tag_publish_does_not_call_create_release_or_send_replace() {93
}103
}94
104
95
#[test]105
#[test]96
fn tag_publish_still_rejects_a_non_semver_tag_before_the_api_block() {106
fn tag_publish_rejects_a_non_semver_tag_before_the_api() {97
let dir =107
let dir =98
std::env::temp_dir().join(format!("paperdoll-release-trap-bad-{}", std::process::id()));108
std::env::temp_dir().join(format!("paperdoll-release-trap-bad-{}", std::process::id()));99
let _ = fs::remove_dir_all(&dir);109
let _ = fs::remove_dir_all(&dir);