Skip to content

Commit

Permalink
fix: deploy fail when validate markdown (#948)
Browse files Browse the repository at this point in the history
  • Loading branch information
fpasquet committed Jun 21, 2023
1 parent e92286a commit 6bfaec3
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 110 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Validate markdown authors and posts
id: validate_markdown
run: yarn validate-markdown
env:
NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -137,7 +138,7 @@ jobs:

- name: Update deployment status
uses: bobheadxi/deployments@v1
if: always()
if: success()
with:
step: finish
token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
12 changes: 6 additions & 6 deletions _posts/fr/2023-06-07-agile-travail-distance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
lang: fr
date: '2023-06-07'
slug: agile-travail-distance
title: 'Optimisez votre travail à distance : Conseils pour une équipe agile 100% remote'
title: >-
'Optimisez votre travail à distance : Conseils pour une équipe agile 100% remote'
excerpt: Retours d’expérience de gestion de projet agile en télétravail avec une équipe de production importante et plusieurs clients répartis dans toute la France. Ce rapport présente les défis rencontrés ainsi que des conseils pour améliorer la communication et la transparence dans votre projet.
categories:
- agile
keywords:
- agile
- travail
- distance
- remote
Expand All @@ -24,7 +24,7 @@ De plus en plus d'entreprises optent pour le travail à distance depuis la péri

Pendant ces 3 ans de projets, nous avons mis en place des méthodes pour améliorer les processus de travail à distance. Voici ce que nous avons réalisé dans les deux domaines clés impactés par la distance des équipes : la collaboration et la transparence.

**Collaboration entre l’équipe et les parties prenantes**
**Collaboration entre l’équipe et les parties prenantes**
Au cours de cette expérience, nous avons constaté qu'en raison du télétravail et de la situation où chaque partie prenante se trouvait dans des bureaux séparés, la communication avec l'équipe de production (PO, développeurs, UX) était difficile.

En effet, étant donné que chacun gérait sa propre entreprise, le projet était perçu comme une utilisation de temps qui empiétait sur la gestion de leurs activités individuelles.
Expand All @@ -43,7 +43,7 @@ Pour la communication externe, toutes les réunions avec les parties prenantes s

Pour finir, afin de maintenir la motivation de nos clients lors des démonstrations nous avons travaillé pour rendre les sessions **interactives et engageantes**. On communiquait en amont sur les fonctionnalités qui allaient être présentées afin que seuls les clients concernés assistent à la démonstration.

**Collaboration entre les membres de l’équipe de production**
**Collaboration entre les membres de l’équipe de production**
En ce qui concerne l'équipe de production, nous étions composés de plusieurs profils différents (PPO, UX, PO, PM, développeurs) chacun ayant sa propre façon de travailler. Cependant, en raison du nombre élevé de membres, certaines informations se perdaient lors des discussions, ce qui entraînait des réponses répétitives aux questions et une confusion dans les processus de communication, rendant le travail plus complexe.

Afin de simplifier la communication entre tous les membres et de faciliter la collaboration, nous avons mis en place les actions suivantes :
Expand All @@ -58,7 +58,7 @@ Afin de simplifier la communication entre tous les membres et de faciliter la co

- La mise en place d'un système de **mentorat** pour aider les nouveaux membres de l'équipe à s'adapter au travail à distance et à s'intégrer rapidement. Nous avons organisé des séances de coaching régulières pour répondre aux questions et résoudre les éventuels blocages.

**Transparence entre l’équipe et les parties prenantes**
**Transparence entre l’équipe et les parties prenantes**
Au cours de ce projet, nous avons identifié un véritable manque de transparence entre l'équipe de production et les parties prenantes. Ce manque s'est principalement manifesté par une incompréhension de l'intérêt commercial du produit de la part de l'équipe de production, ainsi qu'un manque de retour sur les utilisations réelles des clients. Du côté des parties prenantes, il s'agissait de l'un de leurs premiers projets numériques d'une telle envergure, et nous avons constaté une méconnaissance des difficultés techniques de certaines fonctionnalités, ce qui a entraîné une incompréhension du temps nécessaire pour répondre aux demandes exprimées.

Afin d'améliorer ces aspects, nous avons mis en œuvre les actions suivantes :
Expand All @@ -71,7 +71,7 @@ Afin d'améliorer ces aspects, nous avons mis en œuvre les actions suivantes :

- L’établissement d’une méthode de **suivi hebdomadaire pour la partie commerciale**, en identifiant les blocages et en informant efficacement l'équipe des avancements et des défis à relever.

**Transparence entre les membres de l’équipe de développement**
**Transparence entre les membres de l’équipe de développement**
Enfin, nous avons constaté que de nombreuses informations et outils n'étaient pas utilisés par l'équipe, ce qui entraînait des demandes répétées d'informations et une perte de temps dans les activités opérationnelles. De plus, en travaillant de manière isolée, certaines actions étaient réalisées en double, ce qui n'était pas optimal pour les processus de travail. Afin de garantir l'accès à des informations pertinentes pour tous et de simplifier les processus, nous avons mis en place les mesures suivantes :

- Communication **transparente sur l'objectif du produit,** son avancement et les retours utilisateurs en intégrant toutes les informations dans l'outil de gestion de projet et en organisant des communications régulières avec l'équipe.
Expand Down
15 changes: 15 additions & 0 deletions bin/binHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const getArgs = <TArgs = { [p: string]: boolean | string | number }>(): TArgs => {
const args = process.argv.slice(2);
const formattedArgs: { [key: string]: boolean | string | number } = {};

for (let i = 0; i < args.length; i++) {
const arg = args[i];

if (arg.startsWith('--')) {
const [key, value] = arg.substring(2).split('=');
formattedArgs[key] = value ? value : true;
}
}

return formattedArgs as TArgs;
};
21 changes: 18 additions & 3 deletions bin/validateMarkdown.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import { createServer as createViteServer } from 'vite';

import { MarkdownInvalidError } from '../src/helpers/markdownHelper';
import { getArgs } from './binHelper';

(async (): Promise<void> => {
const args = getArgs<{ ci: boolean }>();

const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
});

try {
const { validateMarkdown } = await vite.ssrLoadModule('/src/helpers/validateMarkdownHelper.ts');
const { validateMarkdown } = await vite.ssrLoadModule('/src/helpers/markdownHelper.ts');
validateMarkdown();
vite.close();
} catch (e) {
console.error(e);
} finally {
vite.close();

const markdownInvalidError = e as MarkdownInvalidError;
if (args.ci) {
console.log(`::set-output name=filePath::${markdownInvalidError.markdownFilePathRelative}`);
console.log(`::set-output name=reason::${markdownInvalidError.reason}`);
console.log(`::set-output name=line::${markdownInvalidError.line}`);
console.log(`::set-output name=column::${markdownInvalidError.column}`);
process.exit(1);
}

console.error(markdownInvalidError);
}
})();
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"devDependencies": {
"@babel/core": "^7.20.12",
"@elevenlabs/eslint-config": "^0.0.1",
"@octokit/core": "^4.2.1",
"@storybook/addon-actions": "^7.0.2",
"@storybook/addon-essentials": "^7.0.2",
"@storybook/addon-interactions": "^7.0.2",
Expand All @@ -66,6 +67,7 @@
"@testing-library/react": "^14.0.0",
"@types/express": "^4.17.17",
"@types/i18next": "^13.0.0",
"@types/js-yaml": "^4.0.5",
"@types/markdown-it": "^12.2.3",
"@types/node": "^18.13.0",
"@types/react": "^18.0.28",
Expand All @@ -92,7 +94,7 @@
"rehype-raw": "^6.1.1",
"rehype-react": "^7.1.2",
"rehype-rewrite": "^3.0.6",
"remark-parse": "^10.0.1",
"remark-parse": "^10.0.2",
"remark-rehype": "^10.1.0",
"rollup-plugin-visualizer": "^5.9.0",
"sanitize-html": "^2.10.0",
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/contentHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path, { dirname, resolve } from 'node:path';
import { ASSETS_DIR, DATA_DIR, MARKDOWN_FILE_PATHS } from '@/app-paths';
import { CATEGORIES } from '@/constants';
import { getPathFile } from '@/helpers/assetHelper';
import { markdownToHtml } from '@/helpers/markdownHelper';
import { markdownToHtml } from '@/helpers/markdownToHtmlHelper';
import { intersection } from '@/helpers/objectHelper';
import {
AuthorData,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
import * as glob from 'glob';
import { vi } from 'vitest';
import { z } from 'zod';

import { validateAuthor, validateMarkdown, validatePost } from './validateMarkdownHelper';
import { frontmatter, getDataInMarkdownFile, validateAuthor, validateMarkdown, validatePost } from './markdownHelper';

vi.mock('node:fs', () => ({
readFileSync: vi.fn().mockImplementation((path: string) => {
switch (path) {
case '/path/to/dir/valid-file.md':
return `---
title: Example Title
slug: example-title
---
This is the content`;
case '/path/to/dir/invalid-file-with-markdown-contains-disallowed-syntax.md':
return `---
title: Example Title
date: 2023-06-13
---
This is the content with a disallowed syntax {:{key}}`;
case '/path/to/dir/invalid-file-with-validation-schema.md':
return `---
title: Example Title
date: invalid-date
---
This is the content`;
case '/path/to/dir/invalid-file-syntax.md':
return `---
title: Example Title
slug: example-title
description: ->
'lorem ipsum'
---
This is the content`;
case '/path/to/dir/invalid-author.md':
return `---
username: jdoe
Expand Down Expand Up @@ -124,11 +154,91 @@ This is some valid content`;
}));
vi.mock('glob');

describe('frontmatter', () => {
it('should extract frontmatter data and remove it from the content', () => {
const content = `---
title: Example Title
slug: example-title
---
This is the content`;

const expectedResult = {
data: {
title: 'Example Title',
slug: 'example-title',
},
content: 'This is the content',
};

const result = frontmatter(content);
expect(result).toMatchObject(expectedResult);
});
});

describe('getDataInMarkdownFile', () => {
const ValidationSchema = z.object({
title: z.string(),
slug: z.string(),
});

it('should parse markdown file and validate frontmatter data', () => {
const result = getDataInMarkdownFile({
ValidationSchema,
markdownFilePath: '/path/to/dir/valid-file.md',
});

expect(result).toMatchObject({
title: 'Example Title',
slug: 'example-title',
content: 'This is the content',
});
});

it('should throw an error if markdown contains disallowed syntax', () => {
const markdownFilePath = '/path/to/dir/invalid-file-with-markdown-contains-disallowed-syntax.md';
expect(() => {
getDataInMarkdownFile({
ValidationSchema,
markdownFilePath,
});
}).toThrow(
`The markdown of the file "${markdownFilePath}" is invalid ! Is not compliant, it contains a syntax that is not allowed !`
);
});

it('should throw an error if frontmatter data is invalid', () => {
const markdownFilePath = '/path/to/dir/invalid-file-with-validation-schema.md';
expect(() => {
getDataInMarkdownFile({
ValidationSchema: z.object({
title: z.string(),
date: z.coerce.date(),
}),
markdownFilePath,
});
}).toThrow(
`The markdown of the file "/path/to/dir/invalid-file-with-validation-schema.md" is invalid ! Invalid date at "date"`
);
});

it('should throw an error if an error occurs during parsing or validation', () => {
const markdownFilePath = '/path/to/dir/invalid-file-syntax.md';
expect(() => {
getDataInMarkdownFile({
ValidationSchema,
markdownFilePath,
});
}).toThrow(
`The markdown of the file "${markdownFilePath}" is invalid ! Can not read an implicit mapping pair; a colon is missed at line 5, column 14`
);
});
});

describe('validateAuthor', () => {
it('should throw an error if markdown is invalid', () => {
const options = { markdownFilePath: '/path/to/dir/invalid-author.md' };
expect(() => validateAuthor(options)).toThrowError(
'The markdown of the file "/path/to/dir/invalid-author.md" is invalid ! Validation error: Required at "name"'
'The markdown of the file "/path/to/dir/invalid-author.md" is invalid ! Required at "name"'
);
});

Expand All @@ -153,7 +263,7 @@ describe('validatePost', () => {
markdownFilePath: '/path/to/dir/invalid-post.md',
};
expect(() => validatePost(options)).toThrowError(
'The markdown of the file "/path/to/dir/invalid-post.md" is invalid ! Validation error: Required at "title"'
'The markdown of the file "/path/to/dir/invalid-post.md" is invalid ! Required at "title"'
);
});

Expand All @@ -164,7 +274,7 @@ describe('validatePost', () => {
};

expect(() => validatePost(options)).toThrow(
'The markdown of the file "/path/to/dir/invalid-post-keyword-includes-in-categories.md" is invalid ! Validation error: Must not include a category. at "keywords"'
'The markdown of the file "/path/to/dir/invalid-post-keyword-includes-in-categories.md" is invalid ! Must not include a category. at "keywords"'
);
});

Expand All @@ -175,7 +285,7 @@ describe('validatePost', () => {
};

expect(() => validatePost(options)).toThrow(
'The markdown of the file "/path/to/dir/invalid-post-too-many-keywords.md" is invalid ! Validation error: Too many items 😡. at "keywords"'
'The markdown of the file "/path/to/dir/invalid-post-too-many-keywords.md" is invalid ! Too many items 😡. at "keywords"'
);
});

Expand All @@ -186,7 +296,7 @@ describe('validatePost', () => {
};

expect(() => validatePost(options)).toThrow(
'The markdown of the file "/path/to/dir/invalid-post-duplicates-keywords.md" is invalid ! Validation error: No duplicates allowed. at "keywords"'
'The markdown of the file "/path/to/dir/invalid-post-duplicates-keywords.md" is invalid ! No duplicates allowed. at "keywords"'
);
});

Expand All @@ -197,7 +307,7 @@ describe('validatePost', () => {
};

expect(() => validatePost(options)).toThrow(
'The markdown of the file "/path/to/dir/invalid-post-bad-syntax-on-markdown.md" is not compliant, it contains a syntax that is not allowed !'
'The markdown of the file "/path/to/dir/invalid-post-bad-syntax-on-markdown.md" is invalid ! Is not compliant, it contains a syntax that is not allowed !'
);
});

Expand Down

0 comments on commit 6bfaec3

Please sign in to comment.