Command line publishing #1015

Closed
opened 2026-01-29 16:55:00 +00:00 by claunia · 4 comments
Owner

Originally created by @markatosi on GitHub (Nov 19, 2025).

I have read the new Package build wiki and it looks as though publishing should be fine as long as you are within the VS Studio world. I don't see any guidance regarding publishing an app bundle from the command line. Is that just gone or am I missing something?

I have a software for which use need to build a universal binary on a Mac (Arm + intel glued together)
I have written a js script to do this but it is dependent on being able to build the .app files from the command line in a certain order perform a lot of other maintenance on the binaries to make sure the two apps can be successfully combined.

I will provide a relevant excerpt of the script.

Step 2 is the important bit but it's critical that each step flow one after the other.
(I'm using electron sharp right now as it was the only reasonable way to build an apple mac store app as the old version of electron net didn't easily/semi officially support newer versions of electron and electron builder and it was impossible to create an app bundle that could be successfully approved and or notarized for distribution)

// Step 1: Build MyApp.Core first

console.log('=== STEP 1: Building MyApp.Core ===');
await runCommand('dotnet', ['build', '../myapp-core/Myapp.Core.csproj', '-c', 'Release'], buildCwd);

// Step 2: Build the Electron applications (this creates the .app bundles)

console.log('=== STEP 2: Building Electron applications ===');
await runCommand('electron-sharp', ['build', '/target', 'osx', '/p:ProjectPath= Myapp.csproj'], buildCwd);
await runCommand('electron-sharp', ['build', '/target', 'osx-arm64', '/p:ProjectPath=Myapp.csproj'], buildCwd);

// Step 3: Copy static web assets BEFORE replacing bin directories

console.log('=== STEP 3: Copying static web assets ===');
await copyJsonFile();

// Step 4: Clean and create single-file versions in temporary locations

console.log('=== STEP 4: Creating single-file versions ===');
const tempX64Path = path.join(buildCwd, 'temp-x64');
const tempArm64Path = path.join(buildCwd, 'temp-arm64');

console.log('tempX64Path:', tempX64Path);
console.log('tempArm64Path:', tempArm64Path);

// Clean temp directories first
await cleanTempDirectories(tempX64Path, tempArm64Path);

await runCommand('dotnet', ['publish', 'Myapp.csproj', '-r', 'osx-x64', '-c', 'Release', '--self-contained', '/p:PublishSingleFile=true', '/p:DebugType=None', '-o', tempX64Path], buildCwd);
await runCommand('dotnet', ['publish', 'Myapp.csproj', '-r', 'osx-arm64', '-c', 'Release', '--self-contained', '/p:PublishSingleFile=true', '/p:DebugType=None', '-o', tempArm64Path], buildCwd);

// Step 5: Replace the bin directories with single-file executables

console.log('=== STEP 5: Replacing bin directories with single-file executables ===');

await replaceBinDirectoryWithSingleFile(x64AppPath, tempX64Path, 'x64');
await replaceBinDirectoryWithSingleFile(arm64AppPath, tempArm64Path, 'arm64');

// Step 6: Clean up other unnecessary folders

console.log('=== STEP 6: Cleaning up app bundles ===');
await deleteFolder(x64AppPath, 'dist');
await deleteFolder(arm64AppPath, 'dist');

// Step 7: Final cleanup of unwanted files from both apps

console.log('=== STEP 7: Final cleanup of unwanted files ===');
await cleanUnwantedFilesFromApp(x64AppPath, 'x64');
await cleanUnwantedFilesFromApp(arm64AppPath, 'arm64');

// Step 8: Clean up temporary directories

console.log('=== STEP 8: Cleaning up temporary directories ===');
await fs.rm(tempX64Path, {recursive: true, force: true});
await fs.rm(tempArm64Path, {recursive: true, force: true});

// Step 9: Create universal app

console.log('=== STEP 9: Creating universal app ===');
console.log('makeUniversalApp options:');
console.log('  x64AppPath:', x64AppPath);
console.log('  arm64AppPath:', arm64AppPath);
console.log('  outAppPath:', outAppPath);

await copyJsonFile();
console.log('Copying JSON file for endpoints...');
console.log('Making the universal bundle');
await makeUniversalApp({
    x64AppPath,
    arm64AppPath,
    outAppPath,
    force: true,
    x64ArchFiles: [],
    mergeASARs: true,
});
console.log('Universal app created at:', outAppPath);

