Add new release system

As AppVeyor took our project down and deleted it without any comments, we are switching to GitHub Releases earlier than anticipated.

This isn't the most elegant design (and I would have prefered having a release manifest in place) but this will do for now.

The concept of release channel was also defined with this change.

The new base version is now 1.1.x to avoid confusion with older system.

Standard test CI was disabled temporarly and may be chained later as a CI job after the release job.

Users are expected to redownload the emulator to be sure to be up to date.

PS: If someone from AppVeyor read this, thanks again for ruining my week-end, I will be sure to NEVER recommend you to anyone.

Best Regards, Mary.
This commit is contained in:
Mary 2022-01-22 17:56:09 +01:00
parent 646d4dd5c7
commit 23000eeb9a
7 changed files with 192 additions and 30 deletions

View file

@ -3,13 +3,13 @@ name: Build job
on:
workflow_dispatch:
inputs: {}
push:
branches: [ master ]
paths-ignore:
- '.github/*'
- '.github/ISSUE_TEMPLATE/**'
- '*.yml'
- 'README.md'
#push:
# branches: [ master ]
# paths-ignore:
# - '.github/*'
# - '.github/ISSUE_TEMPLATE/**'
# - '*.yml'
# - 'README.md'
pull_request:
branches: [ master ]
paths-ignore:
@ -59,14 +59,14 @@ jobs:
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
- name: Build
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.0.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.1.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
run: dotnet test -c "${{ matrix.configuration }}"
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
if: github.event_name == 'pull_request'
- name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
if: github.event_name == 'pull_request'
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v2

95
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,95 @@
name: Release job
on:
workflow_dispatch:
inputs: {}
push:
branches: [ master ]
paths-ignore:
- '.github/*'
- '.github/ISSUE_TEMPLATE/**'
- '*.yml'
- 'README.md'
jobs:
release:
runs-on: windows-latest
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
- name: Get version info
id: version_info
run: |
echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} + 7181))"
echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
shell: bash
- name: Configure for release
run: |
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs
shell: bash
- name: Create output dir
run: "mkdir release_output"
- name: Publish Windows
run: |
dotnet publish -c Release -r win-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r win-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
- name: Packing Windows builds
run: |
pushd publish_windows
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
pushd publish_windows_sdl2_headless
7z a ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
shell: bash
- name: Publish Linux
run: |
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
- name: Packing Linux builds
run: |
pushd publish_linux
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
popd
pushd publish_linux_sdl2_headless
tar -czvf ../release_output/ryujinx-headless-sdl2-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
popd
shell: bash
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "release_output/*.tar.gz,release_output/*.zip"
tag: ${{ steps.version_info.outputs.build_version }}
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
allowUpdates: true
removeArtifacts: true
replacesArtifacts: true
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.RELEASE_TOKEN }}

View file

@ -30,7 +30,7 @@ namespace Ryujinx.Common.Logging
files[i].Delete();
}
string version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
string version = ReleaseInformations.GetVersion();
// Get path for the current time
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log");

View file

@ -0,0 +1,32 @@
namespace Ryujinx.Common
{
// DO NOT EDIT, filled by CI
public static class ReleaseInformations
{
public static string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
public static string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
public static string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
public static string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
public static string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
public static bool IsValid()
{
return !BuildGitHash.StartsWith("%%") &&
!ReleaseChannelName.StartsWith("%%") &&
!ReleaseChannelOwner.StartsWith("%%") &&
!ReleaseChannelRepo.StartsWith("%%");
}
public static string GetVersion()
{
if (IsValid())
{
return BuildVersion;
}
else
{
return "1.0.0-dirty";
}
}
}
}

View file

@ -3,6 +3,7 @@ using ARMeilleure.Translation.PTC;
using CommandLine;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
@ -57,7 +58,7 @@ namespace Ryujinx.Headless.SDL2
static void Main(string[] args)
{
Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
Version = ReleaseInformations.GetVersion();
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";

View file

@ -3,6 +3,7 @@ using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json.Linq;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Ui;
using Ryujinx.Ui.Widgets;
@ -29,17 +30,26 @@ namespace Ryujinx.Modules
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
private static readonly int ConnectionCount = 4;
private static string _jobId;
private static string _buildVer;
private static string _platformExt;
private static string _buildUrl;
private static long _buildSize;
private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
private const string GitHubApiURL = "https://api.github.com";
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
private static HttpClient ConstructHttpClient()
{
HttpClient result = new HttpClient();
// Required by GitHub to interract with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
return result;
}
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
{
if (Running) return;
@ -88,22 +98,45 @@ namespace Ryujinx.Modules
return;
}
// Get latest version number from Appveyor
// Get latest version number from GitHub API
try
{
using (HttpClient jsonClient = new HttpClient())
using (HttpClient jsonClient = ConstructHttpClient())
{
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest";
// Fetch latest build information
string fetchedJson = await jsonClient.GetStringAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master");
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
JObject jsonRoot = JObject.Parse(fetchedJson);
JToken buildToken = jsonRoot["build"];
JToken assets = jsonRoot["assets"];
_jobId = (string)buildToken["jobs"][0]["jobId"];
_buildVer = (string)buildToken["version"];
_buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}";
_buildVer = (string)jsonRoot["name"];
// If build not done, assume no new update are availaible.
if ((string)buildToken["jobs"][0]["status"] != "success")
foreach (JToken asset in assets)
{
string assetName = (string)asset["name"];
string assetState = (string)asset["state"];
string downloadURL = (string)asset["browser_download_url"];
if (!assetName.StartsWith("ryujinx-headless-sdl2") && assetName.EndsWith(_platformExt))
{
_buildUrl = downloadURL;
if (assetState != "uploaded")
{
if (showVersionUpToDate)
{
GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
}
return;
}
break;
}
}
if (_buildUrl == null)
{
if (showVersionUpToDate)
{
@ -117,7 +150,7 @@ namespace Ryujinx.Modules
catch (Exception exception)
{
Logger.Error?.Print(LogClass.Application, exception.Message);
GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from AppVeyor. This can be caused if a new release is being compiled by AppVeyor. Try again in a few minutes.");
GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.");
return;
}
@ -128,8 +161,8 @@ namespace Ryujinx.Modules
}
catch
{
GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!");
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!");
GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from GitHub Release.", "Cancelling Update!");
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from GitHub Release!");
return;
}
@ -148,7 +181,7 @@ namespace Ryujinx.Modules
}
// Fetch build size information to learn chunk sizes.
using (HttpClient buildSizeClient = new HttpClient())
using (HttpClient buildSizeClient = ConstructHttpClient())
{
try
{
@ -520,7 +553,7 @@ namespace Ryujinx.Modules
return false;
}
if (Program.Version.Contains("dirty"))
if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid())
{
if (showWarnings)
{

View file

@ -1,5 +1,6 @@
using ARMeilleure.Translation.PTC;
using Gtk;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
@ -68,7 +69,7 @@ namespace Ryujinx
// Delete backup files after updating.
Task.Run(Updater.CleanupUpdate);
Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
Version = ReleaseInformations.GetVersion();
Console.Title = $"Ryujinx Console {Version}";