Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,17 @@ jDeploy is a Java desktop app deployment tool with a multi-module architecture:
- `package.json` files - NPM metadata for deployable apps
- `jdeploy.js` - JavaScript shim for running Java apps via NPM
- `action.yml` - GitHub Action definition
- `build_and_test.sh` - Main build script
- `build_and_test.sh` - Main build script

## Current Implementation Projects

### .jdpignore File Support Implementation
**Status**: In Progress
**Plan Document**: `rfc/jdpignore-implementation-plan.md`
**Description**: Migrating from `nativeNamespaces` in package.json to `.jdpignore` files for platform-specific native library filtering.

**Important**: When working on platform-specific bundles or native library filtering:
1. ALWAYS read `rfc/jdpignore-implementation-plan.md` first to understand the current plan and progress
2. Update the status tracking in that document as work is completed
3. Add implementation notes and decisions to the plan document
4. Follow the phase-by-phase approach outlined in the plan
1 change: 1 addition & 0 deletions cli/.jdpignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.example.test.native
1 change: 1 addition & 0 deletions cli/.jdpignore.mac-x64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!ca.weblite.native.mac.x64
1 change: 1 addition & 0 deletions cli/.jdpignore.win-x64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!ca.weblite.native.win.x64
118 changes: 117 additions & 1 deletion cli/src/main/java/ca/weblite/jdeploy/gui/JDeployProjectEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ca.weblite.jdeploy.gui.controllers.GenerateGithubWorkflowController;
import ca.weblite.jdeploy.gui.controllers.VerifyWebsiteController;
import ca.weblite.jdeploy.gui.services.SwingOneTimePasswordProvider;
import ca.weblite.jdeploy.gui.tabs.BundleFiltersPanel;
import ca.weblite.jdeploy.gui.tabs.CheerpJSettings;
import ca.weblite.jdeploy.gui.tabs.DetailsPanel;
import ca.weblite.jdeploy.gui.tabs.PermissionsPanel;
Expand Down Expand Up @@ -62,6 +63,8 @@
import java.nio.file.*;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
Expand All @@ -75,6 +78,8 @@ public class JDeployProjectEditor {
private JSONObject packageJSON;
private File packageJSONFile;
private String packageJSONMD5;
private Map<String, String> jdpignoreFileMD5s = new HashMap<>(); // Maps filename to MD5
private boolean processingJdpignoreChange = false;
private MainFields mainFields;
private ArrayList<DoctypeFields> doctypeFields;
private ArrayList<LinkFields> linkFields;
Expand All @@ -90,6 +95,8 @@ public class JDeployProjectEditor {

private DownloadPageSettingsPanel downloadPageSettingsPanel;
private PermissionsPanel permissionsPanel;
private BundleFiltersPanel bundleFiltersPanel;
private PublishSettingsPanel publishSettingsPanel;

private NPM npm = null;

Expand Down Expand Up @@ -153,6 +160,79 @@ private void handlePackageJSONChangedOnFileSystem() {
}
}

private void handleJdpignoreFileChangedOnFileSystem(String filename) {
if (processingJdpignoreChange || bundleFiltersPanel == null) {
return;
}
processingJdpignoreChange = true;
try {
File jdpignoreFile = new File(packageJSONFile.getParentFile(), filename);

// Calculate current hash
String currentHash = "";
if (jdpignoreFile.exists()) {
currentHash = MD5.getMD5Checksum(jdpignoreFile);
}

// Get stored hash
String storedHash = jdpignoreFileMD5s.get(filename);

if (currentHash.equals(storedHash)) {
// File hasn't actually changed
return;
}

if (!modified && !bundleFiltersPanel.hasUnsavedChanges()) {
// No unsaved changes, just reload
bundleFiltersPanel.reloadAllFiles();
updateJdpignoreFileMD5s();
return;
}

int result = JOptionPane.showConfirmDialog(frame,
"The " + filename + " file has been modified externally. " +
"Would you like to reload it? Unsaved changes will be lost.",
"Reload " + filename + "?",
JOptionPane.YES_NO_OPTION);

if (result == JOptionPane.YES_OPTION) {
bundleFiltersPanel.reloadAllFiles();
updateJdpignoreFileMD5s();
}
} catch (Exception ex) {
System.err.println("A problem occurred while handling a file system change to " + filename);
ex.printStackTrace(System.err);
} finally {
processingJdpignoreChange = false;
}
}

private void updateJdpignoreFileMD5s() {
try {
jdpignoreFileMD5s.clear();
// Track global .jdpignore file
File globalIgnoreFile = new File(packageJSONFile.getParentFile(), ".jdpignore");
if (globalIgnoreFile.exists()) {
jdpignoreFileMD5s.put(".jdpignore", MD5.getMD5Checksum(globalIgnoreFile));
}

// Track platform-specific .jdpignore files
ca.weblite.jdeploy.models.Platform[] platforms = ca.weblite.jdeploy.models.Platform.values();
for (ca.weblite.jdeploy.models.Platform platform : platforms) {
if (platform == ca.weblite.jdeploy.models.Platform.DEFAULT) {
continue; // Skip DEFAULT, already handled above
}
String filename = ".jdpignore." + platform.getIdentifier();
File platformIgnoreFile = new File(packageJSONFile.getParentFile(), filename);
if (platformIgnoreFile.exists()) {
jdpignoreFileMD5s.put(filename, MD5.getMD5Checksum(platformIgnoreFile));
}
}
} catch (Exception ex) {
System.err.println("Failed to update .jdpignore file MD5s: " + ex.getMessage());
}
}

private void reloadPackageJSON() throws IOException {
packageJSON = new JSONObject(FileUtils.readFileToString(packageJSONFile, "UTF-8"));
packageJSONMD5 = MD5.getMD5Checksum(packageJSONFile);
Expand All @@ -177,6 +257,9 @@ private void watchPackageJSONForChanges() throws IOException, InterruptedExcepti
String contextString = "" + event.context();
if ("package.json".equals(contextString)) {
EventQueue.invokeLater(() -> handlePackageJSONChangedOnFileSystem());
} else if (contextString.startsWith(".jdpignore")) {
// Handle .jdpignore file changes
EventQueue.invokeLater(() -> handleJdpignoreFileChangedOnFileSystem(contextString));
}
}

Expand Down Expand Up @@ -1464,6 +1547,21 @@ public boolean accept(File dir, String name) {
});
tabs.add("Permissions", permissionsPanel);