// Step 10: Sign universal app

console.log('=== STEP 10: Signing universal app ===');
await signUniversalApp();

console.log('=== BUILD COMPLETE ===');```
Originally created by @markatosi on GitHub (Nov 19, 2025). I have read the new Package build wiki and it looks as though publishing should be fine as long as you are within the VS Studio world. I don't see any guidance regarding publishing an app bundle from the command line. Is that just gone or am I missing something? I have a software for which use need to build a universal binary on a Mac (Arm + intel glued together) I have written a js script to do this but it is dependent on being able to build the .app files from the command line in a certain order perform a lot of other maintenance on the binaries to make sure the two apps can be successfully combined. I will provide a relevant excerpt of the script. Step 2 is the important bit but it's critical that each step flow one after the other. (I'm using electron sharp right now as it was the only reasonable way to build an apple mac store app as the old version of electron net didn't easily/semi officially support newer versions of electron and electron builder and it was impossible to create an app bundle that could be successfully approved and or notarized for distribution) ``` // Step 1: Build MyApp.Core first console.log('=== STEP 1: Building MyApp.Core ==='); await runCommand('dotnet', ['build', '../myapp-core/Myapp.Core.csproj', '-c', 'Release'], buildCwd); // Step 2: Build the Electron applications (this creates the .app bundles) console.log('=== STEP 2: Building Electron applications ==='); await runCommand('electron-sharp', ['build', '/target', 'osx', '/p:ProjectPath= Myapp.csproj'], buildCwd); await runCommand('electron-sharp', ['build', '/target', 'osx-arm64', '/p:ProjectPath=Myapp.csproj'], buildCwd); // Step 3: Copy static web assets BEFORE replacing bin directories console.log('=== STEP 3: Copying static web assets ==='); await copyJsonFile(); // Step 4: Clean and create single-file versions in temporary locations console.log('=== STEP 4: Creating single-file versions ==='); const tempX64Path = path.join(buildCwd, 'temp-x64'); const tempArm64Path = path.join(buildCwd, 'temp-arm64'); console.log('tempX64Path:', tempX64Path); console.log('tempArm64Path:', tempArm64Path); // Clean temp directories first await cleanTempDirectories(tempX64Path, tempArm64Path); await runCommand('dotnet', ['publish', 'Myapp.csproj', '-r', 'osx-x64', '-c', 'Release', '--self-contained', '/p:PublishSingleFile=true', '/p:DebugType=None', '-o', tempX64Path], buildCwd); await runCommand('dotnet', ['publish', 'Myapp.csproj', '-r', 'osx-arm64', '-c', 'Release', '--self-contained', '/p:PublishSingleFile=true', '/p:DebugType=None', '-o', tempArm64Path], buildCwd); // Step 5: Replace the bin directories with single-file executables console.log('=== STEP 5: Replacing bin directories with single-file executables ==='); await replaceBinDirectoryWithSingleFile(x64AppPath, tempX64Path, 'x64'); await replaceBinDirectoryWithSingleFile(arm64AppPath, tempArm64Path, 'arm64'); // Step 6: Clean up other unnecessary folders console.log('=== STEP 6: Cleaning up app bundles ==='); await deleteFolder(x64AppPath, 'dist'); await deleteFolder(arm64AppPath, 'dist'); // Step 7: Final cleanup of unwanted files from both apps console.log('=== STEP 7: Final cleanup of unwanted files ==='); await cleanUnwantedFilesFromApp(x64AppPath, 'x64'); await cleanUnwantedFilesFromApp(arm64AppPath, 'arm64'); // Step 8: Clean up temporary directories console.log('=== STEP 8: Cleaning up temporary directories ==='); await fs.rm(tempX64Path, {recursive: true, force: true}); await fs.rm(tempArm64Path, {recursive: true, force: true}); // Step 9: Create universal app console.log('=== STEP 9: Creating universal app ==='); console.log('makeUniversalApp options:'); console.log(' x64AppPath:', x64AppPath); console.log(' arm64AppPath:', arm64AppPath); console.log(' outAppPath:', outAppPath); await copyJsonFile(); console.log('Copying JSON file for endpoints...'); console.log('Making the universal bundle'); await makeUniversalApp({ x64AppPath, arm64AppPath, outAppPath, force: true, x64ArchFiles: [], mergeASARs: true, }); console.log('Universal app created at:', outAppPath); // Step 10: Sign universal app console.log('=== STEP 10: Signing universal app ==='); await signUniversalApp(); console.log('=== BUILD COMPLETE ===');```
claunia added the question label 2026-01-29 16:55:00 +00:00
Author
Owner

