Skip to content

Commit a5c1a7f

Browse files
committed
Merge remote-tracking branch 'origin/ecospheres-demo' into ecospheres-prod-major-merge
2 parents 93c49fb + 1de27eb commit a5c1a7f

File tree

119 files changed

+3812
-1390
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+3812
-1390
lines changed

.github/workflows/create-deploy-release.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ jobs:
103103
shell: bash
104104
run: |
105105
git clone --quiet --depth 1 -b ${{ env.REPO_CURRENT_BRANCH }} ${{ env.REPO_SSH_URL }}
106-
git clone --quiet --depth 1 -b pnpm-ufk $SCAFFOLD_REPO_SSH_URL ${{ env.SCAFFOLD_DIR }}
106+
git clone --quiet --depth 1 $SCAFFOLD_REPO_SSH_URL ${{ env.SCAFFOLD_DIR }}
107107
108108
- name: Parse commit message and set deployment variables
109109
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/heads/') && startsWith(github.event.head_commit.message, '['))
@@ -145,7 +145,9 @@ jobs:
145145
pattern="${CONFIG_NAME}-${ENV}-[0-9]+\.[0-9]+\.[0-9]+$"
146146
tags=$(git ls-remote --tags origin | grep -Eo "refs/tags/${pattern}" | sed 's#refs/tags/##' || true)
147147
if [ -n "$tags" ]; then
148-
for tag in $tags; do git fetch origin tag $tag; done
148+
# --no-tags prevents git from auto-following related tags,
149+
# which would cause "tag already exists" errors on subsequent fetches
150+
for tag in $tags; do git fetch --no-tags origin tag $tag; done
149151
fi
150152
RELEASE_VERSION=$(../${{ env.SCAFFOLD_DIR }}/scripts/bump_version_non_semver.sh $CONFIG_NAME $ENV $VERSION_TYPE)
151153

.github/workflows/review-app.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.site }}
3333
cancel-in-progress: false
3434
strategy:
35+
max-parallel: 2
3536
matrix:
3637
site:
3738
- ecospheres

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ node-linker=hoisted
1111
# Security: Only install packages released at least 4 days ago
1212
# Protects against supply-chain attacks by giving community time to detect malicious releases
1313
minimum-release-age=5760
14+
minimum-release-age-exclude[]=@ecolabdata/*
1415

1516
# Enforce pnpm usage - fail if npm or yarn is used
1617
engine-strict=true

README.md

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,78 @@ Les **review apps** ne sont **pas créées automatiquement** lors de l'ouverture
169169

170170
### 🏭 Déploiement en preprod et en production
171171

172-
Le déploiement des verticales thématiques en preprod et en production s'effectue via un workflow GitHub qui peut être déclenché de deux manières différentes :
173-
174172
#### Comment déployer en préproduction et en production
175173

176-
## Solution 1 - par le message de Git commit
174+
##### Solution 1 : Script de déploiement local (recommandé)
175+
176+
Un script bash `scripts/deploy.sh` simplifie le processus de déploiement en deux étapes.
177+
178+
**Prérequis :** [GitHub CLI (`gh`)](https://cli.github.com/) et `jq` installés, `gh` authentifié.
179+
180+
###### Étape 1 : Préparer le déploiement
181+
182+
Crée une branche de merge temporaire, fusionne les changements (avec résolution de conflits si nécessaire), et crée une PR draft :
183+
184+
```bash
185+
# Pour demo/preprod (source = main par défaut)
186+
./scripts/deploy.sh prepare <site> <env> <version>
187+
188+
# Pour prod (--source obligatoire)
189+
./scripts/deploy.sh prepare <site> prod <version> --source <site>-preprod
190+
```
191+
192+
**Exemples :**
193+
194+
```bash
195+
./scripts/deploy.sh prepare ecospheres demo minor
196+
./scripts/deploy.sh prepare ecospheres prod minor --source ecospheres-demo
197+
```
198+
199+
Le script :
200+
201+
- Crée une branche `{site}-{env}-merge` depuis `{site}-{env}`
202+
- Fusionne la branche source dans cette branche
203+
- En cas de conflits, vous les résolvez localement puis relancez la commande
204+
- Pousse la branche et crée une PR draft
205+
- Poste un commentaire `/deploy {site}` pour créer une review app
206+
207+
###### Étape 2 : Déployer
208+
209+
Après validation et approbation de la PR, lancez le déploiement avec l'URL ou le numéro de la PR :
210+
211+
```bash
212+
./scripts/deploy.sh deploy <pr>
213+
214+
# Exemples
215+
./scripts/deploy.sh deploy 123
216+
./scripts/deploy.sh deploy https://github.com/opendatateam/udata-front-kit/pull/123
217+
```
218+
219+
Le script :
220+
221+
- Vérifie que la PR est ouverte et approuvée
222+
- Merge la PR avec le message normalisé `[{env}:{site}:{version}] {titre_pr} #{numéro_pr}`
223+
- Supprime la branche de merge
224+
- Pour les déploiements **prod** : crée une release GitHub avec changelog auto-généré
225+
- Déclenche automatiquement le pipeline GitLab
226+
227+
**Arguments :**
228+
229+
| Argument | Valeurs possibles |
230+
| ----------- | ------------------------------------------------------ |
231+
| `<site>` | Déterminé dynamiquement depuis les dossiers `configs/` |
232+
| `<env>` | `demo`, `preprod`, `prod` |
233+
| `<version>` | `major`, `minor`, `patch` |
234+
235+
**Options :**
236+
237+
| Option | Description |
238+
| -------------------- | ------------------------------------------------------------------------- |
239+
| `--source <branch>` | Branche source (obligatoire pour prod, défaut : `main` pour demo/preprod) |
240+
| `--ignore-git-clean` | Ignore la vérification de l'état git (utile pour les tests) |
241+
| `--skip-release` | Ne pas créer de release GitHub (prod uniquement) |
242+
243+
##### Solution 2 : Message de commit
177244

178245
Le déploiement des verticales thématiques en preprod et en production peut s'effectuer via un workflow GitHub qui se déclenche automatiquement à partir du message de commit. Le format du message de commit doit être :
179246

@@ -204,9 +271,9 @@ Le workflow se déclenche sur tous les push vers toutes les branches, mais ne s'
204271

205272
Toutes les variables et secrets nécessaires pour ce workflow sont listés dans la section `env:` du [workflow de déploiement](.github/workflows/create-deploy-release.yml).
206273

207-
## Solution 2 — sur l'interface web de GitHub Actions
274+
##### Solution 3 : Interface GitHub Actions
208275

209-
Le déploiement peut également être déclenché manuellement via l'interface GitHub Actions :
276+
Le déploiement peut être déclenché manuellement via l'interface GitHub Actions :
210277

211278
1. **Aller dans l'onglet "Actions"** du dépôt GitHub
212279
2. **Sélectionner "Deployment on datagouv domains with version bump"** dans la liste des workflows

configs/ecospheres/config.yaml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,16 @@ pages:
379379
name: Zones de gestion, de restriction ou de réglementation et unités de déclaration
380380
- id: zones-a-risque-naturel
381381
name: Zones à risque naturel
382+
- name: Label de données
383+
default_option: Tous les labels
384+
id: badge
385+
type: select
386+
use_filter_prefix: false
387+
values:
388+
- id: hvd
389+
name: Données de forte valeur
390+
- id: inspire
391+
name: INSPIRE
382392
# DATASERVICES
383393
dataservices:
384394
list_all: true
@@ -458,10 +468,6 @@ pages:
458468
form:
459469
required: true
460470
values: *fnv_themes
461-
- name: Couverture territoriale
462-
default_option:
463-
id: geozone
464-
type: spatial_zone
465471
# INDICATORS
466472
indicators:
467473
list_all: true

configs/meteo-france/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ website:
6969
description: 'Archive des observations pour différentes sources de données sol, mer et altitude'
7070
url: 'datasets?category=observations'
7171
image_url: '/static/meteo-france/assets/observations.png'
72+
- name: 'Bulletins météorologiques'
73+
description: 'Archive des bulletins météorologiques pour différentes sources de données : forêt, marine, montagne, climatologie ...'
74+
url: 'datasets?category=bulletins'
75+
image_url: '/static/meteo-france/assets/bulletins.png'
7276
sub_section_tiles:
7377
title:
7478
tiles:
@@ -223,6 +227,8 @@ pages:
223227
name: Fiches climatologiques
224228
- id: observations
225229
name: Données d'observations météorologiques
230+
- id: bulletins
231+
name: Archive des bulletins météorologiques
226232
- name: Sous-catégories
227233
default_option: Toutes les sous-catégories
228234
id: subcategory

cypress/e2e/custom/ecospheres/a11y/contrast.cy.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { DataserviceWithRel } from '@/model/dataservice'
21
import type { Topic } from '@/model/topic'
3-
import type { DatasetV2 } from '@datagouv/components-next'
2+
import type { Dataservice, DatasetV2 } from '@datagouv/components-next'
43
import { dataserviceFactory } from 'cypress/support/factories/dataservices_factory'
54
import { datasetFactory } from 'cypress/support/factories/datasets_factory'
65
import { topicFactory } from 'cypress/support/factories/topics_factory'
@@ -9,7 +8,7 @@ import { mockUniverseOrganizations } from '../mocks'
98
describe('a11y contrast testing', () => {
109
let testTopics: Topic[]
1110
let testDatasets: DatasetV2[]
12-
let testDataservices: DataserviceWithRel[]
11+
let testDataservices: Dataservice[]
1312

1413
beforeEach(() => {
1514
// global mocks

cypress/e2e/custom/ecospheres/dataservices/detail.cy.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import type { DataserviceWithRel } from '@/model/dataservice'
2-
import type { DatasetV2 } from '@datagouv/components-next'
1+
import type { Dataservice, DatasetV2 } from '@datagouv/components-next'
32
import { dataserviceFactory } from '../../../../support/factories/dataservices_factory'
43
import { datasetFactory } from '../../../../support/factories/datasets_factory'
4+
import { createIndicator } from '../indicators/support'
55

66
/**
77
* Helper to create a dataservice with all necessary mocks
88
*/
99
function createMockedDataservice(
10-
overrides: Partial<DataserviceWithRel> = {},
10+
overrides: Partial<Dataservice> = {},
1111
datasets: DatasetV2[] = []
12-
): DataserviceWithRel {
12+
): Dataservice {
1313
const dataservice = dataserviceFactory.one({ overrides })
1414

1515
// Mock the dataservice itself
@@ -32,7 +32,7 @@ function createMockedDataservice(
3232
}
3333

3434
describe('Dataservices (API) - Detail Page', () => {
35-
let testDataservice: DataserviceWithRel
35+
let testDataservice: Dataservice
3636
let testDatasets: DatasetV2[]
3737

3838
beforeEach(() => {
@@ -215,7 +215,9 @@ describe('Dataservices (API) - Detail Page', () => {
215215

216216
// Discussions tab content should be visible
217217
cy.get('#tab-content-1').should('be.visible')
218-
cy.contains('Pas de discussion pour cette API').should('be.visible')
218+
cy.contains("Il n'y a pas encore de discussion pour cette API").should(
219+
'be.visible'
220+
)
219221
})
220222

221223
it('should switch to informations tab when clicked', () => {
@@ -278,4 +280,39 @@ describe('Dataservices (API) - Detail Page', () => {
278280
cy.contains('0 jeu de données').should('be.visible')
279281
})
280282
})
283+
284+
describe('Indicator datasets', () => {
285+
it('should display indicator datasets with the Indicateur badge', () => {
286+
// Create a mix of regular datasets and indicator datasets
287+
const regularDataset = datasetFactory.one({
288+
overrides: { title: 'Regular Dataset Title' }
289+
})
290+
const indicatorDataset = createIndicator({
291+
title: 'Indicator Dataset Title'
292+
})
293+
294+
const ds = createMockedDataservice({}, [regularDataset, indicatorDataset])
295+
296+
cy.visit(`/dataservices/${ds.id}`)
297+
cy.wait(`@get_dataservices_${ds.id}`)
298+
cy.wait('@getDataserviceDatasets')
299+
300+
// Check that both datasets are displayed
301+
cy.contains('Regular Dataset Title').should('be.visible')
302+
cy.contains('Indicator Dataset Title').should('be.visible')
303+
304+
// Check that the indicator dataset has the "Indicateur" badge
305+
cy.contains('Indicator Dataset Title')
306+
.closest('.fr-col-12')
307+
.find('.fr-badge')
308+
.contains('Indicateur')
309+
.should('be.visible')
310+
311+
// Check that the regular dataset does NOT have the "Indicateur" badge
312+
cy.contains('Regular Dataset Title')
313+
.closest('.fr-col-12')
314+
.find('.fr-badge')
315+
.should('not.exist')
316+
})
317+
})
281318
})

cypress/e2e/custom/ecospheres/dataservices/list.cy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { DataserviceWithRel } from '@/model/dataservice'
1+
import type { Dataservice } from '@datagouv/components-next'
22
import { dataserviceFactory } from '../../../../support/factories/dataservices_factory'
33
import { mockUniverseOrganizations } from '../mocks'
44

55
describe('Dataservices (API) - List Page', () => {
6-
let testDataservices: DataserviceWithRel[]
6+
let testDataservices: Dataservice[]
77

88
beforeEach(() => {
99
cy.mockMatomo()

cypress/e2e/custom/ecospheres/indicators/detail.cy.ts

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { Indicator } from '@/custom/ecospheres/model/indicator'
2+
import type { Dataservice, Reuse } from '@datagouv/components-next'
3+
import { dataserviceFactory } from 'cypress/support/factories/dataservices_factory'
4+
import { reuseFactory } from 'cypress/support/factories/reuses_factory'
25
import { createIndicator, createIndicatorResource } from './support'
36

47
describe('Indicator Detail View', () => {
@@ -56,25 +59,6 @@ describe('Indicator Detail View', () => {
5659
})
5760
})
5861

59-
describe('Fichiers et API Tab', () => {
60-
beforeEach(() => {
61-
cy.visit(`/indicators/${indicator.id}`)
62-
cy.contains('Fichiers et API').click()
63-
})
64-
65-
it('should display axis annee values', () => {
66-
cy.contains('2020').should('be.visible')
67-
cy.contains('2021').should('be.visible')
68-
cy.contains('2022').should('be.visible')
69-
})
70-
71-
it('should display axis secteur values', () => {
72-
cy.contains('transport').should('be.visible')
73-
cy.contains('energie').should('be.visible')
74-
cy.contains('agriculture').should('be.visible')
75-
})
76-
})
77-
7862
describe('Custom Metadata In Technical Details Tab', () => {
7963
beforeEach(() => {
8064
cy.visit(`/indicators/${indicator.id}`)
@@ -116,6 +100,51 @@ describe('Indicator Detail View', () => {
116100
})
117101
})
118102

103+
describe('Réutilisations et API Tab', () => {
104+
it('should display dataservices when available', () => {
105+
const dataservices = dataserviceFactory.many<Dataservice>(2)
106+
const indicatorWithApis = createIndicator(
107+
{},
108+
{ enable_visualization: false }
109+
)
110+
cy.mockDatasetAndRelatedObjects(indicatorWithApis, [], dataservices)
111+
cy.visit(`/indicators/${indicatorWithApis.id}`)
112+
cy.contains('Réutilisations et API').click()
113+
cy.contains(`${dataservices.length} API`).should('be.visible')
114+
cy.contains(dataservices[0].title).should('be.visible')
115+
cy.contains(dataservices[1].title).should('be.visible')
116+
})
117+
118+
it('should display empty state when no dataservices', () => {
119+
cy.visit(`/indicators/${indicator.id}`)
120+
cy.contains('Réutilisations et API').click()
121+
cy.contains("Il n'y a pas encore d'API pour cet indicateur.").should(
122+
'be.visible'
123+
)
124+
})
125+
126+
it('should display reuses when available', () => {
127+
const reuses = reuseFactory.many<Reuse>(2)
128+
const indicatorWithReuses = createIndicator(
129+
{},
130+
{ enable_visualization: false }
131+
)
132+
cy.mockDatasetAndRelatedObjects(indicatorWithReuses, [], [], reuses)
133+
cy.visit(`/indicators/${indicatorWithReuses.id}`)
134+
cy.contains('Réutilisations et API').click()
135+
cy.contains(reuses[0].title).should('be.visible')
136+
cy.contains(reuses[1].title).should('be.visible')
137+
})
138+
139+
it('should display empty state when no reuses', () => {
140+
cy.visit(`/indicators/${indicator.id}`)
141+
cy.contains('Réutilisations et API').click()
142+
cy.contains(
143+
"Il n'y a pas encore de réutilisation pour cet indicateur."
144+
).should('be.visible')
145+
})
146+
})
147+
119148
describe('No Datavisualisation Configured', () => {
120149
it('should not display the previsualisation', () => {
121150
cy.visit(`/indicators/${indicator.id}`)

0 commit comments

Comments
 (0)