// Add Bundle Filters tab
bundleFiltersPanel = new BundleFiltersPanel(packageJSONFile.getParentFile());
bundleFiltersPanel.setOnChangeCallback(() -> setModified());
bundleFiltersPanel.loadConfiguration(packageJSON);

// Set up NPM enabled checker to check current UI state
bundleFiltersPanel.setNpmEnabledChecker(() -> {
return publishSettingsPanel != null && publishSettingsPanel.getNpmCheckbox().isSelected();
});

tabs.add("Platform-Specific Bundles", bundleFiltersPanel);

// Initialize .jdpignore file MD5 tracking
updateJdpignoreFileMD5s();

downloadPageSettingsPanel = new DownloadPageSettingsPanel(loadDownloadPageSettings());
downloadPageSettingsPanel.addChangeListener(evt -> {
this.saveDownloadPageSettings(downloadPageSettingsPanel.getSettings());
Expand All @@ -1479,6 +1577,18 @@ public boolean accept(File dir, String name) {
cnt.removeAll();
cnt.setLayout(new BorderLayout());
cnt.add(tabs, BorderLayout.CENTER);

// Add tab change listener to refresh Platform-Specific Bundles panel
// This handles cases where publish settings change but package.json hasn't been saved yet
tabs.addChangeListener(e -> {
int selectedIndex = tabs.getSelectedIndex();
if (selectedIndex >= 0) {
String tabTitle = tabs.getTitleAt(selectedIndex);
if ("Platform-Specific Bundles".equals(tabTitle) && bundleFiltersPanel != null) {
bundleFiltersPanel.refreshUI();
}
}
});

JPanel bottomButtons = new JPanel();
bottomButtons.setLayout(new FlowLayout(FlowLayout.RIGHT));
Expand Down Expand Up @@ -1563,7 +1673,8 @@ public boolean accept(File dir, String name) {
}

private Component createPublishSettingsPanel() {
PublishSettingsPanel panel = new PublishSettingsPanel();
publishSettingsPanel = new PublishSettingsPanel();
PublishSettingsPanel panel = publishSettingsPanel;
PublishTargetFactory factory = DIContext.get(PublishTargetFactory.class);
PublishTargetServiceInterface publishTargetService = DIContext.get(PublishTargetServiceInterface.class);
try {
Expand Down Expand Up @@ -1958,8 +2069,13 @@ private void handleSave() {
if (downloadPageSettingsPanel != null) {
saveDownloadPageSettings(downloadPageSettingsPanel.getSettings());
}
if (bundleFiltersPanel != null) {
bundleFiltersPanel.saveConfiguration(packageJSON.getJSONObject("jdeploy"));
bundleFiltersPanel.saveAllFiles();
}
FileUtil.writeStringToFile(packageJSON.toString(4), packageJSONFile);
packageJSONMD5 = MD5.getMD5Checksum(packageJSONFile);
updateJdpignoreFileMD5s(); // Update .jdpignore file MD5s after saving
clearModified();
context.onFileUpdated(packageJSONFile);
} catch (Exception ex) {
Expand Down
Loading
Loading