Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4217ea11c8 | ||
|
|
f41e34486a | ||
|
|
29812e8175 |
@@ -71,7 +71,8 @@ jobs:
|
||||
working-directory: ./server
|
||||
run: |
|
||||
./mvnw -Pnative -DskipTests spring-boot:build-image \
|
||||
-Dspring-boot.build-image.imageName=ghcr.io/${{ steps.repo.outputs.name }}/api:latest
|
||||
-Dspring-boot.build-image.imageName=ghcr.io/${{ steps.repo.outputs.name }}/api:latest \
|
||||
-Dspring-boot.build-image.builder=paketobuildpacks/builder-jammy-tiny:latest
|
||||
|
||||
- name: Tag and push Docker image
|
||||
run: |
|
||||
@@ -85,6 +86,3 @@ jobs:
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -a api-v${{ steps.version.outputs.version }} -m "Release API v${{ steps.version.outputs.version }}"
|
||||
git push origin api-v${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Trigger Dokploy deployment
|
||||
run: curl -X POST "${{ vars.DOKPLOY_WEBHOOK_API }}"
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
# TaskNote
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||

|
||||
|
||||
### TaskNote API
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_server)
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_server)
|
||||
[](https://sonarcloud.io/summary/new_code?id=br.com.tasknoteapp%3Aserver)
|
||||
[](https://sonarcloud.io/summary/new_code?id=br.com.tasknoteapp%3Aserver)
|
||||
[](https://sonarcloud.io/summary/new_code?id=br.com.tasknoteapp%3Aserver)
|
||||
|
||||
### TaskNote WebApp
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_client)
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_client)
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_client)
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_client)
|
||||
[](https://sonarcloud.io/summary/new_code?id=ricardo-campos-org_react-typescript-todolist_client)
|
||||
[](https://github.com/RMCampos/tasknote/actions/workflows/client-ci.yml)
|
||||
[](https://github.com/RMCampos/tasknote/actions/workflows/server-ci.yml)
|
||||
[](https://github.com/RMCampos/tasknote/actions/workflows/main-client.yml)
|
||||
[](https://github.com/RMCampos/tasknote/actions/workflows/main-server.yml)
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
@@ -49,17 +38,17 @@ The project was born from a month-long technical challenge and has since grown t
|
||||
- **Responsive Design**: Mobile-first approach with Bootstrap 5 and dark/light theme support
|
||||
- **Data Visualization**: Task completion charts and productivity analytics
|
||||
- **File Attachments**: URL attachments for tasks and notes
|
||||
- **Tagging System**: `#tag` support for better organization
|
||||
- **Mobile App**: Native mobile applications for iOS and Android with PWA plugin
|
||||
|
||||
### Upcoming Features
|
||||
- **Tagging System**: `#tag` support for better organization
|
||||
- **Advanced Filters**: Enhanced search with date ranges, priority levels, and status filters
|
||||
- **Collaboration**: Share tasks and notes with other users
|
||||
- **Mobile App**: Native mobile applications for iOS and Android
|
||||
- **Notifications**: Email and push notifications for due dates and reminders
|
||||
|
||||
## 🚀 Tech Stack
|
||||
|
||||
### Frontend (React TypeScript)
|
||||
### Frontend (React & TypeScript)
|
||||
- **Framework**: React 19 with TypeScript for type safety
|
||||
- **Build Tool**: Vite for fast development and optimized production builds
|
||||
- **Testing**: Vitest with React Testing Library and comprehensive coverage reporting
|
||||
@@ -69,8 +58,8 @@ The project was born from a month-long technical challenge and has since grown t
|
||||
- **Internationalization**: i18next with automatic language detection
|
||||
- **API Client**: Centralized API service with automatic authentication headers
|
||||
|
||||
### Backend (Java Spring Boot)
|
||||
- **Framework**: Spring Boot 3.5+ with Java 17
|
||||
### Backend (Java & Spring Boot)
|
||||
- **Framework**: Spring Boot 4.x.x with Java 25
|
||||
- **Security**: Spring Security with JWT authentication and refresh tokens
|
||||
- **Database**: PostgreSQL with JPA/Hibernate ORM
|
||||
- **Migration**: Flyway for database schema versioning
|
||||
@@ -84,19 +73,16 @@ The project was born from a month-long technical challenge and has since grown t
|
||||
- **Containerization**: Docker and Docker Compose for development environment
|
||||
- **Web Server**: Caddy for reverse proxy and SSL termination
|
||||
- **CI/CD**: GitHub Actions with automated testing and quality gates
|
||||
- **Code Analysis**: SonarCloud integration for security and maintainability
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### Monorepo Structure
|
||||
```
|
||||
react-typescript-todolist/
|
||||
tasknote/
|
||||
├── client/ # React TypeScript frontend
|
||||
├── server/ # Java Spring Boot REST API
|
||||
├── angular/ # Alternative Angular frontend
|
||||
├── tools/ # Development and deployment scripts
|
||||
├── docker-compose.yml
|
||||
└── CLAUDE.md # AI assistant instructions
|
||||
└── docker-compose.yml
|
||||
```
|
||||
|
||||
### Frontend Architecture
|
||||
@@ -117,15 +103,15 @@ react-typescript-todolist/
|
||||
|
||||
### Prerequisites
|
||||
- **Docker & Docker Compose** (recommended for easy setup)
|
||||
- **Node.js 18+** and **npm** (for frontend development)
|
||||
- **Java 17+** and **Maven 3.6+** (for backend development)
|
||||
- **PostgreSQL 13+** (if running without Docker)
|
||||
- **Node.js 20+** and **npm** (for frontend development)
|
||||
- **Java 25+** and **Maven 3.6+** (for backend development)
|
||||
- **PostgreSQL 15+** (if running without Docker)
|
||||
|
||||
### Quick Start with Docker
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone https://github.com/ricardo-campos-org/react-typescript-todolist.git
|
||||
cd react-typescript-todolist
|
||||
git clone https://github.com/rmcampos/tasknote.git
|
||||
cd tasknote
|
||||
```
|
||||
|
||||
2. **Start the database**
|
||||
@@ -145,7 +131,6 @@ react-typescript-todolist/
|
||||
|
||||
5. **Access the application**
|
||||
- Frontend: http://localhost:5000
|
||||
- API Documentation: http://localhost:8080/swagger-ui.html
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
@@ -234,7 +219,7 @@ We welcome contributions from the community! This project follows the **Fork & M
|
||||
1. **Fork the Project** on GitHub
|
||||
2. **Clone your fork** locally
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/react-typescript-todolist.git
|
||||
git clone https://github.com/YOUR_USERNAME/tasknote.git
|
||||
```
|
||||
3. **Create a feature branch**
|
||||
```bash
|
||||
@@ -269,9 +254,9 @@ For detailed setup instructions and development workflows, see [CONTRIBUTING.md]
|
||||
|
||||
**Ricardo Campos** - Full-Stack Developer & Project Maintainer
|
||||
|
||||
- **GitHub**: [@ricardo-campos-org](https://github.com/ricardo-campos-org)
|
||||
- **GitHub**: [@RMCampos](https://github.com/RMCampos)
|
||||
- **Twitter/X**: [@RMCamposs](https://x.com/RMCamposs)
|
||||
- **LinkedIn**: [Ricardo Campos](https://www.linkedin.com/in/ricardo-campos-org/)
|
||||
- **LinkedIn**: [Ricardo Campos](https://www.linkedin.com/in/ricardompcampos/)
|
||||
|
||||
### About the Developer
|
||||
Ricardo is a passionate full-stack developer with expertise in modern web technologies, cloud architecture, and agile development practices. This project showcases his skills in:
|
||||
@@ -290,8 +275,8 @@ For questions, suggestions, or collaboration opportunities:
|
||||
|
||||
- **Email**: Contact via GitHub issues or discussions
|
||||
- **Twitter/X**: [@RMCamposs](https://x.com/RMCamposs) for quick questions
|
||||
- **GitHub Issues**: [Create an issue](https://github.com/ricardo-campos-org/react-typescript-todolist/issues) for bugs or feature requests
|
||||
- **GitHub Discussions**: [Join discussions](https://github.com/ricardo-campos-org/react-typescript-todolist/discussions) for general questions
|
||||
- **GitHub Issues**: [Create an issue](https://github.com/rmcampos/tasknote/issues) for bugs or feature requests
|
||||
- **GitHub Discussions**: [Join discussions](https://github.com/rmcampos/tasknote/discussions) for general questions
|
||||
|
||||
## 📄 License
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ tasks:
|
||||
|
||||
docker-build-api:
|
||||
desc: Build the tasknote-api prod-ready docker image, tagging it as candidate
|
||||
cmd: cd server && mvn -Pnative -DskipTests spring-boot:build-image -Dspring-boot.build-image.imageName=ghcr.io/rmcampos/tasknote/api:latest
|
||||
cmd: cd server && mvn -Pnative -DskipTests spring-boot:build-image -Dspring-boot.build-image.imageName=ghcr.io/rmcampos/tasknote/api:latest -Dspring-boot.build-image.builder=paketobuildpacks/builder-jammy-tiny:latest
|
||||
|
||||
prod-up-web:
|
||||
desc: Speed up the tasknote-web prod-like image, building it if required
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { describe, vi, it, expect } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { describe, vi, it, expect, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
|
||||
import ModalMarkdown from '../../components/ModalMarkdown';
|
||||
|
||||
describe('ModalMarkdown Component', () => {
|
||||
@@ -11,6 +11,14 @@ describe('ModalMarkdown Component', () => {
|
||||
onHide: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
writeText: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the modal with the correct title and markdown text', () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
@@ -36,4 +44,64 @@ describe('ModalMarkdown Component', () => {
|
||||
|
||||
expect(screen.getByTestId('modal-header-title').innerHTML).toBe('No title');
|
||||
});
|
||||
|
||||
it('should show Source and Copy buttons', () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
expect(screen.getByTestId('modal-source-button')).toBeDefined();
|
||||
expect(screen.getByTestId('modal-copy-button')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should toggle to source view when Source button is clicked', () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
expect(screen.queryByTestId('markdown-source-view')).toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByTestId('modal-source-button'));
|
||||
|
||||
expect(screen.getByTestId('markdown-source-view')).toBeDefined();
|
||||
expect(screen.getByTestId('markdown-source-view').textContent).toBe('# Test Markdown');
|
||||
});
|
||||
|
||||
it('should toggle back to rendered view when Source button is clicked again', () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('modal-source-button'));
|
||||
expect(screen.getByTestId('markdown-source-view')).toBeDefined();
|
||||
|
||||
fireEvent.click(screen.getByTestId('modal-source-button'));
|
||||
expect(screen.queryByTestId('markdown-source-view')).toBeNull();
|
||||
});
|
||||
|
||||
it('should call clipboard writeText with markdownText when Copy button is clicked', async () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('modal-copy-button'));
|
||||
});
|
||||
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('# Test Markdown');
|
||||
});
|
||||
|
||||
it('should show "Copied!" text after Copy button is clicked', async () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('modal-copy-button'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('modal-copy-button').textContent).toBe('Copied!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset source view state when modal is closed', () => {
|
||||
render(<ModalMarkdown {...props} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId('modal-source-button'));
|
||||
expect(screen.getByTestId('markdown-source-view')).toBeDefined();
|
||||
|
||||
fireEvent.click(screen.getByText('Close'));
|
||||
expect(props.onHide).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Markdown from 'react-markdown';
|
||||
@@ -23,11 +23,31 @@ type Props = {
|
||||
* @returns {React.ReactNode} the Markdown component rendered.
|
||||
*/
|
||||
const ModalMarkdown: React.FC<Props> = (props: Props): React.ReactNode => {
|
||||
const [showSource, setShowSource] = useState<boolean>(false);
|
||||
const [copied, setCopied] = useState<boolean>(false);
|
||||
|
||||
const handleToggleSource = () => setShowSource(prev => !prev);
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(props.markdownText).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}).catch(() => {
|
||||
// clipboard write failed silently; no state change
|
||||
});
|
||||
};
|
||||
|
||||
const handleHide = () => {
|
||||
setShowSource(false);
|
||||
setCopied(false);
|
||||
props.onHide();
|
||||
};
|
||||
|
||||
return props.show
|
||||
? (
|
||||
<Modal
|
||||
show={props.show}
|
||||
onHide={props.onHide}
|
||||
onHide={handleHide}
|
||||
backdrop="static"
|
||||
keyboard={false}
|
||||
size="xl"
|
||||
@@ -42,12 +62,34 @@ const ModalMarkdown: React.FC<Props> = (props: Props): React.ReactNode => {
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="markdown-modal">
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{props.markdownText}</Markdown>
|
||||
{showSource
|
||||
? (
|
||||
<pre className="markdown-source" data-testid="markdown-source-view">
|
||||
{props.markdownText}
|
||||
</pre>
|
||||
)
|
||||
: (
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{props.markdownText}</Markdown>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="outline-secondary" onClick={props.onHide}>
|
||||
<Modal.Footer className="d-flex flex-wrap gap-2 justify-content-end">
|
||||
<Button variant="outline-secondary" onClick={handleHide}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant={showSource ? 'info' : 'outline-info'}
|
||||
onClick={handleToggleSource}
|
||||
data-testid="modal-source-button"
|
||||
>
|
||||
Source
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
onClick={handleCopy}
|
||||
data-testid="modal-copy-button"
|
||||
>
|
||||
{copied ? 'Copied!' : 'Copy'}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@@ -154,3 +154,24 @@
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-source {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
padding: 1em;
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
border-radius: 6px;
|
||||
overflow: auto;
|
||||
max-height: 60vh;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.markdown-source {
|
||||
font-size: 12px;
|
||||
padding: 0.75em;
|
||||
max-height: 50vh;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-8
@@ -6,13 +6,13 @@ services:
|
||||
depends_on:
|
||||
tasknote-api:
|
||||
condition: service_healthy
|
||||
image: docker.io/rmcampos/tasknote:app-latest
|
||||
image: ghcr.io/rmcampos/tasknote/app:latest
|
||||
build:
|
||||
context: client
|
||||
context: ./client
|
||||
dockerfile: Dockerfile
|
||||
ports: ["5000:5000"]
|
||||
environment:
|
||||
VITE_BACKEND_SERVER: http://tasknote-api:8585
|
||||
VITE_BACKEND_SERVER: http://localhost:8585
|
||||
networks:
|
||||
- tasknote-network
|
||||
|
||||
@@ -33,16 +33,19 @@ services:
|
||||
SECURITY_KEY: this-is-a-very-long-security-key-for-dev
|
||||
MAILGUN_APIKEY: invalid-api-key-only-placeholder
|
||||
ports: ["8585:8585"]
|
||||
image: docker.io/rmcampos/tasknote:api-latest
|
||||
build:
|
||||
context: server
|
||||
dockerfile: Dockerfile
|
||||
image: ghcr.io/rmcampos/tasknote/api:latest
|
||||
healthcheck:
|
||||
test: ["CMD", "/layers/local_healthcheck/healthcheck/bin/healthcheck"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
networks:
|
||||
- tasknote-network
|
||||
|
||||
tasknote-db:
|
||||
container_name: tasknote-db
|
||||
image: postgres:15.8-bookworm
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_DB: tasknote
|
||||
POSTGRES_USER: tasknoteuser
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build check.go
|
||||
chmod +x check
|
||||
mv check buildpacks/healthcheck/healthcheck
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
LAYERS_DIR="$1"
|
||||
|
||||
echo "CNB_BUILDPACK_DIR: ${CNB_BUILDPACK_DIR}"
|
||||
echo "Contents of buildpack dir:"
|
||||
ls -la "${CNB_BUILDPACK_DIR}"
|
||||
|
||||
BINARY_SOURCE="${CNB_BUILDPACK_DIR}/healthcheck"
|
||||
|
||||
if [ ! -f "${BINARY_SOURCE}" ]; then
|
||||
echo "ERROR: binary not found at ${BINARY_SOURCE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LAYER_DIR="${LAYERS_DIR}/healthcheck"
|
||||
mkdir -p "${LAYER_DIR}/bin"
|
||||
|
||||
cp "${BINARY_SOURCE}" "${LAYER_DIR}/bin/healthcheck"
|
||||
chmod +x "${LAYER_DIR}/bin/healthcheck"
|
||||
|
||||
cat > "${LAYER_DIR}.toml" <<EOL
|
||||
[types]
|
||||
launch = true
|
||||
build = false
|
||||
cache = false
|
||||
EOL
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
@@ -0,0 +1,11 @@
|
||||
api = "0.7"
|
||||
|
||||
[buildpack]
|
||||
id = "local/healthcheck"
|
||||
version = "0.1.0"
|
||||
name = "Healthcheck Binary Buildpack"
|
||||
[[stacks]]
|
||||
id = "io.buildpacks.stacks.jammy"
|
||||
|
||||
[[stacks]]
|
||||
id = "*"
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
// Use 127.0.0.1 as the healthcheck runs inside the container
|
||||
resp, err := client.Get("http://127.0.0.1:8585/health")
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
+18
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>br.com.tasknoteapp</groupId>
|
||||
<artifactId>server</artifactId>
|
||||
<version>8</version>
|
||||
<version>9</version>
|
||||
<name>tasknote-api</name>
|
||||
<description>Java backend REST API to serve TaskNote frontend client</description>
|
||||
|
||||
@@ -113,6 +113,14 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-flyway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
@@ -351,6 +359,15 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<name>ghcr.io/rmcampos/tasknote/api:latest</name>
|
||||
<buildpacks>
|
||||
<buildpack>file://${project.basedir}/buildpacks/healthcheck</buildpack>
|
||||
<buildpack>urn:cnb:builder:paketo-buildpacks/java-native-image</buildpack>
|
||||
</buildpacks>
|
||||
</image>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[build]
|
||||
builder = "paketobuildpacks/builder-jammy-tiny:latest"
|
||||
|
||||
[[build.buildpacks]]
|
||||
uri = "buildpacks/healthcheck"
|
||||
|
||||
[[build.buildpacks]]
|
||||
uri = "urn:cnb:builder:paketo-buildpacks/java-native-image"
|
||||
Reference in New Issue
Block a user