diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c71c6a69..c6847f8b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,21 +6,29 @@ on: default: "0.0.0" required: true type: string + channel: + description: 'Channel' + type: choice + required: true + default: 'beta' + options: + - 'stable' + - 'beta' + - 'nightly' release: type: boolean default: false description: "Release" - beta: - type: boolean - default: true - description: "Beta" draft: type: boolean default: true description: "Draft" +run-name: Build ${{ inputs.version }} + env: - release-name: ${{ inputs.version }}${{ inputs.beta && ' Beta' || '' }} + release-name: ${{ inputs.version }}${{ inputs.channel == 'beta' && ' Beta' || '' }} + release-tag: ${{ inputs.channel == 'nightly' && 'nightly' || inputs.version }} jobs: build: @@ -51,7 +59,7 @@ jobs: node-version: 20 - name: Install NSIS via Chocolatey run: choco install nsis --version=3.11 --no-progress - if: ${{ matrix.arch.os == 'win' }} + if: ${{ matrix.arch.os == 'win' && inputs.channel != 'nightly' }} - uses: justalemon/VersionPatcher@v0.7.1 with: version: ${{ inputs.version }} @@ -66,15 +74,15 @@ jobs: run: dotnet restore OpenUtau -r ${{ matrix.arch.rid }} - name: Build (Windows and Linux) - run: dotnet publish OpenUtau -c Release -r ${{ matrix.arch.rid }} --self-contained true -o bin/${{ matrix.arch.name }}/ + run: dotnet publish OpenUtau -c Release -r ${{ matrix.arch.rid }} --self-contained true -o bin/${{ matrix.arch.name }}/ -p:Nightly=${{ inputs.channel == 'nightly' }} if: ${{ matrix.arch.os != 'osx' }} - name: Build (MacOS) - run: dotnet msbuild OpenUtau -t:BundleApp -p:Configuration=Release -p:RuntimeIdentifier=${{ matrix.arch.rid }} -p:UseAppHost=true -p:SelfContained=true -p:OutputPath=../bin/${{ matrix.arch.name }}/ + run: dotnet msbuild OpenUtau -t:BundleApp -p:Configuration=Release -p:RuntimeIdentifier=${{ matrix.arch.rid }} -p:UseAppHost=true -p:SelfContained=true -p:OutputPath=../bin/${{ matrix.arch.name }}/ -p:Nightly=${{ inputs.channel == 'nightly' }} if: ${{ matrix.arch.os == 'osx' }} - name: Write release channel info - run: "@{Beta=$${{ inputs.beta }}} | ConvertTo-Json > bin/${{ matrix.arch.name }}/prefs-default.json" + run: "@{Beta=$${{ inputs.channel == 'beta' }}} | ConvertTo-Json > bin/${{ matrix.arch.name }}/prefs-default.json" shell: pwsh # Create Zip @@ -114,7 +122,7 @@ jobs: with: script-file: OpenUtau.nsi arguments: "-DPRODUCT_VERSION=${{ inputs.version }} -DARCH=${{ matrix.arch.arch }}" - if: ${{ matrix.arch.os == 'win' }} + if: ${{ matrix.arch.os == 'win' && inputs.channel != 'nightly' }} # Create Dmg - name: Create Dmg @@ -161,7 +169,7 @@ jobs: with: name: OpenUtau-${{ matrix.arch.name }}.exe path: OpenUtau-${{ matrix.arch.name }}.exe - if: ${{ !inputs.release && matrix.arch.os == 'win' }} + if: ${{ !inputs.release && matrix.arch.os == 'win' && inputs.channel != 'nightly' }} - uses: actions/upload-artifact@v4 with: @@ -177,33 +185,34 @@ jobs: # Appcast - name: Appcast Windows - shell: cmd + shell: bash run: | - python appcast.py -v=${{ inputs.version }} -o=windows -r=${{ matrix.arch.name }} -f=OpenUtau-${{ matrix.arch.name }}.zip - python appcast.py -v=${{ inputs.version }} -o=windows -r=${{ matrix.arch.name }}-installer -f=OpenUtau-${{ matrix.arch.name }}.exe + python appcast.py -v=${{ inputs.version }} -o=windows -r=${{ matrix.arch.name }} -t ${{ env.release-tag }} -f=OpenUtau-${{ matrix.arch.name }}.zip + if ${{ inputs.channel != 'nightly' }}; then + python appcast.py -v=${{ inputs.version }} -o=windows -r=${{ matrix.arch.name }}-installer -f=OpenUtau-${{ matrix.arch.name }}.exe + fi if: ${{ inputs.release && matrix.arch.os == 'win' }} - name: Appcast MacOS run: | - python appcast.py -v=${{ inputs.version }} -o=macos -r=${{ matrix.arch.name }} -f=OpenUtau-${{ matrix.arch.name }}.dmg + python appcast.py -v=${{ inputs.version }} -o=macos -r=${{ matrix.arch.name }} -t ${{ env.release-tag }} -f=OpenUtau-${{ matrix.arch.name }}.dmg if: ${{ inputs.release && matrix.arch.os == 'osx' }} - name: Appcast Linux run: | - python appcast.py -v=${{ inputs.version }} -o=linux -r=${{ matrix.arch.name }} -f=OpenUtau-${{ matrix.arch.name }}.tar.gz + python appcast.py -v=${{ inputs.version }} -o=linux -r=${{ matrix.arch.name }} -t ${{ env.release-tag }} -f=OpenUtau-${{ matrix.arch.name }}.tar.gz if: ${{ inputs.release && matrix.arch.os == 'linux' }} # Release - - name: Release - uses: softprops/action-gh-release@v2 + - name: Regular Release + uses: ncipollo/release-action@v1 with: - tag_name: ${{ inputs.version }} + tag: ${{ inputs.version }} name: ${{ env.release-name }} - prerelease: ${{ inputs.beta }} + prerelease: ${{ inputs.channel != 'stable' }} + allowUpdates: true draft: ${{ inputs.draft }} - files: | - appcast.${{ matrix.arch.name }}*.xml - OpenUtau-${{ matrix.arch.name }}.* + artifacts: "appcast.${{ matrix.arch.name }}*.xml,OpenUtau-${{ matrix.arch.name }}.*" body: | See [Getting Started](https://github.com/stakira/OpenUtau/wiki/Getting-Started) for how to use. @@ -211,4 +220,26 @@ jobs: ## macOS says "This app is damaged" Open a terminal and run `xattr -rc /Applications/OpenUtau.app`. Try opening OpenUtau again. - if: ${{ inputs.release }} + if: ${{ inputs.release && inputs.channel != 'nightly' }} + + - name: Nightly Release + uses: ncipollo/release-action@v1 + with: + tag: nightly + name: Nightly + prerelease: true + allowUpdates: true + draft: ${{ inputs.draft }} + artifacts: "appcast.${{ matrix.arch.name }}*.xml,OpenUtau-${{ matrix.arch.name }}.*" + body: | + > [!WARNING] + > Nightly builds are for testing purposes only and must not use for production. + > If you encounter bugs, check the [FAQ](https://github.com/stakira/OpenUtau/wiki/FAQ) first, then open an issue if needed. + + | Build Version | Commit | + | --------------------- | ----------------- | + | ${{ inputs.version }} | ${{ github.sha }} | + + ## macOS says "This app is damaged" + Open a terminal and run `xattr -rc /Applications/OpenUtau.app`. Try opening OpenUtau again. + if: ${{ inputs.release && inputs.channel == 'nightly' }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 000000000..fd11d503b --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,42 @@ +name: Nightly + +on: + schedule: + - cron: '0 1 * * *' + workflow_dispatch: + +jobs: + trigger: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check for changes + id: check_changes + run: | + # Only trigger nightly from these folders + REGEX="^(OpenUtau(.Plugin.Builtin|.Core|)|runtimes)" + echo "changes=$(git log --since="yesterday" --name-only --oneline --pretty=format:"" | grep -m1 -E "$REGEX")" >> $GITHUB_OUTPUT + - name: Get Version + id: get_version + if: ${{ steps.check_changes.outputs.changes != '' }} + env: + GH_TOKEN: ${{ github.token }} + REPO: openutau/OpenUtau # Please modify if you want to release your fork + run: | + latest=$(gh release list --repo $REPO --json tagName --json tagName --jq 'first(.[] | select(.tagName | test("^[0-9]+\\.[0-9]+\\.[0-9]+$"))).tagName') + nextver=$(npx -y semver -i patch ${latest}) + echo fullver=${nextver}-nightly.`date +%Y%m%d` >> $GITHUB_OUTPUT + - name: Trigger Build + if: ${{ steps.check_changes.outputs.changes != '' }} + uses: benc-uk/workflow-dispatch@v1 + with: + workflow: build.yml + inputs: '{ "channel": "nightly", "version": "${{ steps.get_version.outputs.fullver }}", "release": true, "draft": false }' + - name: Delete workflow runs + uses: Mattraks/delete-workflow-runs@v2 + with: + retain_days: 5 + keep_minimum_runs: 5 + delete_workflow_pattern: nightly diff --git a/OpenUtau/OpenUtau.csproj b/OpenUtau/OpenUtau.csproj index 85bf69c8b..b829664bb 100644 --- a/OpenUtau/OpenUtau.csproj +++ b/OpenUtau/OpenUtau.csproj @@ -15,6 +15,9 @@ true embedded + + false + OpenUtau @@ -126,4 +129,7 @@ Resources.Designer.cs + + $(DefineConstants);NIGHTLY + \ No newline at end of file diff --git a/OpenUtau/Program.cs b/OpenUtau/Program.cs index dab8b1029..98c6a0954 100644 --- a/OpenUtau/Program.cs +++ b/OpenUtau/Program.cs @@ -34,7 +34,9 @@ public static void Main(string[] args) { Log.Information($"{RuntimeInformation.OSDescription} " + $"{RuntimeInformation.OSArchitecture} " + $"{RuntimeInformation.ProcessArchitecture}"); - Log.Information($"OpenUtau v{Assembly.GetEntryAssembly()?.GetName().Version} " + + Log.Information($"OpenUtau v{Assembly.GetEntryAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion} " + $"{RuntimeInformation.RuntimeIdentifier}"); Log.Information($"Data path = {PathManager.Inst.DataPath}"); Log.Information($"Cache path = {PathManager.Inst.CachePath}"); diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml index bfc779b45..3ac5b0983 100644 --- a/OpenUtau/Strings/Strings.axaml +++ b/OpenUtau/Strings/Strings.axaml @@ -592,6 +592,7 @@ Warning: this option removes custom presets. Editing General Note: please restart OpenUtau after changing this item. + Note: Switching between channels is not supported in nightly builds. Oto Editor Default Oto Editor setParam Path diff --git a/OpenUtau/ViewModels/MainWindowViewModel.cs b/OpenUtau/ViewModels/MainWindowViewModel.cs index d3090673f..c930eaf12 100644 --- a/OpenUtau/ViewModels/MainWindowViewModel.cs +++ b/OpenUtau/ViewModels/MainWindowViewModel.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reactive; using System.Threading.Tasks; +using System.Reflection; using Avalonia.Threading; using DynamicData.Binding; using OpenUtau.App.Views; @@ -72,7 +73,9 @@ public class MainWindowViewModel : ViewModelBase, ICmdSubscriber { [Reactive] public string ClearCacheHeader { get; set; } public bool ProjectSaved => !string.IsNullOrEmpty(DocManager.Inst.Project.FilePath) && DocManager.Inst.Project.Saved; - public string AppVersion => $"OpenUtau v{System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version}"; + public string AppVersion => $"OpenUtau v{Assembly.GetEntryAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion}"; [Reactive] public double Progress { get; set; } [Reactive] public string ProgressText { get; set; } [Reactive] public bool ShowPianoRoll { get; set; } diff --git a/OpenUtau/ViewModels/PreferencesViewModel.cs b/OpenUtau/ViewModels/PreferencesViewModel.cs index 0683b822e..094505c39 100644 --- a/OpenUtau/ViewModels/PreferencesViewModel.cs +++ b/OpenUtau/ViewModels/PreferencesViewModel.cs @@ -128,6 +128,12 @@ public int SafeMaxThreadCount { [Reactive] public bool RememberVsqx { get; set; } public string WinePath => Preferences.Default.WinePath; + #if NIGHTLY + public bool IsNightly => true; + #else + public bool IsNightly => false; + #endif + public PreferencesViewModel() { var audioOutput = PlaybackManager.Inst.AudioOutput; if (audioOutput != null) { diff --git a/OpenUtau/ViewModels/UpdaterViewModel.cs b/OpenUtau/ViewModels/UpdaterViewModel.cs index ff24dedd2..3225dc0ac 100644 --- a/OpenUtau/ViewModels/UpdaterViewModel.cs +++ b/OpenUtau/ViewModels/UpdaterViewModel.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using System.Reflection; using Avalonia.Media; using NetSparkleUpdater; using NetSparkleUpdater.AppCastHandlers; @@ -29,10 +30,13 @@ class GithubRelease { public bool draft; public bool prerelease; public string name = string.Empty; + public string tag_name = string.Empty; public GithubReleaseAsset[] assets = new GithubReleaseAsset[0]; #pragma warning restore 0649 } - public string AppVersion => $"v{System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version}"; + public string AppVersion => $"v{Assembly.GetEntryAssembly() + ?.GetCustomAttribute() + ?.InformationalVersion}"; public bool IsDarkMode => ThemeManager.IsDarkMode; [Reactive] public string UpdaterStatus { get; set; } [Reactive] public bool UpdateAvailable { get; set; } @@ -84,17 +88,28 @@ public UpdaterViewModel() { client.DefaultRequestHeaders.Add("Accept", "application/json"); client.DefaultRequestHeaders.Add("User-Agent", "Other"); client.Timeout = TimeSpan.FromSeconds(30); - using var resposne = await client.GetAsync("https://api.github.com/repos/stakira/OpenUtau/releases"); + + #if !NIGHTLY + using var resposne = await client.GetAsync("https://api.github.com/repos/openutau/OpenUtau/releases"); + #else + using var resposne = await client.GetAsync("https://api.github.com/repos/openutau/OpenUtau/releases/tags/nightly"); + #endif + resposne.EnsureSuccessStatusCode(); string respBody = await resposne.Content.ReadAsStringAsync(); - List? releases = JsonConvert.DeserializeObject>(respBody); - if (releases == null) { - return null; - } - return releases - .Where(r => !r.draft && r.prerelease == Preferences.Default.Beta) - .OrderByDescending(r => r.id) - .FirstOrDefault(); + + #if !NIGHTLY + List? releases = JsonConvert.DeserializeObject>(respBody); + if (releases == null) { + return null; + } + return releases + .Where(r => !r.draft && r.tag_name != "nightly" && r.prerelease == Preferences.Default.Beta) + .OrderByDescending(r => r.id) + .FirstOrDefault(); + #else + return JsonConvert.DeserializeObject(respBody); + #endif } static GithubReleaseAsset? SelectAppcast(GithubRelease release) { diff --git a/OpenUtau/Views/PreferencesDialog.axaml b/OpenUtau/Views/PreferencesDialog.axaml index 290323724..1b3b08e08 100644 --- a/OpenUtau/Views/PreferencesDialog.axaml +++ b/OpenUtau/Views/PreferencesDialog.axaml @@ -82,11 +82,12 @@ - + - + + diff --git a/appcast.py b/appcast.py index 22f1d6fbd..1a19f8491 100644 --- a/appcast.py +++ b/appcast.py @@ -7,12 +7,14 @@ def main(): parser.add_argument('-o', '--os', help='OS name', required=True) parser.add_argument('-r', '--rid', help='RID', required=True) parser.add_argument('-f', '--file', help='File name', required=True) + parser.add_argument('-t', '--tag', help='Tag (Version if unspecified)') args = parser.parse_args() appcast_ver = args.version appcast_os = args.os appcast_rid = args.rid appcast_file = args.file + appcast_tag = args.tag or args.version xml = ''' @@ -31,7 +33,7 @@ def main(): ''' % (appcast_ver, datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z"), - appcast_ver, appcast_file, appcast_ver, appcast_ver, appcast_os) + appcast_tag, appcast_file, appcast_ver, appcast_ver, appcast_os) with open("appcast.%s.xml" % (appcast_rid), 'w') as f: f.write(xml)