@atom-js-org/cli 0.4.3-alpha.0 → 0.4.5-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/build.cjs +164 -60
- package/src/init.cjs +3 -3
package/package.json
CHANGED
package/src/build.cjs
CHANGED
|
@@ -107,7 +107,7 @@ async function localBuild(project, target, options = {}) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
const manifest = {
|
|
110
|
-
atomjsVersion: '0.4.
|
|
110
|
+
atomjsVersion: '0.4.5-alpha.0',
|
|
111
111
|
target,
|
|
112
112
|
productName,
|
|
113
113
|
appId: project.config.appId,
|
|
@@ -570,50 +570,101 @@ function hasMacCodeSigningTool() {
|
|
|
570
570
|
return process.platform === 'darwin' && fs.existsSync('/usr/bin/codesign');
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
+
async function removeMacMetadataFiles(root) {
|
|
574
|
+
const removed = [];
|
|
575
|
+
if (!root || !fs.existsSync(root)) return removed;
|
|
576
|
+
|
|
577
|
+
async function visit(directory) {
|
|
578
|
+
const entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
579
|
+
for (const entry of entries) {
|
|
580
|
+
const absolute = path.join(directory, entry.name);
|
|
581
|
+
const isMetadata = entry.name === '.DS_Store'
|
|
582
|
+
|| entry.name === '__MACOSX'
|
|
583
|
+
|| entry.name.startsWith('._');
|
|
584
|
+
|
|
585
|
+
if (isMetadata) {
|
|
586
|
+
await fs.promises.rm(absolute, { recursive: true, force: true });
|
|
587
|
+
removed.push(absolute);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (entry.isDirectory()) await visit(absolute);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
await visit(root);
|
|
596
|
+
return removed;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
async function sanitizeMacBundle(appBundle) {
|
|
600
|
+
await removeMacMetadataFiles(appBundle);
|
|
601
|
+
|
|
602
|
+
if (process.platform === 'darwin' && fs.existsSync('/usr/bin/xattr')) {
|
|
603
|
+
const result = spawnSync('/usr/bin/xattr', ['-cr', appBundle], {
|
|
604
|
+
encoding: 'utf8',
|
|
605
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
606
|
+
});
|
|
607
|
+
if (result.error) {
|
|
608
|
+
console.warn(`AtomJS could not clear macOS extended attributes: ${result.error.message}`);
|
|
609
|
+
} else if (result.status !== 0) {
|
|
610
|
+
const detail = String(result.stderr || result.stdout || '').trim();
|
|
611
|
+
if (detail) console.warn(`AtomJS could not clear every macOS extended attribute: ${detail}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Filesystems such as exFAT can materialize extended attributes as AppleDouble
|
|
616
|
+
// sidecars while xattr is running, so remove metadata once more afterwards.
|
|
617
|
+
await removeMacMetadataFiles(appBundle);
|
|
618
|
+
}
|
|
619
|
+
|
|
573
620
|
async function packageMacOS({ project, buildRoot, unpacked, executableName, productName, hostSource }) {
|
|
574
621
|
const outputs = [];
|
|
575
|
-
const
|
|
622
|
+
const finalAppBundle = path.join(buildRoot, `${productName}.app`);
|
|
623
|
+
const stageBase = await resolveShortStageBase();
|
|
624
|
+
const bundleStageRoot = await fs.promises.mkdtemp(path.join(stageBase, 'macos-app-'));
|
|
625
|
+
const appBundle = path.join(bundleStageRoot, `${productName}.app`);
|
|
576
626
|
const contents = path.join(appBundle, 'Contents');
|
|
577
627
|
const macosDir = path.join(contents, 'MacOS');
|
|
578
628
|
const resources = path.join(contents, 'Resources');
|
|
579
629
|
const mainExecutable = path.join(macosDir, productName);
|
|
580
630
|
const nativeHost = path.join(macosDir, 'AtomJSWindowHost');
|
|
581
631
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
await run('/usr/bin/xcrun', [
|
|
596
|
-
'clang',
|
|
597
|
-
'-fobjc-arc',
|
|
598
|
-
'-fmodules',
|
|
599
|
-
'-mmacosx-version-min=12.0',
|
|
600
|
-
'-framework', 'Cocoa',
|
|
601
|
-
'-framework', 'WebKit',
|
|
602
|
-
hostSource,
|
|
603
|
-
'-o', nativeHost
|
|
604
|
-
]);
|
|
605
|
-
await fs.promises.chmod(nativeHost, 0o755);
|
|
632
|
+
try {
|
|
633
|
+
await fse.ensureDir(macosDir);
|
|
634
|
+
await fse.ensureDir(resources);
|
|
635
|
+
await fse.copy(path.join(unpacked, executableName), mainExecutable);
|
|
636
|
+
await fse.copy(path.join(unpacked, 'ATOMJS-CREDIT.txt'), path.join(resources, 'ATOMJS-CREDIT.txt'));
|
|
637
|
+
await fs.promises.chmod(mainExecutable, 0o755);
|
|
638
|
+
|
|
639
|
+
if (!fs.existsSync(hostSource)) {
|
|
640
|
+
throw new Error(`AtomJS macOS native host source was not found: ${hostSource}`);
|
|
641
|
+
}
|
|
642
|
+
if (!commandExists('/usr/bin/xcrun', ['--version'])) {
|
|
643
|
+
throw new Error('macOS builds require the Xcode Command Line Tools. Run `xcode-select --install`.');
|
|
644
|
+
}
|
|
606
645
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
646
|
+
await run('/usr/bin/xcrun', [
|
|
647
|
+
'clang',
|
|
648
|
+
'-fobjc-arc',
|
|
649
|
+
'-fmodules',
|
|
650
|
+
'-mmacosx-version-min=12.0',
|
|
651
|
+
'-framework', 'Cocoa',
|
|
652
|
+
'-framework', 'WebKit',
|
|
653
|
+
hostSource,
|
|
654
|
+
'-o', nativeHost
|
|
655
|
+
]);
|
|
656
|
+
await fs.promises.chmod(nativeHost, 0o755);
|
|
657
|
+
|
|
658
|
+
let iconEntry = '';
|
|
659
|
+
const iconSource = project.config.icon ? path.resolve(project.root, project.config.icon) : null;
|
|
660
|
+
if (iconSource && fs.existsSync(iconSource) && path.extname(iconSource).toLowerCase() === '.icns') {
|
|
661
|
+
await fse.copy(iconSource, path.join(resources, 'AppIcon.icns'));
|
|
662
|
+
iconEntry = '<key>CFBundleIconFile</key><string>AppIcon</string>';
|
|
663
|
+
}
|
|
613
664
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
665
|
+
const version = String(project.packageJson.version || '0.0.0');
|
|
666
|
+
const bundleVersion = (version.match(/\d+/g) || ['1']).slice(0, 3).join('.');
|
|
667
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
617
668
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
618
669
|
<plist version="1.0"><dict>
|
|
619
670
|
<key>CFBundleExecutable</key><string>${xml(productName)}</string>
|
|
@@ -627,35 +678,88 @@ async function packageMacOS({ project, buildRoot, unpacked, executableName, prod
|
|
|
627
678
|
<key>NSHighResolutionCapable</key><true/>
|
|
628
679
|
${iconEntry}
|
|
629
680
|
</dict></plist>`;
|
|
630
|
-
|
|
631
|
-
|
|
681
|
+
const plistPath = path.join(contents, 'Info.plist');
|
|
682
|
+
await fs.promises.writeFile(plistPath, plist, 'utf8');
|
|
632
683
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
684
|
+
if (commandExists('/usr/bin/plutil', ['-help'])) {
|
|
685
|
+
await run('/usr/bin/plutil', ['-lint', plistPath]);
|
|
686
|
+
}
|
|
636
687
|
|
|
637
|
-
|
|
638
|
-
await run('/usr/bin/codesign', ['--force', '--sign', '-', nativeHost]);
|
|
639
|
-
await run('/usr/bin/codesign', ['--force', '--sign', '-', mainExecutable]);
|
|
640
|
-
await run('/usr/bin/codesign', ['--force', '--deep', '--sign', '-', appBundle]);
|
|
641
|
-
await run('/usr/bin/codesign', ['--verify', '--deep', '--strict', appBundle]);
|
|
642
|
-
}
|
|
688
|
+
await sanitizeMacBundle(appBundle);
|
|
643
689
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
await archiveDirectory(appBundle, zipPath, 'zip', `${productName}.app`);
|
|
649
|
-
}
|
|
650
|
-
outputs.push(zipPath);
|
|
690
|
+
if (hasMacCodeSigningTool()) {
|
|
691
|
+
const signingEnvironment = { ...process.env, COPYFILE_DISABLE: '1' };
|
|
692
|
+
await run('/usr/bin/codesign', ['--force', '--sign', '-', nativeHost], { env: signingEnvironment });
|
|
693
|
+
await run('/usr/bin/codesign', ['--force', '--sign', '-', mainExecutable], { env: signingEnvironment });
|
|
651
694
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
695
|
+
// Signing nested Mach-O files on non-APFS volumes can create fresh `._*`
|
|
696
|
+
// sidecars. They must not be present when the outer bundle is sealed.
|
|
697
|
+
await sanitizeMacBundle(appBundle);
|
|
698
|
+
await run('/usr/bin/codesign', ['--force', '--deep', '--sign', '-', appBundle], { env: signingEnvironment });
|
|
699
|
+
await run('/usr/bin/codesign', ['--verify', '--deep', '--strict', '--verbose=2', appBundle], { env: signingEnvironment });
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Assemble and sign on the local temporary filesystem, then copy the sealed
|
|
703
|
+
// bundle to the project output. This avoids AppleDouble metadata generated by
|
|
704
|
+
// external/exFAT project drives such as /Volumes/... during codesign.
|
|
705
|
+
await fse.remove(finalAppBundle);
|
|
706
|
+
await fse.copy(appBundle, finalAppBundle);
|
|
707
|
+
await fs.promises.chmod(path.join(finalAppBundle, 'Contents', 'MacOS', productName), 0o755);
|
|
708
|
+
await fs.promises.chmod(path.join(finalAppBundle, 'Contents', 'MacOS', 'AtomJSWindowHost'), 0o755);
|
|
709
|
+
await sanitizeMacBundle(finalAppBundle);
|
|
710
|
+
|
|
711
|
+
if (hasMacCodeSigningTool()) {
|
|
712
|
+
await run('/usr/bin/codesign', ['--verify', '--deep', '--strict', '--verbose=2', finalAppBundle], {
|
|
713
|
+
env: { ...process.env, COPYFILE_DISABLE: '1' }
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const zipPath = path.join(buildRoot, `${productName}-macos.zip`);
|
|
718
|
+
if (commandExists('ditto', ['-h'])) {
|
|
719
|
+
await run('ditto', ['--norsrc', '-c', '-k', '--keepParent', finalAppBundle, zipPath], {
|
|
720
|
+
env: { ...process.env, COPYFILE_DISABLE: '1' }
|
|
721
|
+
});
|
|
722
|
+
} else {
|
|
723
|
+
await archiveDirectory(finalAppBundle, zipPath, 'zip', `${productName}.app`);
|
|
724
|
+
}
|
|
725
|
+
outputs.push(zipPath);
|
|
726
|
+
|
|
727
|
+
if (commandExists('/usr/bin/hdiutil', ['help'])) {
|
|
728
|
+
const dmgPath = path.join(buildRoot, `${productName}.dmg`);
|
|
729
|
+
const temporaryDmgPath = path.join(bundleStageRoot, `${productName}.dmg`);
|
|
730
|
+
|
|
731
|
+
try {
|
|
732
|
+
// hdiutil can reject both source and output paths located on external,
|
|
733
|
+
// removable, network, or non-APFS volumes. Create the image entirely on
|
|
734
|
+
// the local temporary filesystem, then copy the finished DMG to build/.
|
|
735
|
+
await fse.remove(temporaryDmgPath);
|
|
736
|
+
await run('/usr/bin/hdiutil', [
|
|
737
|
+
'create',
|
|
738
|
+
'-volname', productName,
|
|
739
|
+
'-srcfolder', appBundle,
|
|
740
|
+
'-ov',
|
|
741
|
+
'-format', 'UDZO',
|
|
742
|
+
temporaryDmgPath
|
|
743
|
+
], {
|
|
744
|
+
env: { ...process.env, COPYFILE_DISABLE: '1' }
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
await fse.remove(dmgPath);
|
|
748
|
+
await fs.promises.copyFile(temporaryDmgPath, dmgPath);
|
|
749
|
+
outputs.push(dmgPath);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
await fse.remove(temporaryDmgPath);
|
|
752
|
+
await fse.remove(dmgPath);
|
|
753
|
+
|
|
754
|
+
if (process.env.ATOM_REQUIRE_DMG === '1') throw error;
|
|
755
|
+
console.warn(`AtomJS could not create the optional macOS DMG; the signed .app and ZIP are still valid. ${error.message}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
657
758
|
|
|
658
|
-
|
|
759
|
+
return { outputs, appBundle: finalAppBundle };
|
|
760
|
+
} finally {
|
|
761
|
+
await fse.remove(bundleStageRoot);
|
|
762
|
+
}
|
|
659
763
|
}
|
|
660
764
|
|
|
661
765
|
async function packageLinux({ project, buildRoot, unpacked, executableName, productName }) {
|
|
@@ -808,4 +912,4 @@ function xml(value) {
|
|
|
808
912
|
return String(value).replace(/[<>&'\"]/g, (char) => ({ '<': '<', '>': '>', '&': '&', "'": ''', '"': '"' })[char]);
|
|
809
913
|
}
|
|
810
914
|
|
|
811
|
-
module.exports = { buildCommand, localBuild, createApplicationPayload, readPortableExecutableLayout };
|
|
915
|
+
module.exports = { buildCommand, localBuild, createApplicationPayload, readPortableExecutableLayout, removeMacMetadataFiles };
|
package/src/init.cjs
CHANGED
|
@@ -30,14 +30,14 @@ async function initCommand(directory, options = {}) {
|
|
|
30
30
|
start: 'atom run build'
|
|
31
31
|
},
|
|
32
32
|
dependencies: {
|
|
33
|
-
'@atom-js-org/runtime': '0.4.
|
|
34
|
-
electron: 'npm:@atom-js-org/electron@0.4.
|
|
33
|
+
'@atom-js-org/runtime': '0.4.5-alpha.0',
|
|
34
|
+
electron: 'npm:@atom-js-org/electron@0.4.5-alpha.0'
|
|
35
35
|
},
|
|
36
36
|
optionalDependencies: {
|
|
37
37
|
'webview-nodejs': '0.5.0'
|
|
38
38
|
},
|
|
39
39
|
devDependencies: {
|
|
40
|
-
'@atom-js-org/cli': '0.4.
|
|
40
|
+
'@atom-js-org/cli': '0.4.5-alpha.0'
|
|
41
41
|
},
|
|
42
42
|
overrides: {
|
|
43
43
|
tar: '7.5.20'
|