@softworkz commented on GitHub (Nov 19, 2025):

Command line publishing is working. My own project is using the new Electron.NET Core and our Linux packages are built on Azure DevOps. This is directly from the log (just stripped loggers):

dotnet publish xxx.csproj -c Release -p:Platform=x64 -r linux-x64 -p:PublishProfile=publish-linux-x64 -p:Version=2.268.0

I haven't tried without publish profile yet, but you can take one from the repo here. Or just specify the variables on the command line directly - there's no forcing reading to use publishing profile files.

Summary

Yes, it'sabsolutely possible. You just don't use any special CLI tool anymore but simply dotnet publish .

@softworkz commented on GitHub (Nov 19, 2025): Command line publishing is working. My own project is using the new Electron.NET Core and our Linux packages are built on Azure DevOps. This is directly from the log (just stripped loggers): `dotnet publish xxx.csproj -c Release -p:Platform=x64 -r linux-x64 -p:PublishProfile=publish-linux-x64 -p:Version=2.268.0` I haven't tried without publish profile yet, but you can take one from the repo here. Or just specify the variables on the command line directly - there's no forcing reading to use publishing profile files. ### Summary Yes, it'sabsolutely possible. You just don't use any special CLI tool anymore but simply **dotnet publish** .
Author
Owner

@softworkz commented on GitHub (Nov 19, 2025):

Furthermore, you'll probably able to collapse steps 1, 2, 3, 4 and 5 into a single operation, running dotnet publish with publish settings for self-contained and single-file should give you that result already.

Please note that electron-builder settings are no longer in a package.json but in an "original" electron-builder.json file (./Properties folder).

We have the integration tests running on Mac as well (e.g. https://github.com/ElectronNET/Electron.NET/actions/runs/19486228327), so dotnet build and dotnet test are definitley working on mac.

@softworkz commented on GitHub (Nov 19, 2025): Furthermore, you'll probably able to collapse steps 1, 2, 3, 4 and 5 into a single operation, running `dotnet publish` with publish settings for self-contained and single-file should give you that result already. Please note that electron-builder settings are no longer in a package.json but in an "original" electron-builder.json file (`./Properties` folder). We have the integration tests running on Mac as well (e.g. https://github.com/ElectronNET/Electron.NET/actions/runs/19486228327), so `dotnet build` and `dotnet test` are definitley working on mac.
Author
Owner

@FlorianRappl commented on GitHub (Nov 19, 2025):

Wanted to write the same as @softworkz . It's just a single command, and it's the "standard" command (dotnet publish). No extras required.

Hope that helps!

@FlorianRappl commented on GitHub (Nov 19, 2025): Wanted to write the same as @softworkz . It's just a single command, and it's the "standard" command (`dotnet publish`). No extras required. Hope that helps!
Author
Owner

@markatosi commented on GitHub (Nov 21, 2025):

Furthermore, you'll probably able to collapse steps 1, 2, 3, 4 and 5 into a single operation, running dotnet publish with publish settings for self-contained and single-file should give you that result already.

I was able to whittle my universal binary script from 10 to 3 steps and remove tons of now unnecessary code. I may contribute this script to the project... if you think it would be helpful. Even with the massive improvements with Electron Net build process making a working universal binary with .net and electron is still not remotely obvious.

@markatosi commented on GitHub (Nov 21, 2025): > Furthermore, you'll probably able to collapse steps 1, 2, 3, 4 and 5 into a single operation, running `dotnet publish` with publish settings for self-contained and single-file should give you that result already. I was able to whittle my universal binary script from 10 to 3 steps and remove tons of now unnecessary code. I may contribute this script to the project... if you think it would be helpful. Even with the massive improvements with Electron Net build process making a working universal binary with .net and electron is still not remotely obvious.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/Electron.NET#1015