Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public class ProjectGeneratorRequestBuilder implements ProjectGeneratorRequest.P
@CommandLineParser.Help("Whether the repository should be private")
private boolean privateRepository;

@CommandLineParser.Help("Whether to create the GitHub repository. If false, the repository is assumed to already exist and is only used as the push target. Defaults to true.")
private boolean createGitHubRepository = true;

@CommandLineParser.Help("Whether to use an existing directory")
private boolean useExistingDirectory;

Expand Down Expand Up @@ -394,6 +397,18 @@ public ProjectGeneratorRequest.Params setPrivateRepository(boolean privateReposi
return this;
}

@Override
public ProjectGeneratorRequest.Params setCreateGitHubRepository(boolean createGitHubRepository) {
this.createGitHubRepository = createGitHubRepository;

return this;
}

@Override
public boolean isCreateGitHubRepository() {
return createGitHubRepository;
}

public String getGithubRepository() {
if (githubRepository != null) {
return githubRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class ProjectGeneratorRequest {

private final boolean privateRepository;

private final boolean createGitHubRepository;

private final boolean useExistingDirectory;

public File getParentDirectory() {
Expand Down Expand Up @@ -73,6 +75,10 @@ public boolean isPrivateRepository() {
return privateRepository;
}

public boolean isCreateGitHubRepository() {
return createGitHubRepository;
}

public boolean isUseExistingDirectory() {
return useExistingDirectory;
}
Expand All @@ -86,6 +92,9 @@ public interface Params {
@CommandLineParser.Alias("p")
boolean isPrivateRepository();

@CommandLineParser.Help("Whether to create the GitHub repository. If false, the repository is assumed to already exist and is only used as the push target. Defaults to true.")
boolean isCreateGitHubRepository();

@CommandLineParser.Help("Use existing directory")
@CommandLineParser.Alias("e")
boolean isUseExistingDirectory();
Expand Down Expand Up @@ -161,6 +170,8 @@ public interface Params {

Params setPrivateRepository(boolean privateRepository);

Params setCreateGitHubRepository(boolean createGitHubRepository);

Params setUseExistingDirectory(boolean useExistingDirectory);

Params setExtensions(String[] extensions);
Expand All @@ -182,6 +193,7 @@ public ProjectGeneratorRequest(Params params) {
this.extensions = params.getExtensions();
this.githubRepository = params.getGithubRepository();
this.privateRepository = params.isPrivateRepository();
this.createGitHubRepository = params.isCreateGitHubRepository();
this.useExistingDirectory = params.isUseExistingDirectory();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ca.weblite.jdeploy.services;

import java.io.IOException;

/**
* Thrown when a GitHub API request fails because of invalid, expired or missing
* credentials (HTTP 401). It extends {@link IOException} so existing callers
* continue to work, while allowing callers that care about authentication to
* distinguish a bad token from other I/O errors and prompt the user to
* re-authenticate.
*/
public class GitHubAuthenticationException extends IOException {

public GitHubAuthenticationException(String message) {
super(message);
}

public GitHubAuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,24 @@ public void createGitHubRepository(GitHubRepositoryIntializationRequest request)
"Repository '" + githubRepositoryDto.getFullRepositoryName() + "' created successfully."
);
} else {
try (BufferedReader errorReader = new BufferedReader(
new InputStreamReader(connection.getErrorStream()))
) {
StringBuilder errorMessage = new StringBuilder();
String line;
while ((line = errorReader.readLine()) != null) {
errorMessage.append(line);
StringBuilder errorMessage = new StringBuilder();
if (connection.getErrorStream() != null) {
try (BufferedReader errorReader = new BufferedReader(
new InputStreamReader(connection.getErrorStream()))
) {
String line;
while ((line = errorReader.readLine()) != null) {
errorMessage.append(line);
}
}
throw new IOException("Failed to create the repository. " +
"Response code: " + responseCode + ", Error message: " + errorMessage);
}
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
throw new GitHubAuthenticationException("Failed to create the repository. " +
"GitHub rejected the credentials (HTTP 401). Your GitHub token may be " +
"missing, invalid or expired. Error message: " + errorMessage);
}
throw new IOException("Failed to create the repository. " +
"Response code: " + responseCode + ", Error message: " + errorMessage);
}
} finally {
connection.disconnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public String getGitHubUsername() throws IOException {
return jsonResponse.getString("login");
}
} else {
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
throw new GitHubAuthenticationException("Failed to authenticate with GitHub (HTTP 401). " +
"Your GitHub token may be missing, invalid or expired.");
}
throw new IOException("Failed to retrieve GitHub username. Response code: " + responseCode);
}
}
Expand Down
86 changes: 51 additions & 35 deletions cli/src/main/java/ca/weblite/jdeploy/services/ProjectGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,52 +70,66 @@ public File generate(ProjectGeneratorRequest request) throws Exception {
if (!request.isUseExistingDirectory() && projectDir.exists() ) {
throw new Exception("Project directory already exists: " + projectDir.getAbsolutePath());
}
// Track whether we created the directory so we can clean it up if a later
// step fails (e.g. a GitHub authentication error), rather than leaving a
// half-generated project behind that blocks the user from retrying.
boolean projectDirCreated = !request.isUseExistingDirectory();
projectDir.mkdirs();
if (!projectDir.exists()) {
throw new IOException("Failed to create project directory: " + projectDir.getAbsolutePath());
}

String templateDirectory = request.getTemplateDirectory();
if (templateDirectory == null && request.getTemplateName() != null) {
projectTemplateCatalog.update();
templateDirectory = projectTemplateCatalog.getProjectTemplate(request.getTemplateName()).getAbsolutePath();
}
if (templateDirectory == null) {
throw new Exception("Template directory is not set");
}
try {
String templateDirectory = request.getTemplateDirectory();
if (templateDirectory == null && request.getTemplateName() != null) {
projectTemplateCatalog.update();
templateDirectory = projectTemplateCatalog.getProjectTemplate(request.getTemplateName()).getAbsolutePath();
}
if (templateDirectory == null) {
throw new Exception("Template directory is not set");
}

File templateDir = new File(templateDirectory);
if ( !templateDir.exists() ) {
throw new Exception("Template directory does not exist: " + templateDir.getAbsolutePath());
}
File[] files = templateDir.listFiles();
if (files == null) {
throw new IOException("Failed to get files in template directory: " + templateDir.getAbsolutePath());
}
for ( File file : files) {
if ( file.isDirectory() ) {
FileUtils.copyDirectory(file, new File(projectDir, file.getName()));
} else {
FileUtils.copyFileToDirectory(file, projectDir);
File templateDir = new File(templateDirectory);
if ( !templateDir.exists() ) {
throw new Exception("Template directory does not exist: " + templateDir.getAbsolutePath());
}
}
if (request.getExtensions() != null) {
for (String extension : request.getExtensions()) {
File extensionDir = projectTemplateCatalog.getExtensionTemplate(extension);
applyExtensionToProject(projectDir, extensionDir);
File[] files = templateDir.listFiles();
if (files == null) {
throw new IOException("Failed to get files in template directory: " + templateDir.getAbsolutePath());
}
for ( File file : files) {
if ( file.isDirectory() ) {
FileUtils.copyDirectory(file, new File(projectDir, file.getName()));
} else {
FileUtils.copyFileToDirectory(file, projectDir);
}
}
if (request.getExtensions() != null) {
for (String extension : request.getExtensions()) {
File extensionDir = projectTemplateCatalog.getExtensionTemplate(extension);
applyExtensionToProject(projectDir, extensionDir);

}
}
}

updateFilesInDirectory(projectDir, request);
updatePackageJsonWithGithubSettings(projectDir, request);
updateFilesInDirectory(projectDir, request);
updatePackageJsonWithGithubSettings(projectDir, request);

if (mavenWrapperInjector.isMavenProject(projectDir.getPath())) {
mavenWrapperInjector.installIntoProject(projectDir.getPath());
}
if (mavenWrapperInjector.isMavenProject(projectDir.getPath())) {
mavenWrapperInjector.installIntoProject(projectDir.getPath());
}

initializeAndPushGitRepository(projectDir, request);
return projectDir;
initializeAndPushGitRepository(projectDir, request);
return projectDir;
} catch (Exception e) {
if (projectDirCreated) {
FileUtils.deleteQuietly(projectDir);
FileUtils.deleteQuietly(
new File(projectDir.getParentFile(), projectDir.getName() + "-releases")
);
}
throw e;
}

}

Expand Down Expand Up @@ -191,7 +205,9 @@ private File initializeAndPushGitRepository(
requestBuilder.setRepoName(request.getGithubRepository())
.setProjectPath(projectDirectory.getAbsolutePath())
.setPrivate(request.isPrivateRepository());
gitHubRepositoryInitializer.createGitHubRepository(requestBuilder.build());
if (request.isCreateGitHubRepository()) {
gitHubRepositoryInitializer.createGitHubRepository(requestBuilder.build());
}

if (request.isPrivateRepository()) {
addSecretToWorkflow(request);
Expand Down
Loading