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
170 changes: 170 additions & 0 deletions .github/workflows/build-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Frontend - Build and Push

on:
pull_request:
branches:
- pre
- main
push:
branches:
- pre
workflow_dispatch:
inputs:
publish_pre:
description: "Build and push the PRE image to ECR"
required: true
default: false
type: boolean

permissions:
id-token: write
contents: read

env:
AWS_REGION: eu-west-1
ECR_REPOSITORY_PRE: gesplan-pre/vestimenta-frontend
NODE_VERSION: "20"
DOCKERFILE_PATH: Dockerfile

jobs:
validate:
name: Validate frontend build
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Validate repository structure
shell: bash
run: |
test -f package.json
test -f angular.json
test -d src
test -f "${DOCKERFILE_PATH}"

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Validate npm scripts
shell: bash
run: |
npm run | grep -qE '^ build$'
if npm run | grep -qE '^ lint$'; then
echo "HAS_LINT=true" >> "$GITHUB_ENV"
else
echo "HAS_LINT=false" >> "$GITHUB_ENV"
fi
if npm run | grep -qE '^ test$'; then
echo "HAS_TEST=true" >> "$GITHUB_ENV"
else
echo "HAS_TEST=false" >> "$GITHUB_ENV"
fi

- name: Build Angular application
run: npm run build -- --configuration production

- name: Run lint
if: env.HAS_LINT == 'true'
run: npm run lint

- name: Run tests
if: env.HAS_TEST == 'true'
run: npm test -- --watch=false --browsers=ChromeHeadless

- name: Build Docker image
run: docker build -f "${DOCKERFILE_PATH}" -t vestimenta-frontend:ci .

- name: Workflow summary
shell: bash
run: |
{
echo "### Frontend CI validation"
echo ""
echo "- Angular build validated."
echo "- Docker image build validated with \`${DOCKERFILE_PATH}\`."
echo "- No ECR login or image push was executed."
echo "- No Helm, kubectl, EKS deploy, Terraform apply or destroy was executed."
} >> "$GITHUB_STEP_SUMMARY"

build-and-push-pre:
name: Build and push PRE image
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.publish_pre == true)
outputs:
image_tag: ${{ steps.image.outputs.image_tag }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Validate repository structure
shell: bash
run: |
test -f package.json
test -f angular.json
test -d src
test -f "${DOCKERFILE_PATH}"

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Validate npm build script
run: npm run | grep -qE '^ build$'

- name: Build Angular application
run: npm run build -- --configuration production

- name: Configure AWS credentials PRE
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_PRE }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push Docker image
id: image
shell: bash
run: |
IMAGE_TAG="sha-${GITHUB_SHA}"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
IMAGE="${ECR_REGISTRY}/${ECR_REPOSITORY_PRE}:${IMAGE_TAG}"

docker build -f "${DOCKERFILE_PATH}" -t "${IMAGE}" .
docker push "${IMAGE}"

echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV"
echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"

- name: Workflow summary
shell: bash
run: |
{
echo "### Frontend image published"
echo ""
echo "- Image: \`${IMAGE}\`"
echo "- Tag: \`${IMAGE_TAG}\`"
echo "- Repository: \`${ECR_REPOSITORY_PRE}\`"
echo "- Region: \`${AWS_REGION}\`"
echo ""
echo "Imagen publicada en ECR. Para desplegar, actualizar el tag correspondiente en infra-apps y ejecutar el workflow de despliegue desde infra-apps."
echo ""
echo "No se ha ejecutado Helm, kubectl, despliegue en EKS, Terraform apply ni Terraform destroy desde este repositorio."
} >> "$GITHUB_STEP_SUMMARY"
99 changes: 91 additions & 8 deletions docs/appVestimenta/pre-frontend.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,87 @@
# appVestimenta PRE — Frontend Angular

## CI/CD

El repositorio `appVestimenta` construye y publica la imagen Docker del frontend, pero no despliega en EKS.

Workflow:

`.github/workflows/build-frontend.yml`

Nombre:

`Frontend - Build and Push`

Modelo operativo:

- `pull_request` hacia `pre` o `main`: valida Angular, tests si existen y build Docker sin publicar imagen.
- `push` a `pre`: compila, construye la imagen y la publica en ECR PRE.
- `workflow_dispatch`: valida manualmente y permite publicar PRE si `publish_pre=true`.
- El despliegue Helm/Kubernetes se realiza desde `infra-apps`.

