Public Access
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad1a129159 | ||
|
|
e240c0cf26 |
No files matched your search
+48
-24
@@ -1221,27 +1221,55 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
curl -fsS -X DELETE -H "$auth" "$api/tags/$TAG" >/dev/null 2>&1 || true
|
curl -fsS -X DELETE -H "$auth" "$api/tags/$TAG" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
# vpk rather than curl, unlike the android job above, and the difference is what is being
|
# curl, like the android job above, and no longer `vpk upload gitea`. vpk's Gitea client is a
|
||||||
# uploaded. An APK is one file the client is told about by a manifest this repository writes; a
|
# generated SDK that cannot read the forge's answer to "create a release" since its upgrade to
|
||||||
# Velopack release is a set of files plus an index whose format is Velopack's own. Writing that
|
# 28.0.0: the release is created and the client gets null back, so both 1.2.0 and 1.2.161 threw a
|
||||||
# index by hand would be reimplementing the tool that is already here.
|
# NullReferenceException before uploading anything, leaving an empty draft behind on every push.
|
||||||
|
# Nothing here writes Velopack's index by hand: `vpk pack` already wrote releases.win-nightly.json
|
||||||
|
# beside the packages, and with no earlier release downloaded into that directory it is exactly
|
||||||
|
# the index `vpk upload` would have generated from it.
|
||||||
#
|
#
|
||||||
# --pre is load-bearing twice over. It keeps this out of the release channel, which refuses
|
# A draft first, published only once everything is on it, which is the order vpk used too: the
|
||||||
|
# client reads the index and then fetches what it names, and a published release missing a
|
||||||
|
# package is a few seconds in which every desktop is told to download something that is not
|
||||||
|
# there. The index goes up last for the same reason.
|
||||||
|
#
|
||||||
|
# prerelease is load-bearing twice over. It keeps this out of the release channel, which refuses
|
||||||
# prereleases — and it keeps it out of `releases/latest`, which is what the *phone's* release
|
# prereleases — and it keeps it out of `releases/latest`, which is what the *phone's* release
|
||||||
# channel reads: a desktop nightly published as a stable release would become the newest release
|
# channel reads: a desktop nightly published as a stable release would become the newest release
|
||||||
# in this repository and every phone on the release channel would start failing its update check
|
# in this repository and every phone on the release channel would start failing its update check
|
||||||
# against a release carrying no android manifest.
|
# against a release carrying no android manifest.
|
||||||
dotnet vpk upload gitea \
|
releases="$RUNNER_TEMP/nightly-releases"
|
||||||
--skip-updates \
|
|
||||||
--repoUrl "$FORGE/$REPO" \
|
created="$(curl -fsS -X POST -H "$auth" -H 'Content-Type: application/json' \
|
||||||
--token "$TOKEN" \
|
-d "$(printf '{"tag_name":"%s","target_commitish":"%s","name":"Nightly desktop %s","draft":true,"prerelease":true,"body":"Built from %s by CI. Installs beside the release build, never over it."}' \
|
||||||
--outputDir "$RUNNER_TEMP/nightly-releases" \
|
"$TAG" "$GITHUB_SHA" "$VERSION" "$GITHUB_SHA")" \
|
||||||
--channel win-nightly \
|
"$api/releases")"
|
||||||
--tag "$TAG" \
|
|
||||||
--releaseName "Nightly desktop $VERSION" \
|
draft="$(printf '%s' "$created" | release_id)"
|
||||||
--targetCommitish "$GITHUB_SHA" \
|
if [ -z "$draft" ]; then
|
||||||
--pre \
|
echo "Gitea accepted the release call and returned no id:" >&2
|
||||||
--publish
|
printf '%s\n' "$created" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in "$releases"/*-full.nupkg "$releases"/*-Portable.zip "$releases"/*-Setup.exe \
|
||||||
|
"$releases"/releases.win-nightly.json; do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "vpk pack did not leave $file behind:" >&2
|
||||||
|
ls -la "$releases" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
name="$(basename "$file")"
|
||||||
|
echo "Uploading $name ($(wc -c < "$file") bytes)"
|
||||||
|
curl -fsS -X POST -H "$auth" \
|
||||||
|
-F "attachment=@$file" \
|
||||||
|
"$api/releases/$draft/assets?name=$name" >/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
curl -fsS -X PATCH -H "$auth" -H 'Content-Type: application/json' \
|
||||||
|
-d '{"draft":false}' \
|
||||||
|
"$api/releases/$draft" >/dev/null
|
||||||
|
|
||||||
# Asked for back rather than assumed, and the android job's history is why: it once created a
|
# Asked for back rather than assumed, and the android job's history is why: it once created a
|
||||||
# release, uploaded nothing, and reported success for every upload it never made. A nightly
|
# release, uploaded nothing, and reported success for every upload it never made. A nightly
|
||||||
@@ -1260,20 +1288,16 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# â—† THE RPM GOES ONTO THE RELEASE vpk JUST MADE, RATHER THAN ONTO ONE OF ITS OWN.
|
# â—† THE RPM GOES ONTO THE RELEASE JUST MADE, RATHER THAN ONTO ONE OF ITS OWN.
|
||||||
#
|
#
|
||||||
# One rolling release carrying both heads, because they are one nightly: the same commit,
|
# One rolling release carrying both heads, because they are one nightly: the same commit,
|
||||||
# the same version, and one page a person looks at to find out what is on main. A second tag
|
# the same version, and one page a person looks at to find out what is on main. A second tag
|
||||||
# would double the delete-and-recreate dance this step already does, and would let the two
|
# would double the delete-and-recreate dance this step already does, and would let the two
|
||||||
# drift apart on a run where one of them failed.
|
# drift apart on a run where one of them failed.
|
||||||
#
|
#
|
||||||
# It has to happen here, after `vpk upload`, and not in a step before it. vpk deletes and
|
# It has to happen here, after the Windows upload, and not in a step before it. This step
|
||||||
# recreates this release, so an RPM uploaded first would be attached to something that no
|
# deletes and recreates the release, so an RPM uploaded first would be attached to something
|
||||||
# longer exists by the time the job ends — silently, because the delete succeeds.
|
# that no longer exists by the time the job ends — silently, because the delete succeeds.
|
||||||
#
|
|
||||||
# curl rather than vpk, unlike the assets above, for the reason the android job gives about
|
|
||||||
# its APK: this is one file the release simply carries. There is no index to write and no
|
|
||||||
# format that belongs to a tool.
|
|
||||||
release="$(printf '%s' "$published" | release_id)"
|
release="$(printf '%s' "$published" | release_id)"
|
||||||
if [ -z "$release" ]; then
|
if [ -z "$release" ]; then
|
||||||
echo "The release was published and its id could not be read back:" >&2
|
echo "The release was published and its id could not be read back:" >&2
|
||||||
|
|||||||
@@ -15,28 +15,39 @@ public sealed class ProbedDeviceKeyStoreTests
|
|||||||
{
|
{
|
||||||
private static CancellationToken Token => TestContext.Current.CancellationToken;
|
private static CancellationToken Token => TestContext.Current.CancellationToken;
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// "Off the caller's thread" is proved by blocking, not by comparing thread ids. The probe waits for a
|
||||||
|
/// release the test only gives once construction has returned, so a probe run on the thread that
|
||||||
|
/// constructed the store could never be released and times out instead. Thread ids proved nothing: the
|
||||||
|
/// test itself runs on a pool thread, and that same thread is free to pick up the queued probe the
|
||||||
|
/// moment the test awaits, which is what failed on CI with the code doing exactly the right thing.
|
||||||
|
/// </remarks>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TheChoiceIsMadeOnce_OffTheCallersThread()
|
public async Task TheChoiceIsMadeOnce_OffTheCallersThread()
|
||||||
{
|
{
|
||||||
var asking = Environment.CurrentManagedThreadId;
|
|
||||||
var probes = 0;
|
var probes = 0;
|
||||||
var probedOn = 0;
|
var releasedInTime = false;
|
||||||
|
|
||||||
|
using var release = new ManualResetEventSlim();
|
||||||
var inner = Substitute.For<IDeviceKeyStore>();
|
var inner = Substitute.For<IDeviceKeyStore>();
|
||||||
|
|
||||||
var store = new ProbedDeviceKeyStore(() =>
|
var store = new ProbedDeviceKeyStore(() =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref probes);
|
Interlocked.Increment(ref probes);
|
||||||
probedOn = Environment.CurrentManagedThreadId;
|
releasedInTime = release.Wait(TimeSpan.FromSeconds(10));
|
||||||
return inner;
|
return inner;
|
||||||
});
|
});
|
||||||
|
|
||||||
(await store.IsAvailableAsync(Token)).ShouldBeTrue();
|
var available = store.IsAvailableAsync(Token);
|
||||||
|
available.IsCompleted.ShouldBeFalse("nothing has released the probe yet");
|
||||||
|
release.Set();
|
||||||
|
|
||||||
|
(await available).ShouldBeTrue();
|
||||||
(await store.IsAvailableAsync(Token)).ShouldBeTrue();
|
(await store.IsAvailableAsync(Token)).ShouldBeTrue();
|
||||||
await store.TryLoadAsync(Token);
|
await store.TryLoadAsync(Token);
|
||||||
|
|
||||||
|
releasedInTime.ShouldBeTrue("the probe ran on the thread that asked, so nothing could release it");
|
||||||
probes.ShouldBe(1);
|
probes.ShouldBe(1);
|
||||||
probedOn.ShouldNotBe(asking);
|
|
||||||
|
|
||||||
// Availability is the choice, not a second probe through the store it chose.
|
// Availability is the choice, not a second probe through the store it chose.
|
||||||
await inner.DidNotReceive().IsAvailableAsync(Arg.Any<CancellationToken>());
|
await inner.DidNotReceive().IsAvailableAsync(Arg.Any<CancellationToken>());
|
||||||
|
|||||||
Reference in new issue
Block a user