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
59 changes: 51 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ on:
required: false
default: true
type: boolean
keystore_base64:
description: "Base64-encoded keystore (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_KEYSTORE_BASE64 secret."
required: false
default: ""
type: string
key_alias:
description: "Signing key alias (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_SIGNING_KEY_ALIAS secret."
required: false
default: ""
type: string
key_password:
description: "Signing key password (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_SIGNING_KEY_PASSWORD secret."
required: false
default: ""
type: string
Comment on lines +20 to +34

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow_dispatch inputs keystore_base64, key_alias, and key_password are not secret-protected by GitHub Actions (their values are stored in the workflow run metadata and may be visible to anyone with access to the run). Using them for an Android signing keystore/password risks leaking the private signing key. Consider removing these inputs and only supporting repository/environment secrets (or another secret-protected mechanism) for providing signing material.

Suggested change
keystore_base64:
description: "Base64-encoded keystore (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_KEYSTORE_BASE64 secret."
required: false
default: ""
type: string
key_alias:
description: "Signing key alias (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_SIGNING_KEY_ALIAS secret."
required: false
default: ""
type: string
key_password:
description: "Signing key password (from SAVE-THESE-SECRETS.txt). Overrides ANDROID_SIGNING_KEY_PASSWORD secret."
required: false
default: ""
type: string

Copilot uses AI. Check for mistakes.

jobs:
build-android:
Expand Down Expand Up @@ -82,17 +97,37 @@ jobs:
run: |
KS_PATH="${{ github.workspace }}/osu.Android/osu.keystore"

if [ -n "$KEYSTORE_BASE64" ]; then
# ── User provided a persistent keystore secret ──────────────
echo "$KEYSTORE_BASE64" | base64 --decode > "$KS_PATH"
# Priority: workflow_dispatch inputs > repository secrets > auto-generate.
# This lets users paste values from SAVE-THESE-SECRETS.txt directly into
# the "Run workflow" form so they don't need to set up repo secrets.
# Note: the auto-generated keystore uses the same password for both key and
# store, so a single key_password input covers both. Users with separate
# passwords should use repository secrets instead of workflow inputs.
EFFECTIVE_KS="${INPUT_KEYSTORE_BASE64:-$KEYSTORE_BASE64}"
EFFECTIVE_ALIAS="${INPUT_KEY_ALIAS:-$KEY_ALIAS_SECRET}"
EFFECTIVE_PASS="${INPUT_KEY_PASSWORD:-$KEY_PASS_SECRET}"
EFFECTIVE_STORE_PASS="${INPUT_KEY_PASSWORD:-${STORE_PASS_SECRET:-$EFFECTIVE_PASS}}"

# Mask passwords so they never appear in logs.
if [ -n "$EFFECTIVE_PASS" ]; then echo "::add-mask::$EFFECTIVE_PASS"; fi
if [ -n "$EFFECTIVE_STORE_PASS" ]; then echo "::add-mask::$EFFECTIVE_STORE_PASS"; fi

if [ -n "$EFFECTIVE_KS" ]; then
# ── User provided a keystore (via input or secret) ──────────────
echo "$EFFECTIVE_KS" | base64 --decode > "$KS_PATH"
Comment on lines +106 to +117

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a user-provided keystore is used, the base64 keystore content itself is still highly sensitive (it contains the private key). Even if this step doesn’t currently print it, it’s safer to mask EFFECTIVE_KS/INPUT_KEYSTORE_BASE64 via ::add-mask:: to avoid accidental leakage via debugging or error output.

Copilot uses AI. Check for mistakes.
echo "has_keystore=true" >> "$GITHUB_OUTPUT"
echo "generated=false" >> "$GITHUB_OUTPUT"
echo "key_alias=$KEY_ALIAS_SECRET" >> "$GITHUB_OUTPUT"
echo "key_pass=$KEY_PASS_SECRET" >> "$GITHUB_OUTPUT"
echo "store_pass=$STORE_PASS_SECRET" >> "$GITHUB_OUTPUT"
echo "✅ Using saved keystore from repository secrets."
echo "key_alias=${EFFECTIVE_ALIAS:-osu-release}" >> "$GITHUB_OUTPUT"
echo "key_pass=${EFFECTIVE_PASS}" >> "$GITHUB_OUTPUT"
echo "store_pass=${EFFECTIVE_STORE_PASS}" >> "$GITHUB_OUTPUT"
Comment on lines +115 to +122

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If EFFECTIVE_KS is set but EFFECTIVE_PASS (and/or EFFECTIVE_ALIAS) is empty, the workflow will continue and dotnet publish will receive empty signing parameters, which will likely fail later with a less actionable error. Add an explicit validation/early-exit here to require the necessary password/alias fields whenever a keystore is provided.