El workflow no ejecuta `helm`, `kubectl`, Terraform ni despliegues directos a EKS.

## Imagen

ECR:
ECR PRE:

`674624358677.dkr.ecr.eu-west-1.amazonaws.com/gesplan-pre/vestimenta-frontend`

Repositorio lógico:

`gesplan-pre/vestimenta-frontend`

Tag obligatorio:

`sha-<commit_sha>`

Ejemplo:

`674624358677.dkr.ecr.eu-west-1.amazonaws.com/gesplan-pre/vestimenta-frontend:sha-8f8d02b...`

No usar `latest` como tag de despliegue. El tag que debe consumir `infra-apps` es siempre el `sha-<commit_sha>` generado por el workflow.

## Autenticación AWS

La autenticación con AWS se hace mediante GitHub Actions OIDC.

`674624358677.dkr.ecr.eu-west-1.amazonaws.com/gesplan-pre/vestimenta-frontend:pre`
Secret requerido:

`AWS_ROLE_ARN_PRE`

No configurar `AWS_ACCESS_KEY_ID` ni `AWS_SECRET_ACCESS_KEY` en este repositorio.

## Build frontend

Proyecto Angular detectado por:

- `package.json`
- `angular.json`
- `src/`

Dockerfile:

`Dockerfile`

Scripts npm detectados:

- `build`: `ng build`
- `test`: `ng test`

No hay script `lint` definido actualmente.

## API

La configuración objetivo para consumir el backend es usar ruta relativa:

`/api`

Estado actual detectado:

- `src/app/usuarios/usuarios.component.ts` usa `https://my-cakephp-site.ddev.site/api/v1/users`.
- `src/app/usuarios/usuarios.component.ts` usa `https://my-cakephp-site.ddev.site/sse?debug=1`.

Pendiente técnico: mover esas URLs a ruta relativa o configuración runtime, sin introducir secretos ni dominios PRO en el build.

## Namespace

Expand All @@ -22,15 +99,21 @@ Puerto:

`80`

## Validaciones
## Validaciones post-despliegue

Estas validaciones aplican después de desplegar desde `infra-apps`, no desde el workflow de `appVestimenta`.

```powershell
kubectl get pods -n gesplan-pre-vestimenta
kubectl port-forward svc/vestimenta-frontend 8081:80 -n gesplan-pre-vestimenta
curl.exe -I http://localhost:8081/
curl.exe -I http://localhost:8081/health
Pendiente
Integrar con backend mediante Ingress.
Activar OAuth2 Proxy específico de Vestimenta.
Crear Ingress definitivo.
Crear DNS funcional vestimenta-pre.gesplan.es.
```

## Pendiente

- Actualizar `image.tag` en `infra-apps` con el tag `sha-<commit_sha>` generado.
- Integrar con backend mediante Ingress.
- Activar OAuth2 Proxy específico de Vestimenta.
- Crear Ingress definitivo.
- Crear DNS funcional `vestimenta-pre.gesplan.es`.
7 changes: 0 additions & 7 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,4 @@ describe('AppComponent', () => {
const app = fixture.componentInstance;
expect(app.title).toEqual('angular19');
});

it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, angular19');
});
});
8 changes: 7 additions & 1 deletion src/app/inicio/configurar/configurar.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';

import { ConfigurarComponent } from './configurar.component';

Expand All @@ -8,7 +10,11 @@ describe('ConfigurarComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ConfigurarComponent]
imports: [ConfigurarComponent],
providers: [
provideHttpClient(),
provideHttpClientTesting()
]
})
.compileComponents();

Expand Down
8 changes: 4 additions & 4 deletions src/app/services/sse.service.ts.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';

import { SseServiceTsService } from './sse.service.ts.service';
import { SseService } from './sse.service.ts.service';

describe('SseServiceTsService', () => {
let service: SseServiceTsService;
describe('SseService', () => {
let service: SseService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SseServiceTsService);
service = TestBed.inject(SseService);
});

it('should be created', () => {
Expand Down
8 changes: 7 additions & 1 deletion src/app/usuarios/usuarios.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';

import { UsuariosComponent } from './usuarios.component';

Expand All @@ -8,7 +10,11 @@ describe('UsuariosComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UsuariosComponent]
imports: [UsuariosComponent],
providers: [
provideHttpClient(),
provideHttpClientTesting()
]
})
.compileComponents();

Expand Down
Loading