├── src ├── Assets │ └── Icons │ │ └── icon.ico ├── Flaco.fsproj ├── Program.fs └── Main.fs ├── .config └── dotnet-tools.json ├── flaco.sln ├── README.md └── .gitignore /src/Assets/Icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AngelMunoz/package-fsharp-to-flatpak/HEAD/src/Assets/Icons/icon.ico -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fantomas": { 6 | "version": "6.1.1", 7 | "commands": [ 8 | "fantomas" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Flaco.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /flaco.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Flaco", "src\Flaco.fsproj", "{CFDC2BA9-F171-4E9F-8BE0-83EEF678415A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(SolutionProperties) = preSolution 14 | HideSolutionNode = FALSE 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {CFDC2BA9-F171-4E9F-8BE0-83EEF678415A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {CFDC2BA9-F171-4E9F-8BE0-83EEF678415A}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {CFDC2BA9-F171-4E9F-8BE0-83EEF678415A}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {CFDC2BA9-F171-4E9F-8BE0-83EEF678415A}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /src/Program.fs: -------------------------------------------------------------------------------- 1 | namespace Examples.CounterApp 2 | 3 | open System 4 | 5 | open Avalonia 6 | open Avalonia.Controls 7 | open Avalonia.Controls.ApplicationLifetimes 8 | open Avalonia.Themes.Fluent 9 | open Avalonia.FuncUI.Hosts 10 | 11 | type MainWindow() = 12 | inherit HostWindow() 13 | 14 | do 15 | base.Title <- "Counter Example" 16 | base.Height <- 400.0 17 | base.Width <- 400.0 18 | base.Content <- Main.view 19 | 20 | type App() = 21 | inherit Application() 22 | 23 | override this.Initialize() = 24 | this.Styles.Add(FluentTheme()) 25 | this.RequestedThemeVariant <- Styling.ThemeVariant.Dark 26 | 27 | override this.OnFrameworkInitializationCompleted() = 28 | match this.ApplicationLifetime with 29 | | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime -> 30 | let mainWindow = MainWindow() 31 | desktopLifetime.MainWindow <- mainWindow 32 | | _ -> () 33 | 34 | module Program = 35 | 36 | [] 37 | let main (args: string[]) = 38 | AppBuilder 39 | .Configure() 40 | .UsePlatformDetect() 41 | .UseSkia() 42 | .StartWithClassicDesktopLifetime(args) 43 | -------------------------------------------------------------------------------- /src/Main.fs: -------------------------------------------------------------------------------- 1 | namespace Examples.CounterApp 2 | 3 | module Main = 4 | open Avalonia.Controls 5 | open Avalonia.FuncUI 6 | open Avalonia.FuncUI.DSL 7 | open Avalonia.Layout 8 | 9 | let view = 10 | Component (fun ctx -> 11 | let state = ctx.useState 0 12 | 13 | DockPanel.create [ 14 | DockPanel.children [ 15 | Button.create [ 16 | Button.dock Dock.Bottom 17 | Button.onClick (fun _ -> state.Current - 1 |> state.Set) 18 | Button.content "-" 19 | Button.horizontalAlignment HorizontalAlignment.Stretch 20 | ] 21 | Button.create [ 22 | Button.dock Dock.Bottom 23 | Button.onClick (fun _ -> state.Current + 1 |> state.Set) 24 | Button.content "+" 25 | Button.horizontalAlignment HorizontalAlignment.Stretch 26 | ] 27 | TextBlock.create [ 28 | TextBlock.dock Dock.Top 29 | TextBlock.fontSize 48.0 30 | TextBlock.verticalAlignment VerticalAlignment.Center 31 | TextBlock.horizontalAlignment HorizontalAlignment.Center 32 | TextBlock.text (string state.Current) 33 | ] 34 | ] 35 | ] 36 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Package an F# Avalonia app as a Flatpak 2 | 3 | > ### Flatpaks are not your thing? 4 | > 5 | > Try the packaging F# apps as a [snap](https://github.com/AngelMunoz/package-fsharp-to-snap/tree/main) then! 6 | 7 | > **_Note_**: While this example is written for F#, it should work for any .NET app. 8 | 9 | You're done with your app. Providing binaries is the next step, but you want to make it easy for your users to install and run your app. You also want to make it easy for you to distribute your app and even maybe auto-updates. You've heard about Flatpaks and want to try it out. 10 | 11 | This project is based on this _https://docs.flatpak.org/en/latest/first-build.html_ tutorial which has better information in regards of the flatpak build infrastructure. 12 | 13 | > **_Note_**:For this you should have set already the `org.flatpak.Builder`, `org.freedesktop.Platform`, and `org.freedesktop.Sdk` flatpaks. If you haven't, you can do so by following the instructions here: _https://docs.flatpak.org/en/latest/first-build.html#installing-the-flatpak-sdk_. 14 | 15 | --- 16 | 17 | Before we get into the juicy parts of the flatpak stuff. Let's build the app first. 18 | 19 | ``` 20 | dotnet publish ./src -c Release -o ./flatpak/Flaco --self-contained -r linux-x64. 21 | ``` 22 | 23 | in this case we'll use a self-contained app so we don't have to depend on the flatpak dotnet sdk or runtime. 24 | 25 | Ideally we just want to call a simple command and get running, nothing else. 26 | 27 | ## The Flatpak parts 28 | 29 | We will need a `flatpak` directory mainly for convenience and also to be sure we have what we need to build the flatpak. 30 | 31 | The manifest is relatively simple 32 | 33 | ```yaml 34 | # your app's id which ideally should match the manifest file name 35 | app-id: org.flatpak.Flaco 36 | runtime: org.freedesktop.Platform 37 | runtime-version: "22.08" # version of the runtime 38 | sdk: org.freedesktop.Sdk # the sdk to use 39 | command: Flaco # the command to run, basically the command that starts the app 40 | finish-args: 41 | # Flatpaks run in sandbox mode. This is the list of permissions we need 42 | # As we're running an Avalonia App, we need to specify that we want access 43 | # to the window infrastructure of the host 44 | - --socket=fallback-x11 45 | # for more information visit https://docs.flatpak.org/en/latest/sandbox-permissions.html#sandbox-permissions 46 | 47 | modules: 48 | # This is our app and the ;build' instructions for it 49 | - name: Flaco 50 | buildsystem: simple 51 | # In our case we've already run `dotnet publish` so we just need to copy the 52 | # outputs to the right place 53 | build-commands: 54 | - mkdir -p /app/bin 55 | - mv ./app-sources /app/bin/app-sources 56 | # allow our app to be executed 57 | - chmod +x /app/bin/app-sources/Flaco 58 | # create a symlink to the executable 59 | - ln -s /app/bin/app-sources/Flaco /app/bin/Flaco 60 | # This is the list of files/directories we want to copy to the flatpak 61 | # for more elaborated builds we could fetch a zip file, git repository or 62 | # even build from source however that comes with different challenges 63 | # so we'll leave that for another time 64 | sources: 65 | - type: dir 66 | # This is the path on the host machine './flatpak/Flaco' 67 | # but since we're running the flatpak-builder command in the flatpak directory 68 | # we'll omit the other one 69 | path: Flaco 70 | # This is the path inside the flatpak which gets copied to the root of the flatpak 71 | dest: app-sources 72 | ``` 73 | 74 | Once we've built our dotnet app and have the manifest ready, we can build the flatpak. 75 | 76 | - `flatpak-builder --user --install --force-clean build-dir org.flatpak.Flaco.yml` 77 | - `--user`: we want to install the flatpak for the current user 78 | - `--install`: we want to install the flatpak after building it 79 | - `--force-clean`: we want to clean the build directory before building the flatpak 80 | - `flatpak run org.flatpak.Flaco` 81 | 82 | And that's it. The app should show on the screen and now we have a flatpak for our app tested and ready to be published in flathub! 83 | 84 | ### Extra Notes 85 | 86 | Thanks to [@scitesy](https://social.librem.one/@scitesy/110662886460321742) for providing me great resources to explore this area as well as the flatpak documentation. 87 | Also As noted by [@nlogozzo](https://mastodon.social/@nlogozzo/110662954309000176) (so much thanks, this helped me quite a lot!) when you run `flatpak-builder` you're not allowed to access the network anymore, so if you want to build from source your dotnet code you need to make a couple of extra workaround mainly involving the `sources` section of the manifest. So the dotnet sdk and NuGet have something to work with. 88 | 89 | As you may guess, the way we're building this right now makes apps quite heavy as we're not doing any trimming, or using any of the flatpak features to make the app lighter. But this is a good starting point. 90 | 91 | The following point could be to build the app as a runtime dependant app which can in turn use the dotnet runtime provided by the flatpak sdk. This would make the app lighter and also would allow us to use the flatpak to be more streamlined into it. 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.tlog 97 | *.vspscc 98 | *.vssscc 99 | .builds 100 | *.pidb 101 | *.svclog 102 | *.scc 103 | 104 | # Chutzpah Test files 105 | _Chutzpah* 106 | 107 | # Visual C++ cache files 108 | ipch/ 109 | *.aps 110 | *.ncb 111 | *.opendb 112 | *.opensdf 113 | *.sdf 114 | *.cachefile 115 | *.VC.db 116 | *.VC.VC.opendb 117 | 118 | # Visual Studio profiler 119 | *.psess 120 | *.vsp 121 | *.vspx 122 | *.sap 123 | 124 | # Visual Studio Trace Files 125 | *.e2e 126 | 127 | # TFS 2012 Local Workspace 128 | $tf/ 129 | 130 | # Guidance Automation Toolkit 131 | *.gpState 132 | 133 | # ReSharper is a .NET coding add-in 134 | _ReSharper*/ 135 | *.[Rr]e[Ss]harper 136 | *.DotSettings.user 137 | 138 | # TeamCity is a build add-in 139 | _TeamCity* 140 | 141 | # DotCover is a Code Coverage Tool 142 | *.dotCover 143 | 144 | # AxoCover is a Code Coverage Tool 145 | .axoCover/* 146 | !.axoCover/settings.json 147 | 148 | # Coverlet is a free, cross platform Code Coverage Tool 149 | coverage*.json 150 | coverage*.xml 151 | coverage*.info 152 | 153 | # Visual Studio code coverage results 154 | *.coverage 155 | *.coveragexml 156 | 157 | # NCrunch 158 | _NCrunch_* 159 | .*crunch*.local.xml 160 | nCrunchTemp_* 161 | 162 | # MightyMoose 163 | *.mm.* 164 | AutoTest.Net/ 165 | 166 | # Web workbench (sass) 167 | .sass-cache/ 168 | 169 | # Installshield output folder 170 | [Ee]xpress/ 171 | 172 | # DocProject is a documentation generator add-in 173 | DocProject/buildhelp/ 174 | DocProject/Help/*.HxT 175 | DocProject/Help/*.HxC 176 | DocProject/Help/*.hhc 177 | DocProject/Help/*.hhk 178 | DocProject/Help/*.hhp 179 | DocProject/Help/Html2 180 | DocProject/Help/html 181 | 182 | # Click-Once directory 183 | publish/ 184 | 185 | # Publish Web Output 186 | *.[Pp]ublish.xml 187 | *.azurePubxml 188 | # Note: Comment the next line if you want to checkin your web deploy settings, 189 | # but database connection strings (with potential passwords) will be unencrypted 190 | *.pubxml 191 | *.publishproj 192 | 193 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 194 | # checkin your Azure Web App publish settings, but sensitive information contained 195 | # in these scripts will be unencrypted 196 | PublishScripts/ 197 | 198 | # NuGet Packages 199 | *.nupkg 200 | # NuGet Symbol Packages 201 | *.snupkg 202 | # The packages folder can be ignored because of Package Restore 203 | **/[Pp]ackages/* 204 | # except build/, which is used as an MSBuild target. 205 | !**/[Pp]ackages/build/ 206 | # Uncomment if necessary however generally it will be regenerated when needed 207 | #!**/[Pp]ackages/repositories.config 208 | # NuGet v3's project.json files produces more ignorable files 209 | *.nuget.props 210 | *.nuget.targets 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 301 | *.vbp 302 | 303 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 304 | *.dsw 305 | *.dsp 306 | 307 | # Visual Studio 6 technical files 308 | *.ncb 309 | *.aps 310 | 311 | # Visual Studio LightSwitch build output 312 | **/*.HTMLClient/GeneratedArtifacts 313 | **/*.DesktopClient/GeneratedArtifacts 314 | **/*.DesktopClient/ModelManifest.xml 315 | **/*.Server/GeneratedArtifacts 316 | **/*.Server/ModelManifest.xml 317 | _Pvt_Extensions 318 | 319 | # Paket dependency manager 320 | .paket/paket.exe 321 | paket-files/ 322 | 323 | # FAKE - F# Make 324 | .fake/ 325 | 326 | # CodeRush personal settings 327 | .cr/personal 328 | 329 | # Python Tools for Visual Studio (PTVS) 330 | __pycache__/ 331 | *.pyc 332 | 333 | # Cake - Uncomment if you are using it 334 | # tools/** 335 | # !tools/packages.config 336 | 337 | # Tabs Studio 338 | *.tss 339 | 340 | # Telerik's JustMock configuration file 341 | *.jmconfig 342 | 343 | # BizTalk build output 344 | *.btp.cs 345 | *.btm.cs 346 | *.odx.cs 347 | *.xsd.cs 348 | 349 | # OpenCover UI analysis results 350 | OpenCover/ 351 | 352 | # Azure Stream Analytics local run output 353 | ASALocalRun/ 354 | 355 | # MSBuild Binary and Structured Log 356 | *.binlog 357 | 358 | # NVidia Nsight GPU debugger configuration file 359 | *.nvuser 360 | 361 | # MFractors (Xamarin productivity tool) working folder 362 | .mfractor/ 363 | 364 | # Local History for Visual Studio 365 | .localhistory/ 366 | 367 | # Visual Studio History (VSHistory) files 368 | .vshistory/ 369 | 370 | # BeatPulse healthcheck temp database 371 | healthchecksdb 372 | 373 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 374 | MigrationBackup/ 375 | 376 | # Ionide (cross platform F# VS Code tools) working folder 377 | .ionide/ 378 | 379 | # Fody - auto-generated XML schema 380 | FodyWeavers.xsd 381 | 382 | # VS Code files for those working on multiple tools 383 | .vscode/* 384 | !.vscode/settings.json 385 | !.vscode/tasks.json 386 | !.vscode/launch.json 387 | !.vscode/extensions.json 388 | *.code-workspace 389 | 390 | # Local History for Visual Studio Code 391 | .history/ 392 | 393 | # Windows Installer files from build outputs 394 | *.cab 395 | *.msi 396 | *.msix 397 | *.msm 398 | *.msp 399 | 400 | # JetBrains Rider 401 | *.sln.iml 402 | 403 | ## 404 | ## Visual studio for Mac 405 | ## 406 | 407 | 408 | # globs 409 | Makefile.in 410 | *.userprefs 411 | *.usertasks 412 | config.make 413 | config.status 414 | aclocal.m4 415 | install-sh 416 | autom4te.cache/ 417 | *.tar.gz 418 | tarballs/ 419 | test-results/ 420 | 421 | # Mac bundle stuff 422 | *.dmg 423 | *.app 424 | 425 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 426 | # General 427 | .DS_Store 428 | .AppleDouble 429 | .LSOverride 430 | 431 | # Icon must end with two \r 432 | Icon 433 | 434 | 435 | # Thumbnails 436 | ._* 437 | 438 | # Files that might appear in the root of a volume 439 | .DocumentRevisions-V100 440 | .fseventsd 441 | .Spotlight-V100 442 | .TemporaryItems 443 | .Trashes 444 | .VolumeIcon.icns 445 | .com.apple.timemachine.donotpresent 446 | 447 | # Directories potentially created on remote AFP share 448 | .AppleDB 449 | .AppleDesktop 450 | Network Trash Folder 451 | Temporary Items 452 | .apdisk 453 | 454 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 455 | # Windows thumbnail cache files 456 | Thumbs.db 457 | ehthumbs.db 458 | ehthumbs_vista.db 459 | 460 | # Dump file 461 | *.stackdump 462 | 463 | # Folder config file 464 | [Dd]esktop.ini 465 | 466 | # Recycle Bin used on file shares 467 | $RECYCLE.BIN/ 468 | 469 | # Windows Installer files 470 | *.cab 471 | *.msi 472 | *.msix 473 | *.msm 474 | *.msp 475 | 476 | # Windows shortcuts 477 | *.lnk 478 | 479 | flatpak/ 480 | !flatpak/org.flatpak.Flaco.yml --------------------------------------------------------------------------------