Copilot uses AI. Check for mistakes.

if [ -n "$INPUT_KEYSTORE_BASE64" ]; then
echo "✅ Using keystore from workflow dispatch inputs."
else
echo "✅ Using saved keystore from repository secrets."
fi
else
# ── No secret → auto-generate a keystore for this build ─────
# ── No keystore → auto-generate one for this build ──────────────
# The APK will install fine on any device, but UPDATING from a
# previous build signed with a DIFFERENT key will fail.
# To avoid that, save the generated keystore as a secret
Expand Down Expand Up @@ -141,6 +176,9 @@ jobs:
KEY_ALIAS_SECRET: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
KEY_PASS_SECRET: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}
STORE_PASS_SECRET: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}
INPUT_KEYSTORE_BASE64: ${{ inputs.keystore_base64 }}
INPUT_KEY_ALIAS: ${{ inputs.key_alias }}
INPUT_KEY_PASSWORD: ${{ inputs.key_password }}

- name: Determine version
id: version
Expand Down Expand Up @@ -308,6 +346,11 @@ jobs:
echo ""
echo " ⚡ After saving the secrets, all future builds will use the"
echo " same keystore automatically — no more setup needed."
echo ""
echo " 💡 QUICK OPTION: You can also paste the values from"
echo " SAVE-THESE-SECRETS.txt directly into the workflow"
echo " dispatch inputs (keystore_base64, key_alias, key_password)"
echo " when running the 'Build Android APK' workflow manually."
echo "=================================================================="

# Create a GitHub Release with the APK attached.
Expand Down
33 changes: 33 additions & 0 deletions osu.Game.Tests/Visual/RankedPlay/TestSceneStarRatingSequence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Intro;

namespace osu.Game.Tests.Visual.RankedPlay
{
[TestFixture]
public partial class TestSceneStarRatingSequence : RankedPlayTestScene
{
[Test]
public void TestBasicAppearance()
{
float starRating = 5;

AddSliderStep("set star rating", 0f, 10, 5, sr => starRating = sr);
AddStep("play sequence", () =>
{
StarRatingSequence sequence;

Child = sequence = new StarRatingSequence
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
double delay = 0;
sequence.Play(ref delay, starRating);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Intro
{
Expand All @@ -35,27 +35,26 @@ public partial class StarRatingSequence : CompositeDrawable
private float lastTickStdDev;

[BackgroundDependencyLoader]
private void load(OsuColour colour, AudioManager audio)
private void load(OsuColour colour, OverlayColourProvider overlayColourProvider, AudioManager audio)
{
Width = 600;
AutoSizeAxes = Axes.Y;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Alpha = 0;

Masking = true;
CornerRadius = 10;

InternalChild = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Masking = true,
CornerRadius = 10,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
Alpha = 0.2f,
Colour = overlayColourProvider.Background5,
Alpha = 0.8f,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
Expand Down Expand Up @@ -88,7 +87,7 @@ private void load(OsuColour colour, AudioManager audio)
new Box
{
Alpha = 0.4f,
Colour = Color4.Black,
Colour = overlayColourProvider.Background4,
RelativeSizeAxes = Axes.Both,
},
bars = new Container<Bar>
Expand Down Expand Up @@ -204,7 +203,7 @@ public void Play(ref double delay, float starRating)
RelativePositionAxes = Axes.X,
X = starRating * 0.1f,
Y = 34,
Colour = colours.ForStarDifficulty(starRating),
Colour = starRating < OsuColour.STAR_DIFFICULTY_DEFINED_COLOUR_CUTOFF ? colours.ForStarDifficulty(starRating) : colours.ForStarDifficultyText(starRating),
Spacing = new Vector2(4, 0),
Children =
[
Expand All @@ -226,6 +225,8 @@ public void Play(ref double delay, float starRating)
};

centerContainer.Add(container);
// Avoid text getting masked out by inner containers
AddInternal(container.CreateProxy());

container.FadeInFromZero(200)
.ScaleTo(0)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/osu.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<PackageReference Include="Realm" Version="20.1.0" />
<!-- Use winnerspiros/osu-framework fork (net10.0, optimized) via submodule instead of ppy NuGet package -->
<ProjectReference Include="..\submodules\osu-framework\osu.Framework\osu.Framework.csproj" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2026.411.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2026.420.0" />
<PackageReference Include="Sentry" Version="6.2.0" />
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
<PackageReference Include="SharpCompress" Version="0.47.3" />
Expand Down
2 changes: 1 addition & 1 deletion submodules/osu-framework
Loading