feat: initial project commit
commit to push first working snapshot to codeberg
This commit is contained in:
commit
09b1054588
25 changed files with 1405 additions and 0 deletions
104
README.md
Normal file
104
README.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# JakartaEE & Spring Boot Starter
|
||||
|
||||
This application is a GUI tool developed in Go using the Fyne framework (v2.5.4). It helps users set up a development environment for JakartaEE and Spring Boot projects by checking and installing necessary dependencies, and then allowing users to deploy starter projects.
|
||||
|
||||
## Features
|
||||
- *Dependency Check*: Verifies the installation of required tools (Visual Studio Code and Docker).
|
||||
|
||||
- *Automatic Installation*: Offers to install missing dependencies on supported platforms.
|
||||
|
||||
- *Project Selection*: Provides a list of starter projects for JakartaEE and Spring Boot.
|
||||
|
||||
- *Project Deployment*: Allows users to deploy selected projects and open them in Visual Studio Code with dev containers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go 1.23 or later
|
||||
|
||||
- Fyne v2.5.4
|
||||
|
||||
## Installation
|
||||
|
||||
### Option 1: Download Pre-built Binary
|
||||
|
||||
Go to the Releases page of this repository.
|
||||
|
||||
Download the appropriate binary for your operating system.
|
||||
|
||||
### Option 2: Build from Source
|
||||
|
||||
- Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourusername/jakartaee-springboot-starter.git
|
||||
```
|
||||
|
||||
Navigate to the project directory:
|
||||
|
||||
```bash
|
||||
cd jakartaee-springboot-starter
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
The list of dependencies required for the development of Fyne Apps can be found at “https://docs.fyne.io/started/”.
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
Build the application:
|
||||
|
||||
```bash
|
||||
go build
|
||||
```
|
||||
## Usage
|
||||
|
||||
Run the application:
|
||||
|
||||
If you downloaded a pre-built binary:
|
||||
|
||||
```bash
|
||||
./jakartaee-springboot-starter
|
||||
```
|
||||
|
||||
or by double-clicking the excutable.
|
||||
|
||||
If you built from source:
|
||||
|
||||
```bash
|
||||
./jakartaee-springboot-starter
|
||||
```
|
||||
|
||||
The application will launch a GUI window where you can:
|
||||
|
||||
1. Check and install dependencies (Visual Studio Code and Docker).
|
||||
2. Select a starter project from the available options.
|
||||
3. Deploy the selected project and open it in Visual Studio Code.
|
||||
|
||||
Project Structure
|
||||
|
||||
// TODO
|
||||
- main.go: Contains the entire application logic, including GUI setup, dependency checking, and project deployment.
|
||||
- projects/: Directory containing starter project templates.
|
||||
- projects.json: Configuration file for additional plugin projects.
|
||||
|
||||
## Customization
|
||||
|
||||
To add new starter projects:
|
||||
|
||||
1. Create a new project template in the projects/ directory.
|
||||
2. Add the project details to the projects.json file.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
License
|
||||
[Insert your chosen license here]
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Fyne: https://fyne.io/
|
||||
- JakartaEE: https://jakarta.ee/
|
||||
- Spring Boot: https://spring.io/projects/spring-boot
|
||||
2
cmd/jws/FyneApp.toml
Normal file
2
cmd/jws/FyneApp.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[Details]
|
||||
Build = 4
|
||||
BIN
cmd/jws/Icon.png
Normal file
BIN
cmd/jws/Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
113
cmd/jws/main.go
Normal file
113
cmd/jws/main.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// cmd/jws/main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"jws/internal/dependency"
|
||||
"jws/internal/gui"
|
||||
"jws/internal/project"
|
||||
"os"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
//go:embed all:projects
|
||||
var projectsFS embed.FS
|
||||
|
||||
var (
|
||||
dependencies []dependency.Dependency
|
||||
projects []project.Project
|
||||
mainWindow fyne.Window
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := app.New()
|
||||
mainWindow = a.NewWindow("JakartaEE & Spring Boot Starter")
|
||||
mainWindow.Resize(fyne.NewSize(600, 600))
|
||||
mainWindow.SetFixedSize(true)
|
||||
|
||||
// Initialize dependencies
|
||||
dependencies = []dependency.Dependency{
|
||||
{Name: "Visual Studio Code", Installed: false, Icon: theme.DocumentIcon()},
|
||||
{Name: "Docker", Installed: false, Icon: theme.MediaPlayIcon()},
|
||||
}
|
||||
|
||||
// Standard projects
|
||||
standartProjects := []project.Project{
|
||||
{
|
||||
Name: "JakartaEE Todo-App mit Servlet",
|
||||
Description: "Eine Todo-Anwendung mit Jakarta Servlet und\nPostgreSQL-Datenbank.",
|
||||
FolderName: "jakarta-servlet-todo",
|
||||
},
|
||||
{
|
||||
Name: "JakartaEE Todo-App mit JSP",
|
||||
Description: "Eine Todo-Anwendung mit Jakarta Server Pages und PostgreSQL-Datenbank.",
|
||||
FolderName: "jakarta-jsp-todo",
|
||||
},
|
||||
{
|
||||
Name: "JakartaEE Todo-App als REST-API",
|
||||
Description: "Eine Todo-Anwendung als REST-API mit JakartaEE und PostgreSQL-Datenbank.",
|
||||
FolderName: "jakarta-rest-todo",
|
||||
},
|
||||
{
|
||||
Name: "Spring Boot Todo-App als Microservice",
|
||||
Description: "Eine Todo-Anwendung als Microservice mit Spring Boot und PostgreSQL-Datenbank.",
|
||||
FolderName: "spring-boot-todo",
|
||||
},
|
||||
}
|
||||
|
||||
// Load additional projects from projects.json
|
||||
pluginProjects, err := loadProjects("projects.json")
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error loading projects: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
projects = append(standartProjects, pluginProjects...)
|
||||
|
||||
// Initialize GUI package
|
||||
gui.Init(mainWindow, dependencies, projects, projectsFS)
|
||||
|
||||
// Check dependencies
|
||||
dependency.CheckDependencies(dependencies)
|
||||
allInstalled := true
|
||||
for _, dep := range dependencies {
|
||||
if !dep.Installed {
|
||||
allInstalled = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Show appropriate screen
|
||||
if allInstalled {
|
||||
gui.ShowProjectScreen()
|
||||
} else {
|
||||
gui.ShowDependencyScreen()
|
||||
}
|
||||
|
||||
mainWindow.ShowAndRun()
|
||||
}
|
||||
|
||||
func loadProjects(filePath string) ([]project.Project, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []project.Project{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error opening JSON-file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var projects []project.Project
|
||||
decoder := json.NewDecoder(file)
|
||||
if err := decoder.Decode(&projects); err != nil {
|
||||
return nil, fmt.Errorf("error decoding JSON-file: %v", err)
|
||||
}
|
||||
|
||||
return projects, nil
|
||||
}
|
||||
0
cmd/jws/projects/jakarta-jsp-todo/pom.xml
Normal file
0
cmd/jws/projects/jakarta-jsp-todo/pom.xml
Normal file
0
cmd/jws/projects/jakarta-rest-todo/pom.xml
Normal file
0
cmd/jws/projects/jakarta-rest-todo/pom.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
FROM mcr.microsoft.com/devcontainers/java:1-21-bullseye
|
||||
|
||||
ARG INSTALL_MAVEN="false"
|
||||
ARG MAVEN_VERSION=""
|
||||
|
||||
ARG INSTALL_GRADLE="false"
|
||||
ARG GRADLE_VERSION=""
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y && \
|
||||
apt-get install -y \
|
||||
git
|
||||
|
||||
RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \
|
||||
&& if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi
|
||||
|
||||
RUN mkdir -p /workspaces/${localWorkspaceFolderBasename} && chown -R vscode:vscode /workspaces/${localWorkspaceFolderBasename}
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "Jakarta EE Todo App & PostgreSQL",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces",
|
||||
"extensions": ["vscjava.vscode-java-pack", "redhat.vscode-xml"],
|
||||
"forwardPorts": [9080],
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
version: "3.8"
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
||||
services:
|
||||
app:
|
||||
container_name: javadev
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_HOSTNAME: postgresdb
|
||||
volumes:
|
||||
- ..:/workspace
|
||||
command: sleep infinity
|
||||
network_mode: service:db
|
||||
stdin_open: true
|
||||
|
||||
db:
|
||||
container_name: postgresdb
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: postgres
|
||||
49
cmd/jws/projects/jakarta-servlet-todo/pom.xml
Normal file
49
cmd/jws/projects/jakarta-servlet-todo/pom.xml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>jakarta-servlet-todo</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<jakartaee.version>10.0.0</jakartaee.version>
|
||||
<hibernate.version>6.0.0.Final</hibernate.version>
|
||||
<postgresql.version>42.3.1</postgresql.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jakarta.platform</groupId>
|
||||
<artifactId>jakarta.jakartaee-api</artifactId>
|
||||
<version>${jakartaee.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.3.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.example.dao;
|
||||
|
||||
import com.example.model.Todo;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TodoDAO {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Transactional
|
||||
public void create(Todo todo) {
|
||||
em.persist(todo);
|
||||
}
|
||||
|
||||
public List<Todo> findAll() {
|
||||
return em.createQuery("SELECT t FROM Todo t", Todo.class).getResultList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void update(Todo todo) {
|
||||
em.merge(todo);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(Long id) {
|
||||
Todo todo = em.find(Todo.class, id);
|
||||
if (todo != null) {
|
||||
em.remove(todo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "todos")
|
||||
public class Todo {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean completed;
|
||||
|
||||
// Getter und Setter
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void setCompleted(boolean completed) {
|
||||
this.completed = completed;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.example.servlet;
|
||||
|
||||
import com.example.dao.TodoDAO;
|
||||
import com.example.model.Todo;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.WebServlet;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@WebServlet("/todos")
|
||||
public class TodoServlet extends HttpServlet {
|
||||
|
||||
@Inject
|
||||
private TodoDAO todoDAO;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
List<Todo> todos = todoDAO.findAll();
|
||||
req.setAttribute("todos", todos);
|
||||
req.getRequestDispatcher("/index.jsp").forward(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String title = req.getParameter("title");
|
||||
boolean completed = "on".equals(req.getParameter("completed"));
|
||||
|
||||
Todo todo = new Todo();
|
||||
todo.setTitle(title);
|
||||
todo.setCompleted(completed);
|
||||
|
||||
todoDAO.create(todo);
|
||||
resp.sendRedirect(req.getContextPath() + "/todos");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
|
||||
version="3.0">
|
||||
|
||||
<persistence-unit name="todoPU">
|
||||
<class>com.example.model.Todo</class>
|
||||
<properties>
|
||||
<property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
|
||||
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://db:5432/tododb"/>
|
||||
<property name="jakarta.persistence.jdbc.user" value="todouser"/>
|
||||
<property name="jakarta.persistence.jdbc.password" value="todopass"/>
|
||||
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
|
||||
<property name="hibernate.hbm2ddl.auto" value="update"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
</persistence>
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
|
||||
version="4.0">
|
||||
|
||||
<display-name>JakartaEE Todo App</display-name>
|
||||
|
||||
</web-app>
|
||||
|
||||
0
cmd/jws/projects/spring-boot-todo/pom.xml
Normal file
0
cmd/jws/projects/spring-boot-todo/pom.xml
Normal file
40
go.mod
Normal file
40
go.mod
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
module jws
|
||||
|
||||
go 1.23.5
|
||||
|
||||
require (
|
||||
fyne.io/fyne/v2 v2.6.0-alpha1
|
||||
fyne.io/tools v1.0.0-alpha1
|
||||
)
|
||||
|
||||
require (
|
||||
fyne.io/systray v1.11.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fredbi/uri v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fyne-io/gl-js v0.1.0 // indirect
|
||||
github.com/fyne-io/glfw-js v0.1.0 // indirect
|
||||
github.com/fyne-io/image v0.1.0 // indirect
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
|
||||
github.com/go-text/render v0.2.0 // indirect
|
||||
github.com/go-text/typesetting v0.2.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rymdport/portal v0.3.0 // indirect
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
golang.org/x/image v0.24.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
76
go.sum
Normal file
76
go.sum
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
fyne.io/fyne/v2 v2.6.0-alpha1 h1:ALx1JJDdCYQpm5RS7CIK9bam9H7hddSDuRy/fyj9pb8=
|
||||
fyne.io/fyne/v2 v2.6.0-alpha1/go.mod h1:Bzv2yK+ncZ8LJbHKjyJJpEAFlbs6oulHgKm04ObOqA8=
|
||||
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
||||
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||
fyne.io/tools v1.0.0-alpha1 h1:350eF+LVPbIHNgYPCoP04yCsQSFQ3aupATx33ClZjZk=
|
||||
fyne.io/tools v1.0.0-alpha1/go.mod h1:7gcHTl85tD/yLcGcU2bJGECAwSVAWrr99Ngmgrz/s54=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
|
||||
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fyne-io/gl-js v0.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
|
||||
github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
||||
github.com/fyne-io/glfw-js v0.1.0 h1:RGGMmVjcsG17Oifl3X2UJ5vH3PgS4B1UY3ASeN5BXbI=
|
||||
github.com/fyne-io/glfw-js v0.1.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
|
||||
github.com/fyne-io/image v0.1.0 h1:Vm2TQJ2PWGHCf3jYi1/XroaNNMu+GfI/O2QpSbZd4XQ=
|
||||
github.com/fyne-io/image v0.1.0/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
|
||||
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
|
||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rymdport/portal v0.3.0 h1:QRHcwKwx3kY5JTQcsVhmhC3TGqGQb9LFghVNUy8AdB8=
|
||||
github.com/rymdport/portal v0.3.0/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
315
internal/dependency/dependency.go
Normal file
315
internal/dependency/dependency.go
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
package dependency
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
osinfo "jws/internal/os"
|
||||
"jws/pkg/download"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"fyne.io/tools"
|
||||
)
|
||||
|
||||
type Dependency struct {
|
||||
Name string
|
||||
Installed bool
|
||||
Icon fyne.Resource
|
||||
}
|
||||
|
||||
func CheckDependencies(dependencies []Dependency) {
|
||||
// Check VSCode
|
||||
dependencies[0].Installed = checkVSCode()
|
||||
|
||||
// Check Docker
|
||||
if runtime.GOOS == "windows" {
|
||||
dependencies[1].Installed = checkDockerDesktop()
|
||||
} else {
|
||||
dependencies[1].Installed = checkDocker()
|
||||
}
|
||||
}
|
||||
|
||||
func checkVSCode() bool {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
_, err := os.Stat(filepath.Join(os.Getenv("LOCALAPPDATA"), "Programs", "Microsoft VS Code", "Code.exe"))
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
_, err = os.Stat(filepath.Join(os.Getenv("ProgramFiles"), "Microsoft VS Code", "Code.exe"))
|
||||
return err == nil
|
||||
case "darwin":
|
||||
_, err := os.Stat("/Applications/Visual Studio Code.app")
|
||||
if err != nil {
|
||||
cmd := tools.CommandInShell("which", "code")
|
||||
return cmd.Run() == nil
|
||||
}
|
||||
return err == nil
|
||||
case "linux":
|
||||
cmd := tools.CommandInShell("which", "code")
|
||||
return cmd.Run() == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func checkDocker() bool {
|
||||
cmd := tools.CommandInShell("which", "docker")
|
||||
return cmd.Run() == nil
|
||||
}
|
||||
|
||||
func checkDockerDesktop() bool {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
_, err := os.Stat(filepath.Join(os.Getenv("ProgramFiles"), "Docker", "Docker", "Docker Desktop.exe"))
|
||||
return err == nil
|
||||
case "darwin":
|
||||
_, err := os.Stat("/Applications/Docker.app")
|
||||
return err == nil
|
||||
case "linux":
|
||||
cmd := tools.CommandInShell("systemctl", "is-active", "docker")
|
||||
output, _ := cmd.Output()
|
||||
return strings.TrimSpace(string(output)) == "active"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func InstallDependency(index int, sudoPassword string, dependencies []Dependency, mainWindow fyne.Window) {
|
||||
depName := dependencies[index].Name
|
||||
|
||||
var cmd *exec.Cmd
|
||||
var err error
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
switch index {
|
||||
case 0: // VSCode
|
||||
cmd = tools.CommandInShell("winget", "install", "-e", "--id",
|
||||
"Microsoft.VisualStudioCode")
|
||||
case 1: // Docker Desktop
|
||||
wslCheckCmd := tools.CommandInShell("wsl", "--status")
|
||||
err := wslCheckCmd.Run()
|
||||
if err != nil {
|
||||
wslInstallCmd := tools.CommandInShell("wsl", "--install")
|
||||
err = wslInstallCmd.Run()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error: installing WSL: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
dialog.ShowInformation("WSL wird installiert", "WSL wird installiert. Bitte warten Sie, bis die Installation abgeschlossen ist und starten Sie die Anwendung neu.", mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
cmd = tools.CommandInShell("winget", "install", "-e", "--id",
|
||||
"Docker.DockerDesktop")
|
||||
}
|
||||
case "darwin":
|
||||
brewCheckCmd := tools.CommandInShell("which", "brew")
|
||||
err := brewCheckCmd.Run()
|
||||
if err != nil {
|
||||
brewInstallCmd := tools.CommandInShell("bin/bash", "-c", "\"$(curl -fsSl https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
|
||||
err := brewInstallCmd.Run()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error: installing homebrew falied: %v", err), mainWindow)
|
||||
}
|
||||
}
|
||||
switch index {
|
||||
case 0: // VSCode
|
||||
cmd = tools.CommandInShell("brew", "install", "--cask", "visual-studio-code")
|
||||
case 1: // Docker
|
||||
cmd = tools.CommandInShell("brew", "install", "docker")
|
||||
}
|
||||
case "linux":
|
||||
osInfo, err := osinfo.GetLinuxDistribution()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
dialog.ShowError(fmt.Errorf("error getting OS info: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
var downloadURL, fileName string
|
||||
switch index {
|
||||
case 0: // VSCode
|
||||
switch osInfo.ID {
|
||||
case "debian", "ubuntu", "linuxmint":
|
||||
downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64"
|
||||
fileName = "vscode.deb"
|
||||
case "fedora":
|
||||
downloadURL = "https://code.visualstudio.com/sha/download?build=stable&os=linux-rpm-x64"
|
||||
fileName = "vscode.rpm"
|
||||
default:
|
||||
dialog.ShowInformation("Nicht unterstützt", fmt.Sprintf("Automatische Installation für dieses OS %v nicht verfügbar", osInfo), mainWindow)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
progressBar := widget.NewProgressBar()
|
||||
progressDialog := dialog.NewCustomWithoutButtons("Download in progress", progressBar, mainWindow)
|
||||
|
||||
progressDialog.Show()
|
||||
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
downloadDir := filepath.Join(homeDir, "Downloads")
|
||||
filePath := filepath.Join(downloadDir, fileName)
|
||||
|
||||
err := download.WithProgressBar(downloadURL, filePath, progressBar)
|
||||
progressDialog.Hide()
|
||||
|
||||
if err != nil {
|
||||
dialog.ShowError(err, mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
var installCmd string
|
||||
switch osInfo.ID {
|
||||
case "debian", "ubuntu", "linuxmint":
|
||||
installCmd = fmt.Sprintf("sudo -S dpkg -i %s", filePath)
|
||||
case "fedora":
|
||||
installCmd = fmt.Sprintf("sudo -S dnf install -y %s", filePath)
|
||||
}
|
||||
|
||||
cmd := exec.Command("sh", "-c", installCmd)
|
||||
cmd.Stdin = strings.NewReader(sudoPassword + "\n")
|
||||
|
||||
dialog.ShowInformation("Installation gestartet",
|
||||
"Die Installation von VSCode wurde gestartet.", mainWindow)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("installation failed:\n%s", output), mainWindow)
|
||||
return
|
||||
} else {
|
||||
dialog.ShowInformation("Erfolg", "VSCode erfolgreich installiert!", mainWindow)
|
||||
CheckDependencies(dependencies)
|
||||
}
|
||||
}()
|
||||
case 1: // Docker
|
||||
go func() {
|
||||
progressBar := widget.NewProgressBar()
|
||||
progressDialog := dialog.NewCustomWithoutButtons("Docker Installation läuft...", progressBar, mainWindow)
|
||||
progressDialog.Show()
|
||||
|
||||
osInfo, err := osinfo.GetLinuxDistribution()
|
||||
if err != nil {
|
||||
progressDialog.Hide()
|
||||
dialog.ShowError(fmt.Errorf("error getting os infos: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
var commands []string
|
||||
var cleanupCommands []string
|
||||
var totalSteps int
|
||||
|
||||
switch osInfo.ID {
|
||||
case "ubuntu", "linuxmint", "debian":
|
||||
// Ubuntu/Debian Commands
|
||||
distroPath := "ubuntu"
|
||||
codeName := "$(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\")"
|
||||
if osInfo.ID == "debian" {
|
||||
distroPath = "debian"
|
||||
codeName = "$(. /etc/os-release && echo \"$VERSION_CODENAME\")"
|
||||
}
|
||||
arch := "$(dpkg --print-architecture)"
|
||||
|
||||
commands = []string{
|
||||
"apt-get update",
|
||||
"apt-get install -y wget",
|
||||
fmt.Sprintf("wget -qO- https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/ | grep -oP 'href=\"\\K[^\"]*(?=.*deb)' | xargs -I{} wget https://download.docker.com/linux/%s/dists/%s/pool/stable/%s/{}",
|
||||
distroPath, codeName, arch, distroPath, codeName, arch),
|
||||
"dpkg -i ./containerd.io*.deb docker-ce*.deb docker-ce-cli*.deb docker-buildx-plugin*.deb docker-compose-plugin*.deb",
|
||||
"apt-get install -f -y",
|
||||
"service docker start",
|
||||
}
|
||||
|
||||
cleanupCommands = []string{
|
||||
"rm -rf ./containerd.io*.deb ./docker-ce*.deb ./docker-ce-cli*.deb ./docker-buildx-plugin*.deb ./docker-compose-plugin*.deb",
|
||||
"apt-get autoremove -y",
|
||||
"apt-get clean",
|
||||
}
|
||||
|
||||
totalSteps = len(commands) + len(cleanupCommands)
|
||||
|
||||
case "fedora":
|
||||
// Fedora Commands
|
||||
fedoraVer := "$(rpm -E %fedora)"
|
||||
commands = []string{
|
||||
"dnf install -y wget",
|
||||
fmt.Sprintf("wget https://download.docker.com/linux/fedora/%s/x86_64/stable/Packages/containerd-*.rpm docker-*.rpm docker-ce-*.rpm", fedoraVer),
|
||||
"dnf install -y ./*.rpm",
|
||||
"systemctl enable --now docker",
|
||||
}
|
||||
|
||||
cleanupCommands = []string{
|
||||
"rm -rf ./containerd-*.rpm ./docker-*.rpm ./docker-ce-*.rpm",
|
||||
"dnf autoremove -y",
|
||||
"dnf clean all",
|
||||
}
|
||||
|
||||
totalSteps = len(commands) + len(cleanupCommands)
|
||||
|
||||
default:
|
||||
progressDialog.Hide()
|
||||
dialog.ShowInformation("not supported", "Automatic Docker installation not supported for your OS.", mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
progressStep := 1.0 / float64(totalSteps)
|
||||
currentProgress := 0.0
|
||||
|
||||
for _, cmd := range commands {
|
||||
command := exec.Command("sudo", "-S", "sh", "-c", cmd)
|
||||
command.Stdin = strings.NewReader(sudoPassword + "\n")
|
||||
|
||||
if output, err := command.CombinedOutput(); err != nil {
|
||||
progressDialog.Hide()
|
||||
dialog.ShowError(fmt.Errorf("error at %s:\n%s", cmd, output), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
currentProgress += progressStep
|
||||
progressBar.SetValue(currentProgress)
|
||||
}
|
||||
|
||||
for _, cmd := range cleanupCommands {
|
||||
command := exec.Command("sudo", "-S", "sh", "-c", cmd)
|
||||
command.Stdin = strings.NewReader(sudoPassword + "\n")
|
||||
|
||||
if output, err := command.CombinedOutput(); err != nil {
|
||||
progressDialog.Hide()
|
||||
dialog.ShowError(fmt.Errorf("cleanup error at %s:\n%s", cmd, output), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
currentProgress += progressStep
|
||||
progressBar.SetValue(currentProgress)
|
||||
}
|
||||
|
||||
progressDialog.Hide()
|
||||
|
||||
dialog.ShowInformation(
|
||||
"Installation finished",
|
||||
"Docker was succesfully installed! Please re-start your system to let the changes take effect.",
|
||||
mainWindow,
|
||||
)
|
||||
|
||||
CheckDependencies(dependencies)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
if cmd != nil {
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error starting installation process: %v", err), mainWindow)
|
||||
} else {
|
||||
dialog.ShowInformation("Installation started",
|
||||
fmt.Sprintf("Installation of %s was started", depName), mainWindow)
|
||||
}
|
||||
}
|
||||
}
|
||||
92
internal/gui/dependency_screen.go
Normal file
92
internal/gui/dependency_screen.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"jws/internal/dependency"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// ShowDependencyScreen displays the dependency check screen
|
||||
func ShowDependencyScreen() {
|
||||
title := widget.NewLabel("check dependencies")
|
||||
title.TextStyle = fyne.TextStyle{Bold: true}
|
||||
title.Alignment = fyne.TextAlignCenter
|
||||
|
||||
description := widget.NewLabel("dependencies needed for dev-environment:")
|
||||
description.Wrapping = fyne.TextWrapWord
|
||||
|
||||
dependencyContainer := container.NewVBox()
|
||||
allInstalled := true
|
||||
|
||||
for i, dep := range dependencies {
|
||||
statusIcon := theme.CancelIcon()
|
||||
statusText := "not installed"
|
||||
|
||||
if dep.Installed {
|
||||
statusIcon = theme.ConfirmIcon()
|
||||
statusText = "installed"
|
||||
} else {
|
||||
allInstalled = false
|
||||
}
|
||||
|
||||
depBox := container.NewHBox(
|
||||
widget.NewIcon(dep.Icon),
|
||||
widget.NewLabel(dep.Name),
|
||||
layout.NewSpacer(),
|
||||
widget.NewIcon(statusIcon),
|
||||
widget.NewLabel(statusText),
|
||||
)
|
||||
|
||||
dependencyContainer.Add(depBox)
|
||||
|
||||
if !dep.Installed {
|
||||
installBtn := widget.NewButton("install", func(i int) func() {
|
||||
return func() {
|
||||
ShowPasswordDialog(i)
|
||||
}
|
||||
}(i))
|
||||
|
||||
depBox = container.NewStack(
|
||||
layout.NewSpacer(),
|
||||
installBtn,
|
||||
)
|
||||
dependencyContainer.Add(depBox)
|
||||
}
|
||||
}
|
||||
|
||||
nextBtn := widget.NewButton("go to projects", func() {
|
||||
if allInstalled {
|
||||
ShowProjectScreen()
|
||||
} else {
|
||||
dialog.ShowInformation("dependencies missing",
|
||||
"Bitte installieren Sie alle erforderlichen Abhängigkeiten, bevor Sie fortfahren.", mainWindow)
|
||||
}
|
||||
})
|
||||
|
||||
nextBtn.Importance = widget.HighImportance
|
||||
|
||||
recheckBtn := widget.NewButton("check again", func() {
|
||||
dependency.CheckDependencies(dependencies)
|
||||
ShowDependencyScreen()
|
||||
})
|
||||
|
||||
buttonContainer := container.New(layout.NewGridLayout(2), recheckBtn, nextBtn)
|
||||
|
||||
paddedButtonContainer := container.NewPadded(buttonContainer)
|
||||
|
||||
description.Alignment = fyne.TextAlignCenter
|
||||
header := container.NewVBox(title, description)
|
||||
content := container.NewBorder(header,
|
||||
paddedButtonContainer,
|
||||
nil,
|
||||
nil,
|
||||
dependencyContainer,
|
||||
)
|
||||
|
||||
mainWindow.SetContent(content)
|
||||
}
|
||||
47
internal/gui/gui.go
Normal file
47
internal/gui/gui.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"jws/internal/dependency"
|
||||
"jws/internal/project"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/data/binding"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
var (
|
||||
// Global variables available to the package
|
||||
mainWindow fyne.Window
|
||||
dependencies []dependency.Dependency
|
||||
projects []project.Project
|
||||
projectsFS embed.FS
|
||||
)
|
||||
|
||||
// Init initializes the GUI package with required dependencies
|
||||
func Init(window fyne.Window, deps []dependency.Dependency, projs []project.Project, fs embed.FS) {
|
||||
mainWindow = window
|
||||
dependencies = deps
|
||||
projects = projs
|
||||
projectsFS = fs
|
||||
}
|
||||
|
||||
// ShowPasswordDialog shows a dialog to enter sudo password for installations
|
||||
func ShowPasswordDialog(index int) {
|
||||
password := binding.NewString()
|
||||
entry := widget.NewEntryWithData(password)
|
||||
entry.Password = true
|
||||
|
||||
dialog.NewForm("Sudo Password", "ok", "cancel",
|
||||
[]*widget.FormItem{widget.NewFormItem("password", entry)},
|
||||
func(b bool) {
|
||||
pass, err := password.Get()
|
||||
if err == nil {
|
||||
if b {
|
||||
dependency.InstallDependency(index, pass, dependencies, mainWindow)
|
||||
}
|
||||
}
|
||||
},
|
||||
mainWindow).Show()
|
||||
}
|
||||
91
internal/gui/project_screen.go
Normal file
91
internal/gui/project_screen.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"jws/internal/project"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// ShowProjectScreen displays the project selection screen
|
||||
func ShowProjectScreen() {
|
||||
title := widget.NewLabel("Project-Selection")
|
||||
title.TextStyle = fyne.TextStyle{Bold: true}
|
||||
title.Alignment = fyne.TextAlignCenter
|
||||
|
||||
description := widget.NewLabel("Choose a Starter-Project to deploy:")
|
||||
description.Wrapping = fyne.TextWrapWord
|
||||
description.Alignment = fyne.TextAlignCenter
|
||||
|
||||
projectsList := container.NewVBox()
|
||||
|
||||
for i, proj := range projects {
|
||||
projTitle := widget.NewLabel(proj.Name)
|
||||
projTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||
|
||||
projDesc := widget.NewLabel(proj.Description)
|
||||
projDesc.Wrapping = fyne.TextWrapWord
|
||||
|
||||
deployBtn := widget.NewButton("deploy", func(i int) func() {
|
||||
return func() {
|
||||
project.DeployProject(i, projects, projectsFS, mainWindow)
|
||||
}
|
||||
}(i))
|
||||
deployBtn.Importance = widget.HighImportance
|
||||
|
||||
projectContent := container.NewBorder(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
container.NewCenter(deployBtn),
|
||||
container.NewVBox(projTitle, projDesc),
|
||||
)
|
||||
|
||||
projectCard := container.NewBorder(
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
container.NewPadded(projectContent),
|
||||
)
|
||||
|
||||
bg := canvas.NewRectangle(theme.Color(theme.ColorNameBackground))
|
||||
|
||||
borderedContainer := container.NewStack(
|
||||
bg,
|
||||
projectCard,
|
||||
)
|
||||
|
||||
spacedContainer := container.NewVBox(
|
||||
borderedContainer,
|
||||
widget.NewSeparator(),
|
||||
)
|
||||
|
||||
projectsList.Add(spacedContainer)
|
||||
}
|
||||
|
||||
backBtn := widget.NewButton("Check Dependecies", func() {
|
||||
ShowDependencyScreen()
|
||||
})
|
||||
|
||||
scrollContainer := container.NewVScroll(projectsList)
|
||||
|
||||
header := container.NewVBox(
|
||||
title,
|
||||
description,
|
||||
widget.NewSeparator(),
|
||||
)
|
||||
|
||||
content := container.NewBorder(
|
||||
header,
|
||||
backBtn, // footer,
|
||||
nil,
|
||||
nil,
|
||||
scrollContainer,
|
||||
)
|
||||
|
||||
mainWindow.SetContent(content)
|
||||
}
|
||||
83
internal/os/osinfo.go
Normal file
83
internal/os/osinfo.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package os
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OS struct {
|
||||
ID string
|
||||
Name string
|
||||
Version string
|
||||
PackageManager string
|
||||
}
|
||||
|
||||
func GetLinuxDistribution() (*OS, error) {
|
||||
_, err := os.Stat("/etc/os-release")
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("unable to read system information")
|
||||
}
|
||||
|
||||
osRelease, _ := os.ReadFile("/etc/os-release")
|
||||
return parseOsRelease(string(osRelease)), nil
|
||||
}
|
||||
|
||||
func parseOsRelease(osRelease string) *OS {
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "Unknown"
|
||||
result.Version = "Unknown"
|
||||
|
||||
lines := strings.Split(osRelease, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
splitLine := strings.SplitN(line, "=", 2)
|
||||
if len(splitLine) != 2 {
|
||||
continue
|
||||
}
|
||||
switch splitLine[0] {
|
||||
case "ID":
|
||||
result.ID = strings.ToLower(strings.Trim(splitLine[1], "\""))
|
||||
case "NAME":
|
||||
result.Name = strings.Trim(splitLine[1], "\"")
|
||||
case "VERSION_ID":
|
||||
result.Version = strings.Trim(splitLine[1], "\"")
|
||||
}
|
||||
}
|
||||
err := result.getPackageManager()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (os *OS) getPackageManager() error {
|
||||
switch os.ID {
|
||||
case "debian", "ubuntu", "linuxmint":
|
||||
os.PackageManager = "apt"
|
||||
return nil
|
||||
case "arch":
|
||||
os.PackageManager = "pacman"
|
||||
return nil
|
||||
case "fedora":
|
||||
os.PackageManager = "dnf"
|
||||
return nil
|
||||
default:
|
||||
pmcommands := []string{
|
||||
"apt",
|
||||
"dnf",
|
||||
"pacman",
|
||||
}
|
||||
for _, pmname := range pmcommands {
|
||||
_, err := exec.LookPath(pmname)
|
||||
if err == nil {
|
||||
os.PackageManager = pmname
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no packagemanager found for os: %s", os.Name)
|
||||
}
|
||||
}
|
||||
127
internal/project/project.go
Normal file
127
internal/project/project.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
FolderName string `json:"folderName"`
|
||||
}
|
||||
|
||||
func DeployProject(index int, projects []Project, projectsFS embed.FS, mainWindow fyne.Window) {
|
||||
project := projects[index]
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error getting home directory: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
projectPath := filepath.Join(homeDir, "Projects", project.FolderName)
|
||||
|
||||
confirmDialog := dialog.NewConfirm(
|
||||
"deploying project",
|
||||
fmt.Sprintf("the project '%s' will be deployed to '%s' and opend in VS Code. Continue?", project.Name, projectPath),
|
||||
func(ok bool) {
|
||||
if ok {
|
||||
progress := widget.NewProgressBar()
|
||||
progressDiag := dialog.NewCustomWithoutButtons("project will be deployed", progress, mainWindow)
|
||||
|
||||
progressDiag.Show()
|
||||
|
||||
go func() {
|
||||
defer progressDiag.Hide()
|
||||
|
||||
progress.SetValue(0.1)
|
||||
err := os.MkdirAll(projectPath, 0755)
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error creating directory: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
progress.SetValue(0.3)
|
||||
err = CopyEmbeddedProject(projectsFS, project.FolderName, projectPath)
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error copying project files: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
|
||||
progress.SetValue(0.7)
|
||||
|
||||
var openCmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
openCmd = exec.Command("code", projectPath)
|
||||
case "darwin":
|
||||
openCmd = exec.Command("open", "-a", "Visual Studio Code", projectPath)
|
||||
case "linux":
|
||||
openCmd = exec.Command("code", projectPath)
|
||||
}
|
||||
|
||||
if openCmd != nil {
|
||||
progress.SetValue(0.9)
|
||||
err = openCmd.Run()
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("error opening VS Code: %v", err), mainWindow)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
progress.SetValue(1.0)
|
||||
|
||||
dialog.ShowInformation("project deployed",
|
||||
fmt.Sprintf("the project '%s' was successfully deployed and opend in VS Code.\n\n"+
|
||||
"Path: %s\n\n"+
|
||||
"Application can be started with Docker Compose.",
|
||||
project.Name, projectPath), mainWindow)
|
||||
}()
|
||||
}
|
||||
},
|
||||
mainWindow,
|
||||
)
|
||||
confirmDialog.Show()
|
||||
}
|
||||
|
||||
func CopyEmbeddedProject(projectsFS embed.FS, projectFolder string, targetPath string) error {
|
||||
sourcePath := fmt.Sprintf("projects/%s", projectFolder)
|
||||
|
||||
return fs.WalkDir(projectsFS, sourcePath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(sourcePath, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destPath := filepath.Join(targetPath, relPath)
|
||||
|
||||
if d.IsDir() {
|
||||
return os.MkdirAll(destPath, 0755)
|
||||
}
|
||||
|
||||
data, err := projectsFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(destPath, data, 0644)
|
||||
})
|
||||
}
|
||||
60
pkg/download/download.go
Normal file
60
pkg/download/download.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package download
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
func WithProgressBar(downloadURL, filePath string, progressBar *widget.ProgressBar) error {
|
||||
client := &http.Client{
|
||||
Timeout: 15 * time.Minute,
|
||||
}
|
||||
|
||||
resp, err := client.Get(downloadURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
contentLength := resp.ContentLength
|
||||
if contentLength <= 0 {
|
||||
return fmt.Errorf("invalid content length")
|
||||
}
|
||||
|
||||
progressBar.Max = float64(contentLength)
|
||||
|
||||
buffer := make([]byte, 4096)
|
||||
var totalBytes int64
|
||||
|
||||
for {
|
||||
n, readErr := resp.Body.Read(buffer)
|
||||
if n > 0 {
|
||||
totalBytes += int64(n)
|
||||
progressBar.SetValue(float64(totalBytes))
|
||||
|
||||
if _, writeErr := file.Write(buffer[:n]); writeErr != nil {
|
||||
return fmt.Errorf("error writing to file: %v", writeErr)
|
||||
}
|
||||
}
|
||||
|
||||
if readErr == io.EOF {
|
||||
break
|
||||
}
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("download aborted: %v", readErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue